2 /* C pre-processor functions */
29 /*****************************************************************************/
31 /*****************************************************************************/
35 static int Pass1 (const char* From, char* To);
36 /* Preprocessor pass 1. Remove whitespace and comments. */
40 /*****************************************************************************/
42 /*****************************************************************************/
46 /* Set when the pp calls expr() recursively */
47 unsigned char Preprocessing = 0;
49 /* Management data for #if */
51 static int i_ifdef = -1;
52 static char s_ifdef[N_IFDEF];
54 /* Buffer for macro expansion */
55 static char mlinebuf [LINESIZE];
56 static char* mline = mlinebuf;
59 /* Flag: Expand macros in this line */
60 static int ExpandMacros = 1;
64 /*****************************************************************************/
66 /*****************************************************************************/
70 static int keepch (char c)
71 /* Put character c into translation buffer. */
78 static void keepstr (const char* S)
79 /* Put string str into translation buffer. */
88 static void Comment (void)
89 /* Remove a C comment from line. */
91 /* Remember the current line number, so we can output better error
92 * messages if the comment is not terminated in the current file.
94 unsigned StartingLine = GetCurrentLine();
96 /* Skip the start of comment chars */
100 /* Skip the comment */
101 while (CurC != '*' || NextC != '/') {
103 if (NextLine () == 0) {
104 PPError ("End-of-file reached in comment starting at line %u",
109 if (CurC == '/' && NextC == '*') {
110 PPWarning ("`/*' found inside a comment");
116 /* Skip the end of comment chars */
123 static void SkipBlank (void)
124 /* Skip blanks and tabs in the input stream. */
126 while (IsBlank (CurC)) {
133 static char* CopyQuotedString (char* Target)
134 /* Copy a single or double quoted string from the input to Target. Return the
135 * new target pointer. Target will not be terminated after the copy.
138 /* Remember the quote character, copy it to the target buffer and skip it */
143 /* Copy the characters inside the string */
144 while (CurC != '\0' && CurC != Quote) {
145 /* Keep an escaped char */
150 /* Copy the character */
155 /* If we had a terminating quote, copy it */
161 /* Return the new target pointer */
167 /*****************************************************************************/
169 /*****************************************************************************/
173 static int MacName (char* Ident)
174 /* Get macro symbol name. If error, print message and clear line. */
176 if (IsSym (Ident) == 0) {
177 PPError ("Identifier expected");
187 static void ExpandMacroArgs (Macro* M)
188 /* Preprocessor pass 2. Perform macro substitution. */
191 const char* Replacement;
194 /* Save the current line pointer and setup the new ones */
196 InitLine (M->Replacement);
198 /* Copy the macro replacement checking for parameters to replace */
199 while (CurC != '\0') {
200 /* If the next token is an identifier, check for a macro arg */
201 if (IsIdent (CurC)) {
203 Replacement = FindMacroArg (M, Ident);
205 /* Macro arg, keep the replacement */
206 keepstr (Replacement);
208 /* No macro argument, keep the original identifier */
211 } else if (CurC == '#' && IsIdent (NextC)) {
214 Replacement = FindMacroArg (M, Ident);
217 keepstr (Replacement);
223 } else if (IsQuote (CurC)) {
224 mptr = CopyQuotedString (mptr);
231 /* Reset the line pointer */
237 static int MacroCall (Macro* M)
238 /* Process a function like macro */
240 unsigned ArgCount; /* Macro argument count */
241 unsigned ParCount; /* Number of open parenthesis */
242 char Buf[LINESIZE]; /* Argument buffer */
243 const char* ArgStart;
246 /* Expect an argument list */
249 PPError ("Illegal macro call");
253 /* Eat the left paren */
256 /* Read the actual macro arguments and store pointers to these arguments
257 * into the array of actual arguments in the macro definition.
265 /* Nested parenthesis */
269 } else if (IsQuote (CurC)) {
270 B = CopyQuotedString (B);
271 } else if (CurC == ',' || CurC == ')') {
273 /* End of actual argument */
275 while (IsBlank(*ArgStart)) {
278 if (ArgCount < M->ArgCount) {
279 M->ActualArgs[ArgCount++] = ArgStart;
280 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
281 /* Be sure not to count the single empty argument for a
282 * macro that does not have arguments.
287 /* Check for end of macro param list */
293 /* Start the next param */
297 /* Comma or right paren inside nested parenthesis */
304 } else if (IsBlank (CurC)) {
305 /* Squeeze runs of blanks */
308 } else if (CurC == '\0') {
309 /* End of line inside macro argument list - read next line */
310 if (NextLine () == 0) {
314 /* Just copy the character */
320 /* Compare formal argument count with actual */
321 if (M->ArgCount != ArgCount) {
322 PPError ("Macro argument count mismatch");
323 /* Be sure to make enough empty arguments available */
324 while (ArgCount < M->ArgCount) {
325 M->ActualArgs [ArgCount++] = "";
329 /* Preprocess the line, replacing macro parameters */
338 static void ExpandMacro (Macro* M)
341 /* Check if this is a function like macro */
342 if (M->ArgCount >= 0) {
343 /* Function like macro */
344 if (MacroCall (M) == 0) {
348 /* Just copy the replacement text */
349 keepstr (M->Replacement);
355 static void addmac (void)
356 /* Add a macro to the macro table. */
364 /* Read the macro name */
366 if (!MacName (Ident)) {
370 /* Get an existing macro definition with this name */
371 Existing = FindMacro (Ident);
373 /* Create a new macro definition */
374 M = NewMacro (Ident);
376 /* Check if this is a function like macro */
379 /* Skip the left paren */
382 /* Set the marker that this is a function like macro */
385 /* Read the formal parameter list */
390 if (MacName (Ident) == 0) {
393 AddMacroArg (M, Ident);
400 /* Check for a right paren and eat it if we find one */
402 PPError ("`)' expected");
409 /* Insert the macro into the macro table and allocate the ActualArgs array */
412 /* Remove whitespace and comments from the line, store the preprocessed
420 /* Create a copy of the replacement */
421 M->Replacement = xstrdup (Buf);
423 /* If we have an existing macro, check if the redefinition is identical.
424 * Print a diagnostic if not.
427 if (MacroCmp (M, Existing) != 0) {
428 PPError ("Macro redefinition is not identical");
435 /*****************************************************************************/
437 /*****************************************************************************/
441 static int Pass1 (const char* From, char* To)
442 /* Preprocessor pass 1. Remove whitespace and comments. */
448 /* Initialize reading from "From" */
454 /* Loop removing ws and comments */
456 while (CurC != '\0') {
457 if (IsBlank (CurC)) {
460 } else if (IsIdent (CurC)) {
462 if (Preprocessing && strcmp(Ident, "defined") == 0) {
463 /* Handle the "defined" operator */
471 if (!IsIdent (CurC)) {
472 PPError ("Identifier expected");
476 *mptr++ = IsMacro (Ident)? '1' : '0';
480 PPError ("`)' expected");
487 if (MaybeMacro (Ident[0])) {
492 } else if (IsQuote (CurC)) {
493 mptr = CopyQuotedString (mptr);
494 } else if (CurC == '/' && NextC == '*') {
497 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
499 /* Beware: Because line continuation chars are handled when reading
500 * lines, we may only skip til the end of the source line, which
501 * may not be the same as the end of the input line. The end of the
502 * source line is denoted by a lf (\n) character.
506 } while (CurC != '\n' && CurC != '\0');
521 static int Pass2 (const char* From, char* To)
522 /* Preprocessor pass 2. Perform macro substitution. */
528 /* Initialize reading from "From" */
534 /* Loop substituting macros */
536 while (CurC != '\0') {
537 /* If we have an identifier, check if it's a macro */
538 if (IsIdent (CurC)) {
540 M = FindMacro (Ident);
547 } else if (IsQuote (CurC)) {
548 mptr = CopyQuotedString (mptr);
559 static void xlateline (void)
560 /* Translate one line. */
565 Done = Pass1 (line, mline);
566 if (ExpandMacros == 0) {
568 ExpandMacros = 1; /* Reset to default */
572 /* Swap mline and line */
578 Done = Pass2 (line, mline);
582 /* Reinitialize line parsing */
588 static void doundef (void)
589 /* Process #undef directive */
594 if (MacName (Ident)) {
595 UndefineMacro (Ident);
601 static int setmflag (int skip, int flag, int cond)
602 /* setmflag( skip, flag, cond ) */
605 s_ifdef[++i_ifdef] = 3;
608 s_ifdef[++i_ifdef] = 6;
609 return (flag ^ cond);
615 static int doiff (int skip)
616 /* Process #if directive */
621 /* We're about to abuse the compiler expression parser to evaluate the
622 * #if expression. Save the current tokens to come back here later.
627 /* Remove the #if from the line and add two semicolons as sentinels */
630 while (CurC != '\0') {
638 /* Start over parsing from line */
641 /* Switch into special preprocessing mode */
644 /* Expand macros in this line */
647 /* Prime the token pump (remove old tokens from the stream) */
651 /* Call the expression parser */
654 /* End preprocessing mode */
657 /* Reset the old tokens */
661 /* Set the #if condition according to the expression result */
662 return (setmflag (skip, 1, lval.e_const != 0));
667 static int doifdef (int skip, int flag)
668 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
673 if (MacName (Ident) == 0) {
676 return setmflag (skip, flag, IsMacro(Ident));
682 static void doinclude (void)
683 /* Open an include file. */
692 /* Get the next char and check for a valid file name terminator. Setup
693 * the include directory spec (SYS/USR) by looking at the terminator.
708 PPError ("`\"' or `<' expected");
713 /* Copy the filename into mline. Since mline has the same size as the
714 * input line, we don't need to check for an overflow here.
717 while (CurC != '\0' && CurC != RTerm) {
723 /* Check if we got a terminator */
725 /* No terminator found */
726 PPError ("Missing terminator or file name too long");
730 /* Open the include file */
731 OpenIncludeFile (mline, DirSpec);
734 /* Clear the remaining line so the next input will come from the new
742 static void doerror (void)
747 PPError ("Invalid #error directive");
749 PPError ("#error: %s", lptr);
752 /* clear rest of line */
758 /* C preprocessor. */
760 /* stuff used to bum the keyword dispatching stuff */
776 static const struct tok_elt pre_toks[] = {
777 { "define", PP_DEFINE },
779 { "endif", PP_ENDIF },
780 { "error", PP_ERROR },
782 { "ifdef", PP_IFDEF },
783 { "ifndef", PP_IFNDEF },
784 { "include", PP_INCLUDE },
786 { "pragma", PP_PRAGMA },
787 { "undef", PP_UNDEF },
793 static int searchtok (const char *sym, const struct tok_elt *toks)
794 /* Search a token in a table */
796 while (toks->toknam && strcmp (toks->toknam, sym))
798 return (toks->toknbr);
803 void Preprocess (void)
804 /* Preprocess a line */
809 /* Skip white space at the beginning of the line */
812 /* Check for stuff to skip */
814 while (CurC == '\0' || CurC == '#' || Skip) {
816 /* Check for preprocessor lines lines */
821 /* Ignore the empty preprocessor directive */
824 if (!IsSym (Directive)) {
825 PPError ("Preprocessor directive expected");
828 switch (searchtok (Directive, pre_toks)) {
837 if (s_ifdef[i_ifdef] & 2) {
838 if (s_ifdef[i_ifdef] & 4) {
841 s_ifdef[i_ifdef] ^= 2;
843 PPError ("Unexpected `#else'");
849 Skip = s_ifdef[i_ifdef--] & 1;
851 PPError ("Unexpected `#endif'");
866 Skip = doifdef (Skip, 1);
870 Skip = doifdef (Skip, 0);
880 /* Not allowed in strict ANSI mode */
882 PPError ("Preprocessor directive expected");
889 /* Don't expand macros in this line */
891 /* #pragma is handled on the scanner level */
903 PPError ("Preprocessor directive expected");
909 if (NextLine () == 0) {
911 PPError ("`#endif' expected");
920 Print (stdout, 2, "line: %s\n", line);