2 /* C pre-processor functions */
28 /*****************************************************************************/
30 /*****************************************************************************/
34 static int Pass1 (const char* From, char* To);
35 /* Preprocessor pass 1. Remove whitespace and comments. */
39 /*****************************************************************************/
41 /*****************************************************************************/
45 /* Set when the pp calls expr() recursively */
46 unsigned char Preprocessing = 0;
48 /* Management data for #if */
50 static int i_ifdef = -1;
51 static char s_ifdef[N_IFDEF];
53 /* Buffer for macro expansion */
54 static char mlinebuf [LINESIZE];
55 static char* mline = mlinebuf;
58 /* Flag: Expand macros in this line */
59 static int ExpandMacros = 1;
63 /*****************************************************************************/
65 /*****************************************************************************/
69 static int keepch (char c)
70 /* Put character c into translation buffer. */
77 static void keepstr (const char* S)
78 /* Put string str into translation buffer. */
87 static void Comment (void)
88 /* Remove a C comment from line. */
90 /* Remember the current line number, so we can output better error
91 * messages if the comment is not terminated in the current file.
93 unsigned StartingLine = GetCurrentLine();
95 /* Skip the start of comment chars */
99 /* Skip the comment */
100 while (CurC != '*' || NextC != '/') {
102 if (NextLine () == 0) {
103 PPError ("End-of-file reached in comment starting at line %u",
108 if (CurC == '/' && NextC == '*') {
109 PPWarning ("`/*' found inside a comment");
115 /* Skip the end of comment chars */
122 static void SkipBlank (void)
123 /* Skip blanks and tabs in the input stream. */
125 while (IsBlank (CurC)) {
132 static char* CopyQuotedString (char* Target)
133 /* Copy a single or double quoted string from the input to Target. Return the
134 * new target pointer. Target will not be terminated after the copy.
137 /* Remember the quote character, copy it to the target buffer and skip it */
142 /* Copy the characters inside the string */
143 while (CurC != '\0' && CurC != Quote) {
144 /* Keep an escaped char */
149 /* Copy the character */
154 /* If we had a terminating quote, copy it */
160 /* Return the new target pointer */
166 /*****************************************************************************/
168 /*****************************************************************************/
172 static int MacName (char* Ident)
173 /* Get macro symbol name. If error, print message and clear line. */
175 if (IsSym (Ident) == 0) {
176 PPError ("Identifier expected");
186 static void ExpandMacroArgs (Macro* M)
187 /* Preprocessor pass 2. Perform macro substitution. */
190 const char* Replacement;
193 /* Save the current line pointer and setup the new ones */
195 InitLine (M->Replacement);
197 /* Copy the macro replacement checking for parameters to replace */
198 while (CurC != '\0') {
199 /* If the next token is an identifier, check for a macro arg */
200 if (IsIdent (CurC)) {
202 Replacement = FindMacroArg (M, Ident);
204 /* Macro arg, keep the replacement */
205 keepstr (Replacement);
207 /* No macro argument, keep the original identifier */
210 } else if (CurC == '#' && IsIdent (NextC)) {
213 Replacement = FindMacroArg (M, Ident);
216 keepstr (Replacement);
222 } else if (IsQuote (CurC)) {
223 mptr = CopyQuotedString (mptr);
230 /* Reset the line pointer */
236 static int MacroCall (Macro* M)
237 /* Process a function like macro */
239 unsigned ArgCount; /* Macro argument count */
240 unsigned ParCount; /* Number of open parenthesis */
241 char Buf[LINESIZE]; /* Argument buffer */
242 const char* ArgStart;
245 /* Expect an argument list */
248 PPError ("Illegal macro call");
252 /* Eat the left paren */
255 /* Read the actual macro arguments and store pointers to these arguments
256 * into the array of actual arguments in the macro definition.
264 /* Nested parenthesis */
268 } else if (IsQuote (CurC)) {
269 B = CopyQuotedString (B);
270 } else if (CurC == ',' || CurC == ')') {
272 /* End of actual argument */
274 while (IsBlank(*ArgStart)) {
277 if (ArgCount < M->ArgCount) {
278 M->ActualArgs[ArgCount++] = ArgStart;
279 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
280 /* Be sure not to count the single empty argument for a
281 * macro that does not have arguments.
286 /* Check for end of macro param list */
292 /* Start the next param */
296 /* Comma or right paren inside nested parenthesis */
303 } else if (IsBlank (CurC)) {
304 /* Squeeze runs of blanks */
307 } else if (CurC == '\0') {
308 /* End of line inside macro argument list - read next line */
309 if (NextLine () == 0) {
313 /* Just copy the character */
319 /* Compare formal argument count with actual */
320 if (M->ArgCount != ArgCount) {
321 PPError ("Macro argument count mismatch");
322 /* Be sure to make enough empty arguments available */
323 while (ArgCount < M->ArgCount) {
324 M->ActualArgs [ArgCount++] = "";
328 /* Preprocess the line, replacing macro parameters */
337 static void ExpandMacro (Macro* M)
340 /* Check if this is a function like macro */
341 if (M->ArgCount >= 0) {
342 /* Function like macro */
343 if (MacroCall (M) == 0) {
347 /* Just copy the replacement text */
348 keepstr (M->Replacement);
354 static void addmac (void)
355 /* Add a macro to the macro table. */
363 /* Read the macro name */
365 if (!MacName (Ident)) {
369 /* Get an existing macro definition with this name */
370 Existing = FindMacro (Ident);
372 /* Create a new macro definition */
373 M = NewMacro (Ident);
375 /* Check if this is a function like macro */
378 /* Skip the left paren */
381 /* Set the marker that this is a function like macro */
384 /* Read the formal parameter list */
389 if (MacName (Ident) == 0) {
392 AddMacroArg (M, Ident);
399 /* Check for a right paren and eat it if we find one */
401 PPError ("`)' expected");
408 /* Insert the macro into the macro table and allocate the ActualArgs array */
411 /* Remove whitespace and comments from the line, store the preprocessed
419 /* Create a copy of the replacement */
420 M->Replacement = xstrdup (Buf);
422 /* If we have an existing macro, check if the redefinition is identical.
423 * Print a diagnostic if not.
426 if (MacroCmp (M, Existing) != 0) {
427 PPError ("Macro redefinition is not identical");
434 /*****************************************************************************/
436 /*****************************************************************************/
440 static int Pass1 (const char* From, char* To)
441 /* Preprocessor pass 1. Remove whitespace and comments. */
447 /* Initialize reading from "From" */
453 /* Loop removing ws and comments */
455 while (CurC != '\0') {
456 if (IsBlank (CurC)) {
459 } else if (IsIdent (CurC)) {
461 if (Preprocessing && strcmp(Ident, "defined") == 0) {
462 /* Handle the "defined" operator */
470 if (!IsIdent (CurC)) {
471 PPError ("Identifier expected");
475 *mptr++ = IsMacro (Ident)? '1' : '0';
479 PPError ("`)' expected");
486 if (MaybeMacro (Ident[0])) {
491 } else if (IsQuote (CurC)) {
492 mptr = CopyQuotedString (mptr);
493 } else if (CurC == '/' && NextC == '*') {
496 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
498 /* Beware: Because line continuation chars are handled when reading
499 * lines, we may only skip til the end of the source line, which
500 * may not be the same as the end of the input line. The end of the
501 * source line is denoted by a lf (\n) character.
505 } while (CurC != '\n' && CurC != '\0');
520 static int Pass2 (const char* From, char* To)
521 /* Preprocessor pass 2. Perform macro substitution. */
527 /* Initialize reading from "From" */
533 /* Loop substituting macros */
535 while (CurC != '\0') {
536 /* If we have an identifier, check if it's a macro */
537 if (IsIdent (CurC)) {
539 M = FindMacro (Ident);
546 } else if (IsQuote (CurC)) {
547 mptr = CopyQuotedString (mptr);
558 static void xlateline (void)
559 /* Translate one line. */
564 Done = Pass1 (line, mline);
565 if (ExpandMacros == 0) {
567 ExpandMacros = 1; /* Reset to default */
571 /* Swap mline and line */
577 Done = Pass2 (line, mline);
581 /* Reinitialize line parsing */
587 static void doundef (void)
588 /* Process #undef directive */
593 if (MacName (Ident)) {
594 UndefineMacro (Ident);
600 static int setmflag (int skip, int flag, int cond)
601 /* setmflag( skip, flag, cond ) */
604 s_ifdef[++i_ifdef] = 3;
607 s_ifdef[++i_ifdef] = 6;
608 return (flag ^ cond);
614 static int doiff (int skip)
615 /* Process #if directive */
620 /* We're about to abuse the compiler expression parser to evaluate the
621 * #if expression. Save the current tokens to come back here later.
626 /* Remove the #if from the line and add two semicolons as sentinels */
629 while (CurC != '\0') {
637 /* Start over parsing from line */
640 /* Switch into special preprocessing mode */
643 /* Expand macros in this line */
646 /* Prime the token pump (remove old tokens from the stream) */
650 /* Call the expression parser */
653 /* End preprocessing mode */
656 /* Reset the old tokens */
660 /* Set the #if condition according to the expression result */
661 return (setmflag (skip, 1, lval.e_const != 0));
666 static int doifdef (int skip, int flag)
667 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
672 if (MacName (Ident) == 0) {
675 return setmflag (skip, flag, IsMacro(Ident));
681 static void doinclude (void)
682 /* Open an include file. */
691 /* Get the next char and check for a valid file name terminator. Setup
692 * the include directory spec (SYS/USR) by looking at the terminator.
707 PPError ("`\"' or `<' expected");
712 /* Copy the filename into mline. Since mline has the same size as the
713 * input line, we don't need to check for an overflow here.
716 while (CurC != '\0' && CurC != RTerm) {
722 /* Check if we got a terminator */
724 /* No terminator found */
725 PPError ("Missing terminator or file name too long");
729 /* Open the include file */
730 OpenIncludeFile (mline, DirSpec);
733 /* Clear the remaining line so the next input will come from the new
741 static void doerror (void)
746 PPError ("Invalid #error directive");
748 PPError ("#error: %s", lptr);
751 /* clear rest of line */
757 /* C preprocessor. */
759 /* stuff used to bum the keyword dispatching stuff */
775 static const struct tok_elt pre_toks[] = {
776 { "define", PP_DEFINE },
778 { "endif", PP_ENDIF },
779 { "error", PP_ERROR },
781 { "ifdef", PP_IFDEF },
782 { "ifndef", PP_IFNDEF },
783 { "include", PP_INCLUDE },
785 { "pragma", PP_PRAGMA },
786 { "undef", PP_UNDEF },
792 static int searchtok (const char *sym, const struct tok_elt *toks)
793 /* Search a token in a table */
795 while (toks->toknam && strcmp (toks->toknam, sym))
797 return (toks->toknbr);
802 void Preprocess (void)
803 /* Preprocess a line */
808 /* Skip white space at the beginning of the line */
811 /* Check for stuff to skip */
813 while (CurC == '\0' || CurC == '#' || Skip) {
815 /* Check for preprocessor lines lines */
820 /* Ignore the empty preprocessor directive */
823 if (!IsSym (Directive)) {
824 PPError ("Preprocessor directive expected");
827 switch (searchtok (Directive, pre_toks)) {
836 if (s_ifdef[i_ifdef] & 2) {
837 if (s_ifdef[i_ifdef] & 4) {
840 s_ifdef[i_ifdef] ^= 2;
842 PPError ("Unexpected `#else'");
848 Skip = s_ifdef[i_ifdef--] & 1;
850 PPError ("Unexpected `#endif'");
865 Skip = doifdef (Skip, 1);
869 Skip = doifdef (Skip, 0);
879 /* Not allowed in strict ANSI mode */
881 PPError ("Preprocessor directive expected");
888 /* Don't expand macros in this line */
890 /* #pragma is handled on the scanner level */
902 PPError ("Preprocessor directive expected");
908 if (NextLine () == 0) {
910 PPError ("`#endif' expected");
920 printf ("line: %s\n", line);