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 /*****************************************************************************/
66 /* Low level preprocessor token handling */
67 /*****************************************************************************/
71 /* Types of preprocessor tokens */
89 /* Preprocessor keyword to token mapping table */
90 static const struct PPToken {
91 const char* Key; /* Keyword */
92 pptoken_t Tok; /* Token */
94 { "define", PP_DEFINE },
96 { "endif", PP_ENDIF },
97 { "error", PP_ERROR },
99 { "ifdef", PP_IFDEF },
100 { "ifndef", PP_IFNDEF },
101 { "include", PP_INCLUDE },
103 { "pragma", PP_PRAGMA },
104 { "undef", PP_UNDEF },
107 /* Number of preprocessor tokens */
108 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
112 static int CmpToken (const void* Key, const void* Elem)
113 /* Compare function for bsearch */
115 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
120 static pptoken_t FindPPToken (const char* Ident)
121 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
122 * is not a valid preprocessor token.
126 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
127 return P? P->Tok : PP_ILLEGAL;
132 /*****************************************************************************/
134 /*****************************************************************************/
138 static int keepch (char c)
139 /* Put character c into translation buffer. */
141 return (*mptr++ = c);
146 static void keepstr (const char* S)
147 /* Put string str into translation buffer. */
156 static void Comment (void)
157 /* Remove a C comment from line. */
159 /* Remember the current line number, so we can output better error
160 * messages if the comment is not terminated in the current file.
162 unsigned StartingLine = GetCurrentLine();
164 /* Skip the start of comment chars */
168 /* Skip the comment */
169 while (CurC != '*' || NextC != '/') {
171 if (NextLine () == 0) {
172 PPError ("End-of-file reached in comment starting at line %u",
177 if (CurC == '/' && NextC == '*') {
178 PPWarning ("`/*' found inside a comment");
184 /* Skip the end of comment chars */
191 static void SkipBlank (void)
192 /* Skip blanks and tabs in the input stream. */
194 while (IsBlank (CurC)) {
201 static char* CopyQuotedString (char* Target)
202 /* Copy a single or double quoted string from the input to Target. Return the
203 * new target pointer. Target will not be terminated after the copy.
206 /* Remember the quote character, copy it to the target buffer and skip it */
211 /* Copy the characters inside the string */
212 while (CurC != '\0' && CurC != Quote) {
213 /* Keep an escaped char */
218 /* Copy the character */
223 /* If we had a terminating quote, copy it */
229 /* Return the new target pointer */
235 /*****************************************************************************/
237 /*****************************************************************************/
241 static int MacName (char* Ident)
242 /* Get macro symbol name. If we have an error, print a diagnostic message
246 if (IsSym (Ident) == 0) {
247 PPError ("Identifier expected");
257 static void ExpandMacroArgs (Macro* M)
258 /* Expand the arguments of a macro */
261 const char* Replacement;
264 /* Save the current line pointer and setup the new ones */
266 InitLine (M->Replacement);
268 /* Copy the macro replacement checking for parameters to replace */
269 while (CurC != '\0') {
270 /* If the next token is an identifier, check for a macro arg */
271 if (IsIdent (CurC)) {
273 Replacement = FindMacroArg (M, Ident);
275 /* Macro arg, keep the replacement */
276 keepstr (Replacement);
278 /* No macro argument, keep the original identifier */
281 } else if (CurC == '#' && IsIdent (NextC)) {
284 Replacement = FindMacroArg (M, Ident);
287 keepstr (Replacement);
293 } else if (IsQuote (CurC)) {
294 mptr = CopyQuotedString (mptr);
301 /* Reset the line pointer */
307 static int MacroCall (Macro* M)
308 /* Process a function like macro */
310 unsigned ArgCount; /* Macro argument count */
311 unsigned ParCount; /* Number of open parenthesis */
312 char Buf[LINESIZE]; /* Argument buffer */
313 const char* ArgStart;
316 /* Expect an argument list */
319 PPError ("Illegal macro call");
323 /* Eat the left paren */
326 /* Read the actual macro arguments and store pointers to these arguments
327 * into the array of actual arguments in the macro definition.
335 /* Nested parenthesis */
339 } else if (IsQuote (CurC)) {
340 B = CopyQuotedString (B);
341 } else if (CurC == ',' || CurC == ')') {
343 /* End of actual argument */
345 while (IsBlank(*ArgStart)) {
348 if (ArgCount < M->ArgCount) {
349 M->ActualArgs[ArgCount++] = ArgStart;
350 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
351 /* Be sure not to count the single empty argument for a
352 * macro that does not have arguments.
357 /* Check for end of macro param list */
363 /* Start the next param */
367 /* Comma or right paren inside nested parenthesis */
374 } else if (IsBlank (CurC)) {
375 /* Squeeze runs of blanks */
378 } else if (CurC == '\0') {
379 /* End of line inside macro argument list - read next line */
380 if (NextLine () == 0) {
384 /* Just copy the character */
390 /* Compare formal argument count with actual */
391 if (M->ArgCount != ArgCount) {
392 PPError ("Macro argument count mismatch");
393 /* Be sure to make enough empty arguments available */
394 while (ArgCount < M->ArgCount) {
395 M->ActualArgs [ArgCount++] = "";
399 /* Preprocess the line, replacing macro parameters */
408 static void ExpandMacro (Macro* M)
411 /* Check if this is a function like macro */
412 if (M->ArgCount >= 0) {
413 /* Function like macro */
414 if (MacroCall (M) == 0) {
418 /* Just copy the replacement text */
419 keepstr (M->Replacement);
425 static void DefineMacro (void)
426 /* Handle a macro definition. */
434 /* Read the macro name */
436 if (!MacName (Ident)) {
440 /* Get an existing macro definition with this name */
441 Existing = FindMacro (Ident);
443 /* Create a new macro definition */
444 M = NewMacro (Ident);
446 /* Check if this is a function like macro */
449 /* Skip the left paren */
452 /* Set the marker that this is a function like macro */
455 /* Read the formal parameter list */
460 if (MacName (Ident) == 0) {
463 AddMacroArg (M, Ident);
470 /* Check for a right paren and eat it if we find one */
472 PPError ("`)' expected");
479 /* Insert the macro into the macro table and allocate the ActualArgs array */
482 /* Remove whitespace and comments from the line, store the preprocessed
490 /* Create a copy of the replacement */
491 M->Replacement = xstrdup (Buf);
493 /* If we have an existing macro, check if the redefinition is identical.
494 * Print a diagnostic if not.
497 if (MacroCmp (M, Existing) != 0) {
498 PPError ("Macro redefinition is not identical");
505 /*****************************************************************************/
507 /*****************************************************************************/
511 static int Pass1 (const char* From, char* To)
512 /* Preprocessor pass 1. Remove whitespace and comments. */
518 /* Initialize reading from "From" */
524 /* Loop removing ws and comments */
526 while (CurC != '\0') {
527 if (IsBlank (CurC)) {
530 } else if (IsIdent (CurC)) {
532 if (Preprocessing && strcmp(Ident, "defined") == 0) {
533 /* Handle the "defined" operator */
541 if (!IsIdent (CurC)) {
542 PPError ("Identifier expected");
546 *mptr++ = IsMacro (Ident)? '1' : '0';
550 PPError ("`)' expected");
557 if (MaybeMacro (Ident[0])) {
562 } else if (IsQuote (CurC)) {
563 mptr = CopyQuotedString (mptr);
564 } else if (CurC == '/' && NextC == '*') {
567 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
569 /* Beware: Because line continuation chars are handled when reading
570 * lines, we may only skip til the end of the source line, which
571 * may not be the same as the end of the input line. The end of the
572 * source line is denoted by a lf (\n) character.
576 } while (CurC != '\n' && CurC != '\0');
591 static int Pass2 (const char* From, char* To)
592 /* Preprocessor pass 2. Perform macro substitution. */
598 /* Initialize reading from "From" */
604 /* Loop substituting macros */
606 while (CurC != '\0') {
607 /* If we have an identifier, check if it's a macro */
608 if (IsIdent (CurC)) {
610 M = FindMacro (Ident);
617 } else if (IsQuote (CurC)) {
618 mptr = CopyQuotedString (mptr);
629 static void xlateline (void)
630 /* Translate one line. */
635 Done = Pass1 (line, mline);
636 if (ExpandMacros == 0) {
638 ExpandMacros = 1; /* Reset to default */
642 /* Swap mline and line */
648 Done = Pass2 (line, mline);
652 /* Reinitialize line parsing */
658 static void doundef (void)
659 /* Process #undef directive */
664 if (MacName (Ident)) {
665 UndefineMacro (Ident);
671 static int setmflag (int skip, int flag, int cond)
672 /* setmflag( skip, flag, cond ) */
675 s_ifdef[++i_ifdef] = 3;
678 s_ifdef[++i_ifdef] = 6;
679 return (flag ^ cond);
685 static int doiff (int skip)
686 /* Process #if directive */
691 /* We're about to abuse the compiler expression parser to evaluate the
692 * #if expression. Save the current tokens to come back here later.
693 * NOTE: Yes, this is a hack, but it saves a complete separate expression
694 * evaluation for the preprocessor.
699 /* Make sure the line infos for the tokens won't get removed */
701 UseLineInfo (sv1.LI);
704 UseLineInfo (sv2.LI);
707 /* Remove the #if from the line and add two semicolons as sentinels */
710 while (CurC != '\0') {
718 /* Start over parsing from line */
721 /* Switch into special preprocessing mode */
724 /* Expand macros in this line */
727 /* Prime the token pump (remove old tokens from the stream) */
731 /* Call the expression parser */
734 /* End preprocessing mode */
737 /* Reset the old tokens */
741 /* Set the #if condition according to the expression result */
742 return (setmflag (skip, 1, lval.ConstVal != 0));
747 static int doifdef (int skip, int flag)
748 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
753 if (MacName (Ident) == 0) {
756 return setmflag (skip, flag, IsMacro(Ident));
762 static void doinclude (void)
763 /* Open an include file. */
772 /* Get the next char and check for a valid file name terminator. Setup
773 * the include directory spec (SYS/USR) by looking at the terminator.
788 PPError ("`\"' or `<' expected");
793 /* Copy the filename into mline. Since mline has the same size as the
794 * input line, we don't need to check for an overflow here.
797 while (CurC != '\0' && CurC != RTerm) {
803 /* Check if we got a terminator */
805 /* No terminator found */
806 PPError ("Missing terminator or file name too long");
810 /* Open the include file */
811 OpenIncludeFile (mline, DirSpec);
814 /* Clear the remaining line so the next input will come from the new
822 static void doerror (void)
827 PPError ("Invalid #error directive");
829 PPError ("#error: %s", lptr);
832 /* clear rest of line */
838 void Preprocess (void)
839 /* Preprocess a line */
844 /* Skip white space at the beginning of the line */
847 /* Check for stuff to skip */
849 while (CurC == '\0' || CurC == '#' || Skip) {
851 /* Check for preprocessor lines lines */
856 /* Ignore the empty preprocessor directive */
859 if (!IsSym (Directive)) {
860 PPError ("Preprocessor directive expected");
863 switch (FindPPToken (Directive)) {
872 if (s_ifdef[i_ifdef] & 2) {
873 if (s_ifdef[i_ifdef] & 4) {
876 s_ifdef[i_ifdef] ^= 2;
878 PPError ("Unexpected `#else'");
884 Skip = s_ifdef[i_ifdef--] & 1;
886 PPError ("Unexpected `#endif'");
901 Skip = doifdef (Skip, 1);
905 Skip = doifdef (Skip, 0);
915 /* Not allowed in strict ANSI mode */
917 PPError ("Preprocessor directive expected");
924 /* Don't expand macros in this line */
926 /* #pragma is handled on the scanner level */
938 PPError ("Preprocessor directive expected");
944 if (NextLine () == 0) {
946 PPError ("`#endif' expected");
955 Print (stdout, 2, "line: %s\n", line);