2 /* C pre-processor functions */
10 #include "../common/xmalloc.h"
26 /*****************************************************************************/
28 /*****************************************************************************/
32 static int Pass1 (const char* From, char* To);
33 /* Preprocessor pass 1. Remove whitespace and comments. */
37 /*****************************************************************************/
39 /*****************************************************************************/
43 /* Set when the pp calls expr() recursively */
44 unsigned char Preprocessing = 0;
46 /* Management data for #if */
48 static int i_ifdef = -1;
49 static char s_ifdef[N_IFDEF];
51 /* Buffer for macro expansion */
52 static char mlinebuf [LINESIZE];
53 static char* mline = mlinebuf;
56 /* Flag: Expand macros in this line */
57 static int ExpandMacros = 1;
61 /*****************************************************************************/
63 /*****************************************************************************/
67 static int keepch (char c)
68 /* Put character c into translation buffer. */
75 static void keepstr (const char* S)
76 /* Put string str into translation buffer. */
85 static void Comment (void)
86 /* Remove a C comment from line. */
88 /* Remember the current line number, so we can output better error
89 * messages if the comment is not terminated in the current file.
91 unsigned StartingLine = GetCurrentLine();
93 /* Skip the start of comment chars */
97 /* Skip the comment */
98 while (CurC != '*' || NextC != '/') {
100 if (NextLine () == 0) {
101 PPError (ERR_EOF_IN_COMMENT, StartingLine);
105 if (CurC == '/' && NextC == '*') {
106 PPWarning (WARN_NESTED_COMMENT);
112 /* Skip the end of comment chars */
119 static void SkipBlank (void)
120 /* Skip blanks and tabs in the input stream. */
122 while (IsBlank (CurC)) {
129 static char* CopyQuotedString (char* Target)
130 /* Copy a single or double quoted string from the input to Target. Return the
131 * new target pointer. Target will not be terminated after the copy.
134 /* Remember the quote character, copy it to the target buffer and skip it */
139 /* Copy the characters inside the string */
140 while (CurC != '\0' && CurC != Quote) {
141 /* Keep an escaped char */
146 /* Copy the character */
151 /* If we had a terminating quote, copy it */
157 /* Return the new target pointer */
163 /*****************************************************************************/
165 /*****************************************************************************/
169 static int MacName (char* Ident)
170 /* Get macro symbol name. If error, print message and clear line. */
172 if (IsSym (Ident) == 0) {
173 PPError (ERR_IDENT_EXPECTED);
183 static void ExpandMacroArgs (Macro* M)
184 /* Preprocessor pass 2. Perform macro substitution. */
187 const char* Replacement;
190 /* Save the current line pointer and setup the new ones */
192 InitLine (M->Replacement);
194 /* Copy the macro replacement checking for parameters to replace */
195 while (CurC != '\0') {
196 /* If the next token is an identifier, check for a macro arg */
197 if (IsIdent (CurC)) {
199 Replacement = FindMacroArg (M, Ident);
201 /* Macro arg, keep the replacement */
202 keepstr (Replacement);
204 /* No macro argument, keep the original identifier */
207 } else if (CurC == '#' && IsIdent (NextC)) {
210 Replacement = FindMacroArg (M, Ident);
213 keepstr (Replacement);
219 } else if (IsQuoteChar (CurC)) {
220 mptr = CopyQuotedString (mptr);
227 /* Reset the line pointer */
233 static int MacroCall (Macro* M)
234 /* Process a function like macro */
236 unsigned ArgCount; /* Macro argument count */
237 unsigned ParCount; /* Number of open parenthesis */
238 char Buf[LINESIZE]; /* Argument buffer */
239 const char* ArgStart;
242 /* Expect an argument list */
245 PPError (ERR_ILLEGAL_MACRO_CALL);
249 /* Eat the left paren */
252 /* Read the actual macro arguments and store pointers to these arguments
253 * into the array of actual arguments in the macro definition.
261 /* Nested parenthesis */
265 } else if (IsQuoteChar (CurC)) {
266 B = CopyQuotedString (B);
267 } else if (CurC == ',' || CurC == ')') {
269 /* End of actual argument */
271 while (IsBlank(*ArgStart)) {
274 if (ArgCount < M->ArgCount) {
275 M->ActualArgs[ArgCount++] = ArgStart;
276 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
277 /* Be sure not to count the single empty argument for a
278 * macro that does not have arguments.
283 /* Check for end of macro param list */
289 /* Start the next param */
293 /* Comma or right paren inside nested parenthesis */
300 } else if (IsBlank (CurC)) {
301 /* Squeeze runs of blanks */
304 } else if (CurC == '\0') {
305 /* End of line inside macro argument list - read next line */
306 if (NextLine () == 0) {
310 /* Just copy the character */
316 /* Compare formal argument count with actual */
317 if (M->ArgCount != ArgCount) {
318 PPError (ERR_MACRO_ARGCOUNT);
319 /* Be sure to make enough empty arguments available */
320 while (ArgCount < M->ArgCount) {
321 M->ActualArgs [ArgCount++] = "";
325 /* Preprocess the line, replacing macro parameters */
334 static void ExpandMacro (Macro* M)
337 /* Check if this is a function like macro */
338 if (M->ArgCount >= 0) {
339 /* Function like macro */
340 if (MacroCall (M) == 0) {
344 /* Just copy the replacement text */
345 keepstr (M->Replacement);
351 static void addmac (void)
352 /* Add a macro to the macro table. */
359 /* Read the macro name */
361 if (!MacName (Ident)) {
365 /* Create a new macro definition */
366 M = NewMacro (Ident);
368 /* Check if this is a function like macro */
371 /* Skip the left paren */
374 /* Set the marker that this is a function like macro */
377 /* Read the formal parameter list */
382 if (MacName (Ident) == 0) {
385 AddMacroArg (M, Ident);
392 /* Check for a right paren and eat it if we find one */
394 PPError (ERR_RPAREN_EXPECTED);
401 /* Insert the macro into the macro table and allocate the ActualArgs array */
404 /* Remove whitespace and comments from the line, store the preprocessed
412 /* Create a copy of the replacement */
413 M->Replacement = xstrdup (Buf);
418 /*****************************************************************************/
420 /*****************************************************************************/
424 static int Pass1 (const char* From, char* To)
425 /* Preprocessor pass 1. Remove whitespace and comments. */
431 /* Initialize reading from "From" */
437 /* Loop removing ws and comments */
439 while (CurC != '\0') {
440 if (IsBlank (CurC)) {
443 } else if (IsIdent (CurC)) {
445 if (Preprocessing && strcmp(Ident, "defined") == 0) {
446 /* Handle the "defined" operator */
454 if (!IsIdent (CurC)) {
455 PPError (ERR_IDENT_EXPECTED);
459 *mptr++ = IsMacro (Ident)? '1' : '0';
463 PPError (ERR_RPAREN_EXPECTED);
470 if (MaybeMacro (Ident[0])) {
475 } else if (IsQuoteChar (CurC)) {
476 mptr = CopyQuotedString (mptr);
477 } else if (CurC == '/' && NextC == '*') {
480 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
482 /* Beware: Because line continuation chars are handled when reading
483 * lines, we may only skip til the end of the source line, which
484 * may not be the same as the end of the input line. The end of the
485 * source line is denoted by a lf (\n) character.
489 } while (CurC != '\n' && CurC != '\0');
504 static int Pass2 (const char* From, char* To)
505 /* Preprocessor pass 2. Perform macro substitution. */
511 /* Initialize reading from "From" */
517 /* Loop substituting macros */
519 while (CurC != '\0') {
520 /* If we have an identifier, check if it's a macro */
521 if (IsIdent (CurC)) {
523 M = FindMacro (Ident);
530 } else if (IsQuoteChar(CurC)) {
531 mptr = CopyQuotedString (mptr);
542 static void xlateline (void)
543 /* Translate one line. */
548 Done = Pass1 (line, mline);
549 if (ExpandMacros == 0) {
551 ExpandMacros = 1; /* Reset to default */
555 /* Swap mline and line */
561 Done = Pass2 (line, mline);
565 /* Reinitialize line parsing */
571 static void doundef (void)
572 /* Process #undef directive */
577 if (MacName (Ident)) {
578 UndefineMacro (Ident);
584 static int setmflag (int skip, int flag, int cond)
585 /* setmflag( skip, flag, cond ) */
588 s_ifdef[++i_ifdef] = 3;
591 s_ifdef[++i_ifdef] = 6;
592 return (flag ^ cond);
598 static int doiff (int skip)
599 /* Process #if directive */
604 /* We're about to abuse the compiler expression parser to evaluate the
605 * #if expression. Save the current tokens to come back here later.
610 /* Remove the #if from the line and add two semicolons as sentinels */
613 while (CurC != '\0') {
621 /* Start over parsing from line */
624 /* Switch into special preprocessing mode */
627 /* Expand macros in this line */
630 /* Prime the token pump (remove old tokens from the stream) */
634 /* Call the expression parser */
637 /* End preprocessing mode */
640 /* Reset the old tokens */
644 /* Set the #if condition according to the expression result */
645 return (setmflag (skip, 1, lval.e_const != 0));
650 static int doifdef (int skip, int flag)
651 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
656 if (MacName (Ident) == 0) {
659 return setmflag (skip, flag, IsMacro(Ident));
665 static void doinclude (void)
666 /* Open an include file. */
675 /* Get the next char and check for a valid file name terminator. Setup
676 * the include directory spec (SYS/USR) by looking at the terminator.
691 PPError (ERR_INCLUDE_LTERM_EXPECTED);
696 /* Copy the filename into mline. Since mline has the same size as the
697 * input line, we don't need to check for an overflow here.
700 while (CurC != '\0' && CurC != RTerm) {
706 /* Check if we got a terminator */
708 /* No terminator found */
709 PPError (ERR_INCLUDE_RTERM_EXPECTED);
713 /* Open the include file */
714 OpenIncludeFile (mline, DirSpec);
717 /* Clear the remaining line so the next input will come from the new
725 static void doerror (void)
730 PPError (ERR_INVALID_USER_ERROR);
732 PPError (ERR_USER_ERROR, lptr);
735 /* clear rest of line */
741 /* C preprocessor. */
743 /* stuff used to bum the keyword dispatching stuff */
759 static const struct tok_elt pre_toks[] = {
760 { "define", PP_DEFINE },
762 { "endif", PP_ENDIF },
763 { "error", PP_ERROR },
765 { "ifdef", PP_IFDEF },
766 { "ifndef", PP_IFNDEF },
767 { "include", PP_INCLUDE },
769 { "pragma", PP_PRAGMA },
770 { "undef", PP_UNDEF },
776 static int searchtok (const char *sym, const struct tok_elt *toks)
777 /* Search a token in a table */
779 while (toks->toknam && strcmp (toks->toknam, sym))
781 return (toks->toknbr);
786 void Preprocess (void)
787 /* Preprocess a line */
792 /* Skip white space at the beginning of the line */
795 /* Check for stuff to skip */
797 while (CurC == '\0' || CurC == '#' || Skip) {
799 /* Check for preprocessor lines lines */
804 /* Ignore the empty preprocessor directive */
807 if (!IsSym (Directive)) {
808 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
811 switch (searchtok (Directive, pre_toks)) {
820 if (s_ifdef[i_ifdef] & 2) {
821 if (s_ifdef[i_ifdef] & 4) {
824 s_ifdef[i_ifdef] ^= 2;
826 PPError (ERR_UNEXPECTED_CPP_ELSE);
832 Skip = s_ifdef[i_ifdef--] & 1;
834 PPError (ERR_UNEXPECTED_CPP_ENDIF);
849 Skip = doifdef (Skip, 1);
853 Skip = doifdef (Skip, 0);
863 /* Not allowed in strict ANSI mode */
865 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
872 /* Don't expand macros in this line */
874 /* #pragma is handled on the scanner level */
886 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
892 if (NextLine () == 0) {
894 PPError (ERR_CPP_ENDIF_EXPECTED);
904 printf ("line: %s\n", line);