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. */
360 /* Read the macro name */
362 if (!MacName (Ident)) {
366 /* Get an existing macro definition with this name */
367 Existing = FindMacro (Ident);
369 /* Create a new macro definition */
370 M = NewMacro (Ident);
372 /* Check if this is a function like macro */
375 /* Skip the left paren */
378 /* Set the marker that this is a function like macro */
381 /* Read the formal parameter list */
386 if (MacName (Ident) == 0) {
389 AddMacroArg (M, Ident);
396 /* Check for a right paren and eat it if we find one */
398 PPError (ERR_RPAREN_EXPECTED);
405 /* Insert the macro into the macro table and allocate the ActualArgs array */
408 /* Remove whitespace and comments from the line, store the preprocessed
416 /* Create a copy of the replacement */
417 M->Replacement = xstrdup (Buf);
419 /* If we have an existing macro, check if the redefinition is identical.
420 * Print a diagnostic if not.
423 if (MacroCmp (M, Existing) != 0) {
424 PPError (ERR_MACRO_REDEF);
431 /*****************************************************************************/
433 /*****************************************************************************/
437 static int Pass1 (const char* From, char* To)
438 /* Preprocessor pass 1. Remove whitespace and comments. */
444 /* Initialize reading from "From" */
450 /* Loop removing ws and comments */
452 while (CurC != '\0') {
453 if (IsBlank (CurC)) {
456 } else if (IsIdent (CurC)) {
458 if (Preprocessing && strcmp(Ident, "defined") == 0) {
459 /* Handle the "defined" operator */
467 if (!IsIdent (CurC)) {
468 PPError (ERR_IDENT_EXPECTED);
472 *mptr++ = IsMacro (Ident)? '1' : '0';
476 PPError (ERR_RPAREN_EXPECTED);
483 if (MaybeMacro (Ident[0])) {
488 } else if (IsQuoteChar (CurC)) {
489 mptr = CopyQuotedString (mptr);
490 } else if (CurC == '/' && NextC == '*') {
493 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
495 /* Beware: Because line continuation chars are handled when reading
496 * lines, we may only skip til the end of the source line, which
497 * may not be the same as the end of the input line. The end of the
498 * source line is denoted by a lf (\n) character.
502 } while (CurC != '\n' && CurC != '\0');
517 static int Pass2 (const char* From, char* To)
518 /* Preprocessor pass 2. Perform macro substitution. */
524 /* Initialize reading from "From" */
530 /* Loop substituting macros */
532 while (CurC != '\0') {
533 /* If we have an identifier, check if it's a macro */
534 if (IsIdent (CurC)) {
536 M = FindMacro (Ident);
543 } else if (IsQuoteChar(CurC)) {
544 mptr = CopyQuotedString (mptr);
555 static void xlateline (void)
556 /* Translate one line. */
561 Done = Pass1 (line, mline);
562 if (ExpandMacros == 0) {
564 ExpandMacros = 1; /* Reset to default */
568 /* Swap mline and line */
574 Done = Pass2 (line, mline);
578 /* Reinitialize line parsing */
584 static void doundef (void)
585 /* Process #undef directive */
590 if (MacName (Ident)) {
591 UndefineMacro (Ident);
597 static int setmflag (int skip, int flag, int cond)
598 /* setmflag( skip, flag, cond ) */
601 s_ifdef[++i_ifdef] = 3;
604 s_ifdef[++i_ifdef] = 6;
605 return (flag ^ cond);
611 static int doiff (int skip)
612 /* Process #if directive */
617 /* We're about to abuse the compiler expression parser to evaluate the
618 * #if expression. Save the current tokens to come back here later.
623 /* Remove the #if from the line and add two semicolons as sentinels */
626 while (CurC != '\0') {
634 /* Start over parsing from line */
637 /* Switch into special preprocessing mode */
640 /* Expand macros in this line */
643 /* Prime the token pump (remove old tokens from the stream) */
647 /* Call the expression parser */
650 /* End preprocessing mode */
653 /* Reset the old tokens */
657 /* Set the #if condition according to the expression result */
658 return (setmflag (skip, 1, lval.e_const != 0));
663 static int doifdef (int skip, int flag)
664 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
669 if (MacName (Ident) == 0) {
672 return setmflag (skip, flag, IsMacro(Ident));
678 static void doinclude (void)
679 /* Open an include file. */
688 /* Get the next char and check for a valid file name terminator. Setup
689 * the include directory spec (SYS/USR) by looking at the terminator.
704 PPError (ERR_INCLUDE_LTERM_EXPECTED);
709 /* Copy the filename into mline. Since mline has the same size as the
710 * input line, we don't need to check for an overflow here.
713 while (CurC != '\0' && CurC != RTerm) {
719 /* Check if we got a terminator */
721 /* No terminator found */
722 PPError (ERR_INCLUDE_RTERM_EXPECTED);
726 /* Open the include file */
727 OpenIncludeFile (mline, DirSpec);
730 /* Clear the remaining line so the next input will come from the new
738 static void doerror (void)
743 PPError (ERR_INVALID_USER_ERROR);
745 PPError (ERR_USER_ERROR, lptr);
748 /* clear rest of line */
754 /* C preprocessor. */
756 /* stuff used to bum the keyword dispatching stuff */
772 static const struct tok_elt pre_toks[] = {
773 { "define", PP_DEFINE },
775 { "endif", PP_ENDIF },
776 { "error", PP_ERROR },
778 { "ifdef", PP_IFDEF },
779 { "ifndef", PP_IFNDEF },
780 { "include", PP_INCLUDE },
782 { "pragma", PP_PRAGMA },
783 { "undef", PP_UNDEF },
789 static int searchtok (const char *sym, const struct tok_elt *toks)
790 /* Search a token in a table */
792 while (toks->toknam && strcmp (toks->toknam, sym))
794 return (toks->toknbr);
799 void Preprocess (void)
800 /* Preprocess a line */
805 /* Skip white space at the beginning of the line */
808 /* Check for stuff to skip */
810 while (CurC == '\0' || CurC == '#' || Skip) {
812 /* Check for preprocessor lines lines */
817 /* Ignore the empty preprocessor directive */
820 if (!IsSym (Directive)) {
821 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
824 switch (searchtok (Directive, pre_toks)) {
833 if (s_ifdef[i_ifdef] & 2) {
834 if (s_ifdef[i_ifdef] & 4) {
837 s_ifdef[i_ifdef] ^= 2;
839 PPError (ERR_UNEXPECTED_CPP_ELSE);
845 Skip = s_ifdef[i_ifdef--] & 1;
847 PPError (ERR_UNEXPECTED_CPP_ENDIF);
862 Skip = doifdef (Skip, 1);
866 Skip = doifdef (Skip, 0);
876 /* Not allowed in strict ANSI mode */
878 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
885 /* Don't expand macros in this line */
887 /* #pragma is handled on the scanner level */
899 PPError (ERR_CPP_DIRECTIVE_EXPECTED);
905 if (NextLine () == 0) {
907 PPError (ERR_CPP_ENDIF_EXPECTED);
917 printf ("line: %s\n", line);