2 /* C pre-processor functions */
30 /*****************************************************************************/
32 /*****************************************************************************/
36 static int Pass1 (const char* From, char* To);
37 /* Preprocessor pass 1. Remove whitespace and comments. */
41 /*****************************************************************************/
43 /*****************************************************************************/
47 /* Set when the pp calls expr() recursively */
48 unsigned char Preprocessing = 0;
50 /* Management data for #if */
52 static int i_ifdef = -1;
53 static char s_ifdef[N_IFDEF];
55 /* Buffer for macro expansion */
56 static char mlinebuf [LINESIZE];
57 static char* mline = mlinebuf;
60 /* Flag: Expand macros in this line */
61 static int ExpandMacros = 1;
65 /*****************************************************************************/
67 /*****************************************************************************/
71 static int keepch (char c)
72 /* Put character c into translation buffer. */
79 static void keepstr (const char* S)
80 /* Put string str into translation buffer. */
89 static void Comment (void)
90 /* Remove a C comment from line. */
92 /* Remember the current line number, so we can output better error
93 * messages if the comment is not terminated in the current file.
95 unsigned StartingLine = GetCurrentLine();
97 /* Skip the start of comment chars */
101 /* Skip the comment */
102 while (CurC != '*' || NextC != '/') {
104 if (NextLine () == 0) {
105 PPError ("End-of-file reached in comment starting at line %u",
110 if (CurC == '/' && NextC == '*') {
111 PPWarning ("`/*' found inside a comment");
117 /* Skip the end of comment chars */
124 static void SkipBlank (void)
125 /* Skip blanks and tabs in the input stream. */
127 while (IsBlank (CurC)) {
134 static char* CopyQuotedString (char* Target)
135 /* Copy a single or double quoted string from the input to Target. Return the
136 * new target pointer. Target will not be terminated after the copy.
139 /* Remember the quote character, copy it to the target buffer and skip it */
144 /* Copy the characters inside the string */
145 while (CurC != '\0' && CurC != Quote) {
146 /* Keep an escaped char */
151 /* Copy the character */
156 /* If we had a terminating quote, copy it */
162 /* Return the new target pointer */
168 /*****************************************************************************/
170 /*****************************************************************************/
174 static int MacName (char* Ident)
175 /* Get macro symbol name. If error, print message and clear line. */
177 if (IsSym (Ident) == 0) {
178 PPError ("Identifier expected");
188 static void ExpandMacroArgs (Macro* M)
189 /* Preprocessor pass 2. Perform macro substitution. */
192 const char* Replacement;
195 /* Save the current line pointer and setup the new ones */
197 InitLine (M->Replacement);
199 /* Copy the macro replacement checking for parameters to replace */
200 while (CurC != '\0') {
201 /* If the next token is an identifier, check for a macro arg */
202 if (IsIdent (CurC)) {
204 Replacement = FindMacroArg (M, Ident);
206 /* Macro arg, keep the replacement */
207 keepstr (Replacement);
209 /* No macro argument, keep the original identifier */
212 } else if (CurC == '#' && IsIdent (NextC)) {
215 Replacement = FindMacroArg (M, Ident);
218 keepstr (Replacement);
224 } else if (IsQuote (CurC)) {
225 mptr = CopyQuotedString (mptr);
232 /* Reset the line pointer */
238 static int MacroCall (Macro* M)
239 /* Process a function like macro */
241 unsigned ArgCount; /* Macro argument count */
242 unsigned ParCount; /* Number of open parenthesis */
243 char Buf[LINESIZE]; /* Argument buffer */
244 const char* ArgStart;
247 /* Expect an argument list */
250 PPError ("Illegal macro call");
254 /* Eat the left paren */
257 /* Read the actual macro arguments and store pointers to these arguments
258 * into the array of actual arguments in the macro definition.
266 /* Nested parenthesis */
270 } else if (IsQuote (CurC)) {
271 B = CopyQuotedString (B);
272 } else if (CurC == ',' || CurC == ')') {
274 /* End of actual argument */
276 while (IsBlank(*ArgStart)) {
279 if (ArgCount < M->ArgCount) {
280 M->ActualArgs[ArgCount++] = ArgStart;
281 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
282 /* Be sure not to count the single empty argument for a
283 * macro that does not have arguments.
288 /* Check for end of macro param list */
294 /* Start the next param */
298 /* Comma or right paren inside nested parenthesis */
305 } else if (IsBlank (CurC)) {
306 /* Squeeze runs of blanks */
309 } else if (CurC == '\0') {
310 /* End of line inside macro argument list - read next line */
311 if (NextLine () == 0) {
315 /* Just copy the character */
321 /* Compare formal argument count with actual */
322 if (M->ArgCount != ArgCount) {
323 PPError ("Macro argument count mismatch");
324 /* Be sure to make enough empty arguments available */
325 while (ArgCount < M->ArgCount) {
326 M->ActualArgs [ArgCount++] = "";
330 /* Preprocess the line, replacing macro parameters */
339 static void ExpandMacro (Macro* M)
342 /* Check if this is a function like macro */
343 if (M->ArgCount >= 0) {
344 /* Function like macro */
345 if (MacroCall (M) == 0) {
349 /* Just copy the replacement text */
350 keepstr (M->Replacement);
356 static void addmac (void)
357 /* Add a macro to the macro table. */
365 /* Read the macro name */
367 if (!MacName (Ident)) {
371 /* Get an existing macro definition with this name */
372 Existing = FindMacro (Ident);
374 /* Create a new macro definition */
375 M = NewMacro (Ident);
377 /* Check if this is a function like macro */
380 /* Skip the left paren */
383 /* Set the marker that this is a function like macro */
386 /* Read the formal parameter list */
391 if (MacName (Ident) == 0) {
394 AddMacroArg (M, Ident);
401 /* Check for a right paren and eat it if we find one */
403 PPError ("`)' expected");
410 /* Insert the macro into the macro table and allocate the ActualArgs array */
413 /* Remove whitespace and comments from the line, store the preprocessed
421 /* Create a copy of the replacement */
422 M->Replacement = xstrdup (Buf);
424 /* If we have an existing macro, check if the redefinition is identical.
425 * Print a diagnostic if not.
428 if (MacroCmp (M, Existing) != 0) {
429 PPError ("Macro redefinition is not identical");
436 /*****************************************************************************/
438 /*****************************************************************************/
442 static int Pass1 (const char* From, char* To)
443 /* Preprocessor pass 1. Remove whitespace and comments. */
449 /* Initialize reading from "From" */
455 /* Loop removing ws and comments */
457 while (CurC != '\0') {
458 if (IsBlank (CurC)) {
461 } else if (IsIdent (CurC)) {
463 if (Preprocessing && strcmp(Ident, "defined") == 0) {
464 /* Handle the "defined" operator */
472 if (!IsIdent (CurC)) {
473 PPError ("Identifier expected");
477 *mptr++ = IsMacro (Ident)? '1' : '0';
481 PPError ("`)' expected");
488 if (MaybeMacro (Ident[0])) {
493 } else if (IsQuote (CurC)) {
494 mptr = CopyQuotedString (mptr);
495 } else if (CurC == '/' && NextC == '*') {
498 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
500 /* Beware: Because line continuation chars are handled when reading
501 * lines, we may only skip til the end of the source line, which
502 * may not be the same as the end of the input line. The end of the
503 * source line is denoted by a lf (\n) character.
507 } while (CurC != '\n' && CurC != '\0');
522 static int Pass2 (const char* From, char* To)
523 /* Preprocessor pass 2. Perform macro substitution. */
529 /* Initialize reading from "From" */
535 /* Loop substituting macros */
537 while (CurC != '\0') {
538 /* If we have an identifier, check if it's a macro */
539 if (IsIdent (CurC)) {
541 M = FindMacro (Ident);
548 } else if (IsQuote (CurC)) {
549 mptr = CopyQuotedString (mptr);
560 static void xlateline (void)
561 /* Translate one line. */
566 Done = Pass1 (line, mline);
567 if (ExpandMacros == 0) {
569 ExpandMacros = 1; /* Reset to default */
573 /* Swap mline and line */
579 Done = Pass2 (line, mline);
583 /* Reinitialize line parsing */
589 static void doundef (void)
590 /* Process #undef directive */
595 if (MacName (Ident)) {
596 UndefineMacro (Ident);
602 static int setmflag (int skip, int flag, int cond)
603 /* setmflag( skip, flag, cond ) */
606 s_ifdef[++i_ifdef] = 3;
609 s_ifdef[++i_ifdef] = 6;
610 return (flag ^ cond);
616 static int doiff (int skip)
617 /* Process #if directive */
622 /* We're about to abuse the compiler expression parser to evaluate the
623 * #if expression. Save the current tokens to come back here later.
624 * NOTE: Yes, this is a hack, but it saves a complete separate expression
625 * evaluation for the preprocessor.
630 /* Make sure the line infos for the tokens won't get removed */
632 UseLineInfo (sv1.LI);
635 UseLineInfo (sv2.LI);
638 /* Remove the #if from the line and add two semicolons as sentinels */
641 while (CurC != '\0') {
649 /* Start over parsing from line */
652 /* Switch into special preprocessing mode */
655 /* Expand macros in this line */
658 /* Prime the token pump (remove old tokens from the stream) */
662 /* Call the expression parser */
665 /* End preprocessing mode */
668 /* Reset the old tokens */
672 /* Set the #if condition according to the expression result */
673 return (setmflag (skip, 1, lval.ConstVal != 0));
678 static int doifdef (int skip, int flag)
679 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
684 if (MacName (Ident) == 0) {
687 return setmflag (skip, flag, IsMacro(Ident));
693 static void doinclude (void)
694 /* Open an include file. */
703 /* Get the next char and check for a valid file name terminator. Setup
704 * the include directory spec (SYS/USR) by looking at the terminator.
719 PPError ("`\"' or `<' expected");
724 /* Copy the filename into mline. Since mline has the same size as the
725 * input line, we don't need to check for an overflow here.
728 while (CurC != '\0' && CurC != RTerm) {
734 /* Check if we got a terminator */
736 /* No terminator found */
737 PPError ("Missing terminator or file name too long");
741 /* Open the include file */
742 OpenIncludeFile (mline, DirSpec);
745 /* Clear the remaining line so the next input will come from the new
753 static void doerror (void)
758 PPError ("Invalid #error directive");
760 PPError ("#error: %s", lptr);
763 /* clear rest of line */
769 /* C preprocessor. */
771 /* stuff used to bum the keyword dispatching stuff */
787 static const struct tok_elt pre_toks[] = {
788 { "define", PP_DEFINE },
790 { "endif", PP_ENDIF },
791 { "error", PP_ERROR },
793 { "ifdef", PP_IFDEF },
794 { "ifndef", PP_IFNDEF },
795 { "include", PP_INCLUDE },
797 { "pragma", PP_PRAGMA },
798 { "undef", PP_UNDEF },
804 static int searchtok (const char *sym, const struct tok_elt *toks)
805 /* Search a token in a table */
807 while (toks->toknam && strcmp (toks->toknam, sym))
809 return (toks->toknbr);
814 void Preprocess (void)
815 /* Preprocess a line */
820 /* Skip white space at the beginning of the line */
823 /* Check for stuff to skip */
825 while (CurC == '\0' || CurC == '#' || Skip) {
827 /* Check for preprocessor lines lines */
832 /* Ignore the empty preprocessor directive */
835 if (!IsSym (Directive)) {
836 PPError ("Preprocessor directive expected");
839 switch (searchtok (Directive, pre_toks)) {
848 if (s_ifdef[i_ifdef] & 2) {
849 if (s_ifdef[i_ifdef] & 4) {
852 s_ifdef[i_ifdef] ^= 2;
854 PPError ("Unexpected `#else'");
860 Skip = s_ifdef[i_ifdef--] & 1;
862 PPError ("Unexpected `#endif'");
877 Skip = doifdef (Skip, 1);
881 Skip = doifdef (Skip, 0);
891 /* Not allowed in strict ANSI mode */
893 PPError ("Preprocessor directive expected");
900 /* Don't expand macros in this line */
902 /* #pragma is handled on the scanner level */
914 PPError ("Preprocessor directive expected");
920 if (NextLine () == 0) {
922 PPError ("`#endif' expected");
931 Print (stdout, 2, "line: %s\n", line);