/************************************************************************/ /* */ /* File: pp2.c */ /* */ /* Symbol table and macro functions. */ /* */ /* Written by: */ /* Gary Oliver */ /* 3420 NW Elmwood Dr. */ /* PO Box 826 */ /* Corvallis, Oregon 97339 */ /* (503)758-5549 */ /* Maintained by: */ /* Kirk Bailey */ /* Logical Systems */ /* P.O. Box 1702 */ /* Corvallis, OR 97339 */ /* (503)753-9051 */ /* */ /* This program is hereby placed in the public domain. In */ /* contrast to other claims of "public domain", this means no */ /* copyright is claimed and you may do anything you like with PP, */ /* including selling it! As a gesture of courtesy, please retain */ /* the authorship information in the source code and */ /* documentation. */ /* */ /* Functions contained within this module: */ /* */ /* docall Execute macro call. */ /* _docall Helping function for docall. */ /* dodefine Process #define directive. */ /* doerror Process #error directive. */ /* doundef Process #undef directive. */ /* fbind A a formal paramter instantiation. */ /* flookup Lookup a formal parameter. */ /* esc_str Helper for strize. */ /* getparams Get parameters for defining macro. */ /* hash Hash symbol to a value. */ /* lookup Lookup a symbol in symbol table. */ /* predef Lookup a directive in directive table. */ /* sbind Bind a macro body to a symbol. */ /* strize Handle "#" (stringify) operator. */ /* unfbind Remove binding of formal parameters. */ /* unparam Return to free mem a parameter chain. */ /* unsbind Unbind a symbol from symbol table. */ /* */ /************************************************************************/ #include "pp.h" #include "ppext.h" /************************************************************************/ /* */ /* docall */ /* */ /* Process a macro call. */ /* */ /* The last fetched token was the macro name. The input */ /* parameter is the symtab pointer to the macro prototype. */ /* Now we must fetch the parameters (if any) and bind the */ /* values to local (new) symbols for processing. */ /* */ /************************************************************************/ #ifdef MSC_OPT #pragma optimize("e",off) /* Disable global reg optimizing */ #pragma optimize("g",off) /* Disable global common subs */ #pragma optimize("l",off) /* Disable loop optimizations */ #endif /* MSC_OPT */ char * docall(p,internal,internal_limit) struct symtab *p; char *internal; char *internal_limit; { static char mbomsg[] = "Macro body overflow"; static char toggle_token[] = { (char) TOGGLE_EXPAND,'\0' }; int addit; char *body; char *bodyp; char *cp; int done; register int expand; int flags; struct symtab *formals; register int had_ws; int maclevel; int nl_count; int pcount; struct param *pp; int printed; struct symtab *sy; register int t; char t_token[TOKENSIZE]; /* * Crack the formal parameters for the macro. */ formals = NULL; /* No parameter instantiations yet */ maclevel = 0; if((pp = p->s_params) != NULL) { /* This macro has parameters: see if present on this call */ had_ws = FALSE; expand = TRUE; nl_count = 0; while(((t = gettoken(GT_STR)) == '\n') || istype(t,C_W) || (t == TOGGLE_EXPAND) || (t == END_MACRO)) { if(t == '\n') ++nl_count; else if(t == TOGGLE_EXPAND) expand = !expand; else if(t == END_MACRO) ++maclevel; else had_ws = TRUE; } if(expand && (t == '(')) { /* A left parenthesis -- parameters follow */ printed = done = FALSE; cp = t_token; pcount = 1; /* Special parameter? */ if(pp->p_name[0] == '\0') pp = pp->p_link; /* Skip over it */ while(! done) { addit = TRUE; /* Assume to add to string */ switch(gettoken(GT_STR)) { case '"': if((pp != NULL) && ((pp->p_flags & PF_RQUOTES) != 0)) { /* Remove surrounding quotes */ memmov(Token + 1,Token, (unsigned) strlen(Token)); Token[strlen(Token) - 1] = '\0'; } break; case '\n': if((pp != NULL) && ((pp->p_flags & PF_PNLINES) == 0)) { /* Remove parameter newlines */ Token[0] = ' '; Token[1] = '\0'; } break; case '(': ++pcount; /* Count up parenthesis */ break; case ')': if(--pcount != 0) /* Count down '()' */ break; /* Continue if not level 0 */ pcount = 1; /* Wrap -- make comma fake */ done = TRUE; /* We are finished */ /* Fall through for end of param list */ case ',': if(pcount == 1) { addit = FALSE; /* Do not add */ if(pp == NULL) { if(((! done) || (cp != t_token)) && (! printed)) { warning("Too many arguments for: ", p->s_name); printed = TRUE; } } else { *cp = '\0'; fbind(&formals,pp->p_name, t_token); if((cp == t_token) && (! printed)) { warning("Missing argument for: ", p->s_name); printed = TRUE; } cp = t_token; pp = pp->p_link; if((! done) && (pp == NULL) && (! printed)) { warning("Too many arguments for: ", p->s_name); printed = TRUE; } } } break; case EOF: end_of_file(); /* Unexpected end of file */ default: break; } if(addit) { cp = addstr(cp,&t_token[TOKENSIZE - 1], "Parameter buffer overflow", Token); } } /* * Null bindings for the rest of the parameters, and complain. */ if(pp != NULL) { warning("Too few arguments for: ", p->s_name); for(/* Empty */; pp != NULL; pp = pp->p_link) fbind(&formals,pp->p_name,""); } } else { /* * Although this looked like a macro call, it wasn't really. Push back * the non "(" token, together with a whitespace, if one was present. * Then just output the macro name. */ pbstr(Token); /* Put it back */ while(nl_count-- > 0) /* Dump linefeeds through */ pushback('\n'); while(maclevel-- != 0) pushback(END_MACRO); /* Keep call lvl OK */ if(! expand) pushback(TOGGLE_EXPAND); if(internal == NULL) { puttoken(p->s_name); if(had_ws) puttoken(" "); } else { /* * HACK: Put TOGGLE_TOKENS around the non-macro to "pre-complement" it * for the later expansion toggle flag put on by the calling copy of * "docall". */ internal = addstr(internal,internal_limit, mbomsg,toggle_token); internal = addstr(internal,internal_limit, mbomsg,p->s_name); internal = addstr(internal,internal_limit, mbomsg,toggle_token); if(had_ws) { internal = addstr(internal, internal_limit,mbomsg," "); } } return (internal); } } while(maclevel-- != 0) pushback(END_MACRO); /* Keep call level correct */ /* * Now open the macro body for input and read while processing formal * parameters. */ pushback(END_MACRO); pbcstr(p->s_body); /* Allocate a place for the macro replacement */ if((body = (char *) malloc((unsigned) MACROSIZE)) == NULL) out_of_memory(); flags = (A_astring ? (0) : (GT_STR)); had_ws = FALSE; expand = TRUE; bodyp = body; /* Point at start of body */ /* * The following code does the formal parameter substitution and macro * expansion of formal parameters to ANSI specifications with the ## and * # operators. To understand this code get a copy of the ANSI spec and * expect to spend some time! The code within the while loop is a state * machine with "if" clauses either looping back via a "continue" or * falling through to the next test. The "had_ws" and "expand" flags * are used to remember the fact that whitespace was encountered prior to * the "current" token and the current value of the expand/no-expand * flags prior to the current token (respectively). The expand/no-expand * flag is used to indicate where macro expansion has already been done * (ie. implement ANSI scopes and the fact that formals get expanded * before the rest of the body). */ while((t = gettoken(flags)) != END_MACRO) { if(t == EOF) end_of_file(); /* * Keep track of EXPAND_TOKEN and whitespace occurring before the current * token. */ if(istype(t,C_W)) { had_ws = TRUE; continue; } if(t == TOGGLE_EXPAND) { expand = !expand; continue; } /* * Handle ## operator. */ if(t == '#') { if((t = gettoken(flags)) == '#') { while(istype((t = gettoken(flags)),C_W) || (t == TOGGLE_EXPAND)) { if(t == TOGGLE_EXPAND) /* Skip toggle tokens */ expand = !expand; } if(t == END_MACRO) { pushback(END_MACRO); if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg," "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg, toggle_token); } bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, "##"); continue; } had_ws = FALSE; /* Ignore whitespace */ if((t == LETTER) && ((cp = flookup(formals,Token)) != NULL)) { pushback(END_MACRO); pbcstr(cp); while(istype((t = gettoken(flags)), C_W) || (t == TOGGLE_EXPAND)) { if(t == TOGGLE_EXPAND) /* Skip toggle tokens */ expand = !expand; } if(expand != TRUE) { expand = TRUE; pushback(TOGGLE_EXPAND); } /* Refetch in while loop */ pbstr(Token); while((t = gettoken(flags)) != END_MACRO) { if(t == EOF) end_of_file(); if(istype(t,C_W)) { had_ws = TRUE; continue; } if(t == TOGGLE_EXPAND) { expand = !expand; continue; } if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg," "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg, toggle_token); } bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg,Token); } } else { bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, Token); } continue; } else /* Was # operator, look at in a bit */ { pbstr(Token); strcpy(Token,"#"); t = '#'; } } /* * All other token types need to have the "had_ws" and "expand" flags * flushed prior to being examined. */ if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp,&body[MACROSIZE - 1],mbomsg," "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp,&body[MACROSIZE - 1],mbomsg, toggle_token); } /* * Handle # operator. */ if(t == '#') { while(istype((t = gettoken(flags)),C_W) || (t == TOGGLE_EXPAND)) { if(istype(t,C_W)) had_ws = TRUE; /* Skip whitespace */ else if(t == TOGGLE_EXPAND) /* Skip toggle tokens */ expand = !expand; } if((t == LETTER) && ((cp = flookup(formals,Token)) != NULL)) { had_ws = FALSE; bodyp = strize(bodyp,&body[MACROSIZE - 1], mbomsg,cp); if(! expand) { expand = TRUE; bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, toggle_token); } } else /* Wasn't formal send # through */ { bodyp = addstr(bodyp,&body[MACROSIZE - 1], mbomsg,"#"); if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg," "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, toggle_token); } pbstr(Token); /* Refetch later */ } continue; } /* * Handle formal parameters. Note that we need to look ahead to see if * the next operator is ## in order to know whether to perform macro * expansion on the parameter value or not. */ if((t == LETTER) && ((cp = flookup(formals,Token)) != NULL)) { while(istype((t = gettoken(flags)),C_W) || (t == TOGGLE_EXPAND)) { if(istype(t,C_W)) had_ws = TRUE; /* Skip whitespace */ else if(t == TOGGLE_EXPAND) /* Skip toggle tokens */ expand = !expand; } /* * See in what form we are going to need the parameter. */ if(t == '#') /* See if ## */ { if((t = gettoken(flags)) == '#') { had_ws = FALSE; pushback(END_MACRO); pbcstr(cp); /* * Transfer the formal parameter into the result while pruning trailing * whitespace and TOGGLE_EXPAND's. */ while((t = gettoken(flags)) != END_MACRO) { if(t == EOF) end_of_file(); if(istype(t,C_W)) { had_ws = TRUE; continue; } if(t == TOGGLE_EXPAND) { expand = !expand; continue; } if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg," "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg, toggle_token); } bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg,Token); } pbstr("##"); /* Refetch again */ continue; } else { /* * Was single #, pushback # for later. */ pbstr(Token); strcpy(Token,"#"); } } pbstr(Token); /* Refetch the last non # token */ if(had_ws) { had_ws = FALSE; pushback(' '); } if(! expand) { expand = TRUE; pushback(TOGGLE_EXPAND); } /* * This macro replaces the entire replacement string for the parameter. */ pushback(END_ARG); pbcstr(cp); /* Parameter value */ while((t = gettoken(flags)) != END_ARG) { if(t == EOF) end_of_file(); if(istype(t,C_W)) { had_ws = TRUE; continue; } if(t == TOGGLE_EXPAND) { expand = !expand; continue; } if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, " "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, toggle_token); } /* * Process an individual token in the replacement string for the formal * parameter. */ if((t == LETTER) && ((sy = lookup(Token,NULL)) != NULL) && (! sy->disable)) { *docall(sy,t_token, &t_token[TOKENSIZE - 1]) = '\0'; pushback(END_MACRO); pbcstr(t_token); /* Disable further macro expansion */ bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, toggle_token); while((t = gettoken(flags)) != END_MACRO) { if(t == EOF) end_of_file(); if(istype(t,C_W)) { had_ws = TRUE; continue; } if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg," "); } bodyp = addstr(bodyp, &body[MACROSIZE - 1], mbomsg,Token); } expand = FALSE; } else { bodyp = addstr(bodyp, &body[MACROSIZE - 1],mbomsg, Token); } } } /* * Handle all other tokens. */ else bodyp = addstr(bodyp,&body[MACROSIZE-1],mbomsg,Token); } /* * Flush any remaining whitespace or disable tokens. */ if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp,&body[MACROSIZE - 1],mbomsg," "); } if(! expand) { expand = TRUE; bodyp = addstr(bodyp,&body[MACROSIZE - 1],mbomsg,toggle_token); } *bodyp = '\0'; /* Terminate macro body */ unfbind(formals); /* Return param instantiation mem */ p->disable = TRUE; /* No recursive macros in ANSI */ /* * Now scan the macro body (modified by formal parameter substitution and * formal parameter macro expansion), so that the rest of the macro * expansion occurs. Do this by buffering a line at a time so that * directive re-scanning will work if the user has requested it. Note * that rescanning for macro expansion is not performed on any line * beginning with a '#'. */ pushback(END_MACRO); pbcstr(body); expand = TRUE; bodyp = body; had_ws = FALSE; while((t = gettoken(GT_STR)) != END_MACRO) { if(istype(t,C_W)) { had_ws = TRUE; continue; } if(t == EOF) end_of_file(); if(t == TOGGLE_EXPAND) expand = !expand; else if((t == LETTER) && expand && ((bodyp > body) ? (body[0] != '#') : TRUE) && ((sy = lookup(Token,NULL)) != NULL) && (! sy->disable)) { if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp,&body[MACROSIZE - 1], mbomsg," "); } bodyp = docall(sy,bodyp,&body[MACROSIZE - 1]); } else if(t == '\n') { bodyp = addstr(bodyp,&body[MACROSIZE-1],mbomsg,Token); *bodyp = '\0'; internal = _docall(body,internal,internal_limit); had_ws = FALSE; bodyp = body; } else { if((t == '#') && (bodyp == body)) had_ws = FALSE; if(had_ws) { had_ws = FALSE; bodyp = addstr(bodyp,&body[MACROSIZE - 1], mbomsg," "); } bodyp = addstr(bodyp,&body[MACROSIZE-1],mbomsg,Token); } } /* * Output any trailing stuff. */ *bodyp = '\0'; internal = _docall(body,internal,internal_limit); free(body); /* Return body buffer memory */ p->disable = FALSE; /* Macro symbol scoping update */ return (internal); } #ifdef MSC_OPT #pragma optimize("e",on) /* Enable global reg optimizing */ #pragma optimize("g",on) /* Enable global common subs */ #pragma optimize("l",on) /* Enable loop optimizing */ #endif /* MSC_OPT */ /************************************************************************/ /* */ /* _docall */ /* */ /* Process a macro replaced line to either output it where */ /* appropriate or process any contained directives if desired. */ /* */ /************************************************************************/ char * _docall(line,internal,internal_limit) char *line; char *internal; char *internal_limit; { static char mbomsg[] = "Macro body overflow"; register struct ppdir *d; register int had_ws; register int t; pushback(END_MACRO); pbcstr(line); Lastnl = TRUE; while((t = gettoken(GT_STR)) != END_MACRO) { if(Lastnl && (t == DIRECTIVE_CHAR)) /* Process directive */ { had_ws = FALSE; while(((t = gettoken(GT_STR)) != END_MACRO) && (t != EOF) && istype(t,C_W)) { had_ws = TRUE; } if((t == LETTER) && ((d = predef(Token,pptab)) != NULL) && ((d->pp_func == dopragma) || A_rescan)) { if(d->pp_ifif || (Ifstate == IFTRUE)) (void) (*(d->pp_func)) (d->pp_arg); Lastnl = TRUE; } else { if(internal == NULL) puttoken("#"); else { internal = addstr(internal, internal_limit,mbomsg,"#"); } pbstr(Token); if(had_ws) pushback(' '); Lastnl = FALSE; } } else if(Ifstate == IFTRUE) /* Normal token, just output */ { if(internal == NULL) puttoken(Token); else { internal = addstr(internal,internal_limit, mbomsg,Token); } if(! istype(t,C_W)) Lastnl = FALSE; } else /* Toss up to EOL */ { while((t != '\n') && (t != EOF) && (t != END_MACRO)) t = gettoken(GT_STR); } if(t == EOF) end_of_file(); else if(t == '\n') Lastnl = TRUE; else if(t == END_MACRO) break; } return (internal); } /************************************************************************/ /* */ /* dodefine */ /* */ /* Define a macro. */ /* */ /* There are several types of macro definitions: */ /* */ /* #define macro string \* No check for parameters *\ */ /* #define macro() string \* check for parameters *\ */ /* #define macro(x) (x) \* Process parameters x *\ */ /* #pragma macro name(-one of above three-) */ /* */ /* ... */ /* #pragma endm */ /* */ /* 1) get name token. */ /* */ /* 2) if next token is a '(' then build a list of strings */ /* delimited by zero-level commas, until a zero-level */ /* ')' is found. */ /* */ /* 3) get EOL (or EOF) terminated string for body. */ /* */ /* sbind(name, body, list_of_parameters (or NO_PARAMS)); */ /* */ /************************************************************************/ void dodefine(mactype) int mactype; /* True if #pragma */ { static char mtlmsg[] = "Macro too long"; char body[MACROSIZE]; /* Body of def */ register char *bodyp; /* Pointer into body */ register int lasttok;/* Last token fetched in loop */ register int macctr; /* Level ctr for #macro/#endm */ char name[TOKENSIZE]; /* Name buffer */ register struct param *pp; /* Param proto pointer */ register struct symtab *sy; /* Symbol table ptr */ register int t; /* Token type */ if(getnstoken(GT_STR) == LETTER) { strcpy(name,Token); /* Move name token to save buffer */ #if DEBUG if(Debug) printf("dodefine: <%s>\n",name); #endif /* DEBUG */ if(gettoken(GT_STR) == '(') pp = getparams(); /* Get list of param protos */ else { pp = NULL; /* No parameters to check for */ pbstr(Token); /* Will see it again */ } while(istype(t = gettoken(GT_STR),C_W)) { if(t == EOF) end_of_file(); /* Unexpected EOF */ } pbstr(Token); /* Push it back */ if(mactype) scaneol(); /* Scan to eol but leave eol */ for(macctr = 0, bodyp = body, lasttok = '\n'; ((t = gettoken(GT_STR)) != 0); ) { if(mactype) { if((lasttok == '\n') && (t == '#')) lasttok = '#'; else if((lasttok == '#') && (t == LETTER)) { if(strcmp(Token,"pragma") == EQUAL) { /************************************************************************/ /* * Possible nesting with "#pragma macro" or ending with * "#pragma endmacro". */ if(getnstoken(GT_STR) == LETTER) { if(strcmp(Token,"endmacro") == EQUAL) { if(macctr-- == 0) { scaneol(); /* Remove EOL */ (void) gettoken(GT_STR); break; } } /* Nesting? */ else if(strcmp(Token,"macro") == EQUAL) macctr++; } pbstr(Token); pushback(' '); strcpy(Token,"pragma"); /* Refetch it all */ /************************************************************************/ } } else if(! istype(t,C_W)) /* Don't count whitespace */ lasttok = t; } else if(t == '\n') break; /* Give up on eol if #define */ if(t == EOF) end_of_file(); /* Unexpected eof */ else { bodyp = addstr(bodyp,&body[MACROSIZE-1],mtlmsg, Token); } } pushback('\n'); if(mactype) { while(istype(*--bodyp & 0xFF,C_W)) ; /* Nuke trailing whitespace */ } else { while((bodyp > body) && istype(*(bodyp-1) & 0xFF,C_W)) bodyp--; /* Nuke trailing whitespace */ } /* * Because of how the preceeding searches are done the following string * termination also removes the orphan "#" for the case where the last * line was a "#pragma endmacro" termination ("mactype" being TRUE). */ *bodyp = '\0'; if((sy = lookup(name,NULL)) != NULL) { if(! A_stack) { if(strcmp(sy->s_body,body) != EQUAL) { non_fatal("Illegal redefinition of symbol: ",name); } unparam(pp); } else { if(strcmp(sy->s_body,body) != EQUAL) warning("Redefining symbol: ",name); sbind(name,body,pp); } } else sbind(name,body,pp); /* Bind new symbol */ } else illegal_symbol(); } /************************************************************************/ /* */ /* doerror */ /* */ /* Process #error statement. */ /* */ /************************************************************************/ void doerror() { char *cptr1; char *cptr2; char msgbuf[MESSAGEBUFSIZE]; cptr1 = readline(msgbuf,MESSAGEBUFSIZE,GT_STR); if((*cptr1 == '"') && ((cptr2 = strrchr(++cptr1,'"')) != NULL)) { *cptr2 = '\0'; /* Terminate for display */ non_fatal(cptr1,""); } else non_fatal("Bad parameter to \"#error\"",""); } /************************************************************************/ /* */ /* doundef */ /* */ /* Process #undef statement. */ /* */ /************************************************************************/ void doundef() { if(getnstoken(GT_STR) == LETTER) { if(lookup(Token,NULL) != NULL) /* OK if symbol not defined */ unsbind(Token); /* Remove symbol from table */ } else illegal_symbol(); } /************************************************************************/ /* */ /* esc_str */ /* */ /* Helper function for strize to escape special string characters. */ /* */ /************************************************************************/ char * esc_str(old,c,limit) char *old; int c; char *limit; { if(((c == '"') || (c == '\\')) && (old < limit)) *old++ = '\\'; if(old < limit) *old++ = c; return (old); } /************************************************************************/ /* */ /* fbind */ /* */ /* Add a formal parameter to the list. */ /* */ /************************************************************************/ void fbind(formals,name,value) struct symtab **formals; char *name; char *value; { struct symtab *temp; if((temp = (struct symtab *) malloc(sizeof(struct symtab) + (unsigned) strlen(name))) == NULL) { out_of_memory(); } temp->s_link = *formals; *formals = temp; strcpy(temp->s_name,name); if((temp->s_body = (char *) malloc(strlen(value) + 1)) == NULL) out_of_memory(); strcpy(temp->s_body,value); } /************************************************************************/ /* */ /* flookup */ /* */ /* Find body associated with a formal parameter. */ /* */ /************************************************************************/ char * flookup(formals,name) struct symtab *formals; char *name; { while(formals != NULL) { if(strcmp(formals->s_name,name) == EQUAL) return (formals->s_body); formals = formals->s_link; } return (NULL); } /************************************************************************/ /* */ /* getparams */ /* */ /* Get list of parameters, terminated by zero-level ')' */ /* and return list as value of function. If no parameters are */ /* found, return a null parameter (NOT a null pointer.) */ /* */ /* Parameters within [] are processed in an extended form: */ /* The first LETTER token within the [] is the parameter name. */ /* Subsequent LETTER tokens are taken as flag symbols that */ /* define flag bits for the parameter. Currently these are: */ /* */ /* PN - Preserve newline chars when substituted. */ /* RQ - Remove quote chars when substituted. */ /* */ /************************************************************************/ struct param * getparams() { static char *iffmsg = "Invalid formal parameter flag: "; register int flags; register struct param *lh; register struct param *lt; register struct param *p; char pname[TOKENSIZE]; register int t; lh = NULL; lt = NULL; /* For lint */ for(;;) { if((t = getnstoken(GT_STR)) == '[') { if(getnstoken(GT_STR) != LETTER) { p = NULL; /* For lint */ non_fatal("Invalid formal parameter",""); } else { strcpy(pname,Token); flags = 0; for(t = getnstoken(GT_STR); t != ']'; t = getnstoken(GT_STR)) { if(t == EOF) end_of_file(); /* * Ignore commas and whitespace. */ if((t == ',') || istype(t,C_W)) continue; if(t == LETTER) { if(strcmp(Token,"RQ") == EQUAL) flags |= PF_RQUOTES; else if(strcmp(Token,"PN") == EQUAL) { flags |= PF_PNLINES; } else { non_fatal(iffmsg, Token); } } else non_fatal(iffmsg,Token); } p = makeparam(pname,flags); } } else if(t == LETTER) p = makeparam(Token,0); else { p = NULL; /* For lint */ break; } if(lh == NULL) lh = lt = p; /* Head and tail of list */ else { lt->p_link = p; /* New tail of list */ lt = p; /* Mark it as end */ } if((t = getnstoken(GT_STR)) != ',' ) break; } if(t != ')') non_fatal("Illegal or out of place token: ",Token); if(lh == NULL) lh = makeparam("",0); /* Make a null parameter */ return (lh); /* Return the list */ } /************************************************************************/ /* */ /* hash */ /* */ /* Calculate a hash function for the character string. */ /* */ /* There is probably a better function to do this, but I am */ /* simply too lazy to search for one now. This one sums */ /* the character values together. The caller is responsible */ /* for taking the sum modulo the value range desired. */ /* */ /************************************************************************/ unsigned int hash(sym) char *sym; { register unsigned int s; register char *str; for(str = sym, s = 0; *str != '\0'; s += *str++) ; /* Compute sum of chars */ return (s); } /************************************************************************/ /* */ /* lookup */ /* */ /* Locate name and return pointer to symtab entry. */ /* */ /************************************************************************/ struct symtab * lookup(name,pe) char *name; /* Name for search */ struct symtab **pe; /* Previous entry to allow removal */ /* If NULL, do not return pointer */ { register struct symtab *c; register struct symtab *p; #if DEBUG if(Debug) printf("lookup: %s - ",name); #endif /* DEBUG */ p = (struct symtab *) &Macros[hash(name) & (NUMBUCKETS - 1)]; c = p->s_link; while(c != NULL) { if(strcmp(c->s_name,name) == EQUAL) { #if DEBUG if(Debug) printf("found!\n"); #endif /* DEBUG */ if(pe != NULL) *pe = p; return (c); } p = c; c = c->s_link; } #if DEBUG if(Debug) printf("NOT found.\n"); #endif /* DEBUG */ return (NULL); /* Didn't find it */ } /************************************************************************/ /* */ /* makeparam */ /* */ /* Generate one parameter cell and return its pointer. */ /* */ /************************************************************************/ struct param * makeparam(s,f) char *s; int f; { struct param *p; p = (struct param *) malloc(sizeof(struct param) + strlen(s)); if(p == NULL) out_of_memory(); strcpy(p->p_name,s); /* Copy name to parameter cell */ p->p_link = NULL; /* End of list so far */ p->p_flags = f; /* Set flags */ return (p); } /************************************************************************/ /* */ /* predef */ /* */ /* Search through predefined table. Return pointer to entry */ /* if found, else return NULL. */ /* */ /************************************************************************/ struct ppdir * predef(n,table) char *n; struct ppdir *table; { register char *name; register struct ppdir *p; for(name = n, p = table; p->pp_name != NULL; p++) { #if DEBUG if(Debug) printf("predef: %s : %s\n",name,p->pp_name); #endif /* DEBUG */ if(strcmp(name,p->pp_name) == EQUAL) { #if DEBUG if(Debug) printf("predef: found! %p\n",p->pp_func); #endif /* DEBUG */ return (p); /* Return table address */ } } #if DEBUG if(Debug) printf("predef: not found.\n"); #endif /* DEBUG */ return (NULL); } /************************************************************************/ /* */ /* sbind */ /* */ /* Add symbol and definitions to symbol table. */ /* */ /************************************************************************/ void sbind(sym,defn,params) char *sym; char *defn; struct param *params; { register int i; register struct symtab *p; #if DEBUG if(Debug) printf("sbind: name <%s> with <%s>\n",sym,defn); #endif /* DEBUG */ p = (struct symtab *) malloc(sizeof(struct symtab) + (unsigned) strlen(sym)); if((p == NULL) || ((p->s_body = malloc((unsigned) strlen(defn) + 1)) == NULL)) { out_of_memory(); } strcpy(p->s_name,sym); strcpy(p->s_body,defn); p->s_params = params; p->disable = FALSE; /* Initially legal to expand */ /* Add to symbol table */ i = hash(sym) & (NUMBUCKETS - 1); /* Hash value for symbol */ p->s_link = Macros[i]; Macros[i] = p; /* Link in to list */ if(++Nsyms > Maxsyms) Maxsyms = Nsyms; /* One more symbol counted */ } /************************************************************************/ /* */ /* strize */ /* */ /* Handle '#' (stringify) operator. */ /* */ /************************************************************************/ char * strize(result,limit,msg,new) char *result; char *limit; char *msg; char *new; { int c; int had_ws; while(TRUE) /* Skip whitespace */ { if((c = *new) == EOF) end_of_file(); c &= 0xFF; if(! (istype(c,C_W) || (c == '\n'))) break; new++; } *result++ = '"'; /* Leading " for string */ had_ws = FALSE; /* TRUE if accumulating whitespace */ while((c = *new++) != '\0') { if(c == EOF) end_of_file(); c &= 0xFF; if(c == TOGGLE_EXPAND) /* Ignore scoping flags */ continue; if(istype(c,C_W) || (c == '\n')) /* Skip inner ws */ had_ws = TRUE; else if((c == '"') || (c == '\'')) /* Str/char const? */ { if(had_ws) { had_ws = FALSE; result = esc_str(result,' ',limit); } result = esc_str(result,c,limit); while((*new != '\0') && (*new != c)) { if((*new & 0xFF) == TOGGLE_EXPAND) continue; if(*new == EOF) end_of_file(); if(*new == '\\') result = esc_str(result,*new++,limit); if(*new != '\0') result = esc_str(result,*new++,limit); } if(*new != '\0') new++; result = esc_str(result,c,limit); } else /* Everything else ... */ { if(had_ws) { had_ws = FALSE; result = esc_str(result,' ',limit); } if(result < limit) *result++ = c; } } if(result < limit) *result++ = '"'; else non_fatal(msg,""); return (result); } /************************************************************************/ /* */ /* unfbind */ /* */ /* Return the formal parameters to a macro to free storage. */ /* */ /************************************************************************/ void unfbind(formals) struct symtab *formals; { register struct symtab *temp; while(formals != NULL) { free(formals->s_body); /* Free up parameter instantiation */ temp = formals->s_link; /* Point at next paramter */ free((char *) formals); /* Free up symbol block & name */ formals = temp; } } /************************************************************************/ /* */ /* unparam */ /* */ /* Return to free storage a macro parameter prototype chain. */ /* */ /************************************************************************/ void unparam(pp) struct param *pp; { struct param *npp; for(; pp != NULL; pp = npp) { npp = pp->p_link; /* Save next address */ free((char *)pp); /* Free this memory */ } } /************************************************************************/ /* */ /* unsbind */ /* */ /* Look up the symbol and remove its entry from the symbol table. */ /* */ /************************************************************************/ void unsbind(sym) char *sym; { struct symtab *p; register struct symtab *s; #if DEBUG if(Debug) printf("unsbind: %s\n",sym); #endif /* DEBUG */ if((s = lookup(sym,&p)) == NULL) non_fatal("Symbol not defined: ",sym); else { if(s->s_body != NULL) free(s->s_body); /* Free macro body */ unparam(s->s_params); /* Free parameter cells */ p->s_link = s->s_link; /* Remove item from list */ free((char *)s); /* Free the symbol table entry */ Nsyms--; /* One less symbol */ } }