2 /* C pre-processor functions */
31 /*****************************************************************************/
33 /*****************************************************************************/
37 static int Pass1 (const char* From, char* To);
38 /* Preprocessor pass 1. Remove whitespace and comments. */
42 /*****************************************************************************/
44 /*****************************************************************************/
48 /* Set when the preprocessor calls expr() recursively */
49 unsigned char Preprocessing = 0;
51 /* Management data for #if */
53 #define IFCOND_NONE 0x00U
54 #define IFCOND_SKIP 0x01U
55 #define IFCOND_ELSE 0x02U
56 #define IFCOND_NEEDTERM 0x04U
57 static unsigned char IfStack[MAX_IFS];
58 static int IfIndex = -1;
60 /* Buffer for macro expansion */
61 static char mlinebuf [LINESIZE];
62 static char* mline = mlinebuf;
65 /* Flag: Expand macros in this line */
66 static int ExpandMacros = 1;
70 /*****************************************************************************/
71 /* Low level preprocessor token handling */
72 /*****************************************************************************/
76 /* Types of preprocessor tokens */
95 /* Preprocessor keyword to token mapping table */
96 static const struct PPToken {
97 const char* Key; /* Keyword */
98 pptoken_t Tok; /* Token */
100 { "define", PP_DEFINE },
103 { "endif", PP_ENDIF },
104 { "error", PP_ERROR },
106 { "ifdef", PP_IFDEF },
107 { "ifndef", PP_IFNDEF },
108 { "include", PP_INCLUDE },
110 { "pragma", PP_PRAGMA },
111 { "undef", PP_UNDEF },
114 /* Number of preprocessor tokens */
115 #define PPTOKEN_COUNT (sizeof(PPTokens) / sizeof(PPTokens[0]))
119 static int CmpToken (const void* Key, const void* Elem)
120 /* Compare function for bsearch */
122 return strcmp ((const char*) Key, ((const struct PPToken*) Elem)->Key);
127 static pptoken_t FindPPToken (const char* Ident)
128 /* Find a preprocessor token and return ut. Return PP_ILLEGAL if the identifier
129 * is not a valid preprocessor token.
133 P = bsearch (Ident, PPTokens, PPTOKEN_COUNT, sizeof (PPTokens[0]), CmpToken);
134 return P? P->Tok : PP_ILLEGAL;
139 /*****************************************************************************/
141 /*****************************************************************************/
145 static void keepch (char c)
146 /* Put character c into translation buffer. */
153 static void keepstr (const char* S)
154 /* Put string str into translation buffer. */
156 unsigned Len = strlen (S);
157 memcpy (mptr, S, Len);
163 static void OldStyleComment (void)
164 /* Remove an old style C comment from line. */
166 /* Remember the current line number, so we can output better error
167 * messages if the comment is not terminated in the current file.
169 unsigned StartingLine = GetCurrentLine();
171 /* Skip the start of comment chars */
175 /* Skip the comment */
176 while (CurC != '*' || NextC != '/') {
178 if (NextLine () == 0) {
179 PPError ("End-of-file reached in comment starting at line %u",
184 if (CurC == '/' && NextC == '*') {
185 PPWarning ("`/*' found inside a comment");
191 /* Skip the end of comment chars */
198 static void NewStyleComment (void)
199 /* Remove a new style C comment from line. */
201 /* Beware: Because line continuation chars are handled when reading
202 * lines, we may only skip til the end of the source line, which
203 * may not be the same as the end of the input line. The end of the
204 * source line is denoted by a lf (\n) character.
208 } while (CurC != '\n' && CurC != '\0');
216 static void SkipBlank (void)
217 /* Skip blanks and tabs in the input stream. */
219 while (IsBlank (CurC)) {
226 static char* CopyQuotedString (char* Target)
227 /* Copy a single or double quoted string from the input to Target. Return the
228 * new target pointer. Target will not be terminated after the copy.
231 /* Remember the quote character, copy it to the target buffer and skip it */
236 /* Copy the characters inside the string */
237 while (CurC != '\0' && CurC != Quote) {
238 /* Keep an escaped char */
243 /* Copy the character */
248 /* If we had a terminating quote, copy it */
254 /* Return the new target pointer */
260 /*****************************************************************************/
262 /*****************************************************************************/
266 static int MacName (char* Ident)
267 /* Get a macro symbol name into Ident. If we have an error, print a
268 * diagnostic message and clear the line.
271 if (IsSym (Ident) == 0) {
272 PPError ("Identifier expected");
282 static void ExpandMacroArgs (Macro* M)
283 /* Expand the arguments of a macro */
286 const char* Replacement;
289 /* Save the current line pointer and setup the new ones */
291 InitLine (M->Replacement);
293 /* Copy the macro replacement checking for parameters to replace */
294 while (CurC != '\0') {
295 /* If the next token is an identifier, check for a macro arg */
296 if (IsIdent (CurC)) {
298 Replacement = FindMacroArg (M, Ident);
300 /* Macro arg, keep the replacement */
301 keepstr (Replacement);
303 /* No macro argument, keep the original identifier */
306 } else if (CurC == '#' && IsIdent (NextC)) {
309 Replacement = FindMacroArg (M, Ident);
312 /* We have to escape any characters inside replacement that
313 * may not be part of a string unescaped.
315 while (*Replacement) {
316 switch (*Replacement) {
322 keepch (*Replacement);
332 } else if (IsQuote (CurC)) {
333 mptr = CopyQuotedString (mptr);
340 /* Reset the line pointer */
346 static int MacroCall (Macro* M)
347 /* Process a function like macro */
349 int ArgCount; /* Macro argument count */
350 unsigned ParCount; /* Number of open parenthesis */
351 char Buf[LINESIZE]; /* Argument buffer */
352 const char* ArgStart;
355 /* Expect an argument list */
358 PPError ("Illegal macro call");
362 /* Eat the left paren */
365 /* Read the actual macro arguments and store pointers to these arguments
366 * into the array of actual arguments in the macro definition.
374 /* Nested parenthesis */
378 } else if (IsQuote (CurC)) {
379 B = CopyQuotedString (B);
380 } else if (CurC == ',' || CurC == ')') {
382 /* End of actual argument */
384 while (IsBlank(*ArgStart)) {
387 if (ArgCount < M->ArgCount) {
388 M->ActualArgs[ArgCount++] = ArgStart;
389 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
390 /* Be sure not to count the single empty argument for a
391 * macro that does not have arguments.
396 /* Check for end of macro param list */
402 /* Start the next param */
406 /* Comma or right paren inside nested parenthesis */
413 } else if (IsBlank (CurC)) {
414 /* Squeeze runs of blanks */
417 } else if (CurC == '/' && NextC == '*') {
420 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
423 } else if (CurC == '\0') {
424 /* End of line inside macro argument list - read next line */
425 if (NextLine () == 0) {
429 /* Just copy the character */
435 /* Compare formal argument count with actual */
436 if (M->ArgCount != ArgCount) {
437 PPError ("Macro argument count mismatch");
438 /* Be sure to make enough empty arguments available */
439 while (ArgCount < M->ArgCount) {
440 M->ActualArgs [ArgCount++] = "";
444 /* Preprocess the line, replacing macro parameters */
453 static void ExpandMacro (Macro* M)
456 /* Check if this is a function like macro */
457 if (M->ArgCount >= 0) {
458 /* Function like macro */
459 if (MacroCall (M) == 0) {
463 /* Just copy the replacement text */
464 keepstr (M->Replacement);
470 static void DefineMacro (void)
471 /* Handle a macro definition. */
479 /* Read the macro name */
481 if (!MacName (Ident)) {
485 /* Get an existing macro definition with this name */
486 Existing = FindMacro (Ident);
488 /* Create a new macro definition */
489 M = NewMacro (Ident);
491 /* Check if this is a function like macro */
494 /* Skip the left paren */
497 /* Set the marker that this is a function like macro */
500 /* Read the formal parameter list */
505 if (MacName (Ident) == 0) {
508 AddMacroArg (M, Ident);
515 /* Check for a right paren and eat it if we find one */
517 PPError ("`)' expected");
524 /* Insert the macro into the macro table and allocate the ActualArgs array */
527 /* Remove whitespace and comments from the line, store the preprocessed
535 /* Create a copy of the replacement */
536 M->Replacement = xstrdup (Buf);
538 /* If we have an existing macro, check if the redefinition is identical.
539 * Print a diagnostic if not.
542 if (MacroCmp (M, Existing) != 0) {
543 PPError ("Macro redefinition is not identical");
550 /*****************************************************************************/
552 /*****************************************************************************/
556 static int Pass1 (const char* From, char* To)
557 /* Preprocessor pass 1. Remove whitespace and comments. */
563 /* Initialize reading from "From" */
569 /* Loop removing ws and comments */
571 while (CurC != '\0') {
572 if (IsBlank (CurC)) {
575 } else if (IsIdent (CurC)) {
577 if (Preprocessing && strcmp(Ident, "defined") == 0) {
578 /* Handle the "defined" operator */
586 if (!IsIdent (CurC)) {
587 PPError ("Identifier expected");
591 *mptr++ = IsMacro (Ident)? '1' : '0';
595 PPError ("`)' expected");
602 if (MaybeMacro (Ident[0])) {
607 } else if (IsQuote (CurC)) {
608 mptr = CopyQuotedString (mptr);
609 } else if (CurC == '/' && NextC == '*') {
612 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
626 static int Pass2 (const char* From, char* To)
627 /* Preprocessor pass 2. Perform macro substitution. */
633 /* Initialize reading from "From" */
639 /* Loop substituting macros */
641 while (CurC != '\0') {
642 /* If we have an identifier, check if it's a macro */
643 if (IsIdent (CurC)) {
645 M = FindMacro (Ident);
652 } else if (IsQuote (CurC)) {
653 mptr = CopyQuotedString (mptr);
664 static void xlateline (void)
665 /* Translate one line. */
670 Done = Pass1 (line, mline);
671 if (ExpandMacros == 0) {
673 ExpandMacros = 1; /* Reset to default */
677 /* Swap mline and line */
683 Done = Pass2 (line, mline);
687 /* Reinitialize line parsing */
693 static void DoUndef (void)
694 /* Process the #undef directive */
699 if (MacName (Ident)) {
700 UndefineMacro (Ident);
706 static int PushIf (int Skip, int Invert, int Cond)
707 /* Push a new if level onto the if stack */
709 /* Check for an overflow of the if stack */
710 if (IfIndex >= MAX_IFS-1) {
711 PPError ("Too many nested #if clauses");
715 /* Push the #if condition */
718 IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM;
721 IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM;
722 return (Invert ^ Cond);
728 static int DoIf (int Skip)
729 /* Process #if directive */
734 /* We're about to abuse the compiler expression parser to evaluate the
735 * #if expression. Save the current tokens to come back here later.
736 * NOTE: Yes, this is a hack, but it saves a complete separate expression
737 * evaluation for the preprocessor.
742 /* Make sure the line infos for the tokens won't get removed */
744 UseLineInfo (sv1.LI);
747 UseLineInfo (sv2.LI);
750 /* Remove the #if from the line and add two semicolons as sentinels */
753 while (CurC != '\0') {
761 /* Start over parsing from line */
764 /* Switch into special preprocessing mode */
767 /* Expand macros in this line */
770 /* Prime the token pump (remove old tokens from the stream) */
774 /* Call the expression parser */
777 /* End preprocessing mode */
780 /* Reset the old tokens */
784 /* Set the #if condition according to the expression result */
785 return PushIf (Skip, 1, lval.ConstVal != 0);
790 static int DoIfDef (int skip, int flag)
791 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
796 if (MacName (Ident) == 0) {
799 return PushIf (skip, flag, IsMacro(Ident));
805 static void DoInclude (void)
806 /* Open an include file. */
815 /* Get the next char and check for a valid file name terminator. Setup
816 * the include directory spec (SYS/USR) by looking at the terminator.
831 PPError ("`\"' or `<' expected");
836 /* Copy the filename into mline. Since mline has the same size as the
837 * input line, we don't need to check for an overflow here.
840 while (CurC != '\0' && CurC != RTerm) {
846 /* Check if we got a terminator */
848 /* No terminator found */
849 PPError ("Missing terminator or file name too long");
853 /* Open the include file */
854 OpenIncludeFile (mline, DirSpec);
857 /* Clear the remaining line so the next input will come from the new
865 static void DoError (void)
870 PPError ("Invalid #error directive");
872 PPError ("#error: %s", lptr);
875 /* Clear the rest of line */
881 void Preprocess (void)
882 /* Preprocess a line */
887 /* Skip white space at the beginning of the line */
890 /* Check for stuff to skip */
892 while (CurC == '\0' || CurC == '#' || Skip) {
894 /* Check for preprocessor lines lines */
899 /* Ignore the empty preprocessor directive */
902 if (!IsSym (Directive)) {
903 PPError ("Preprocessor directive expected");
906 switch (FindPPToken (Directive)) {
916 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
918 /* Handle as #else/#if combination */
919 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
922 IfStack[IfIndex] |= IFCOND_ELSE;
925 /* #elif doesn't need a terminator */
926 IfStack[IfIndex] &= ~IFCOND_NEEDTERM;
928 PPError ("Duplicate #else/#elif");
931 PPError ("Unexpected #elif");
937 if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) {
938 if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) {
941 IfStack[IfIndex] |= IFCOND_ELSE;
943 PPError ("Duplicate #else");
946 PPError ("Unexpected `#else'");
952 /* Remove any clauses on top of stack that do not
953 * need a terminating #endif.
955 while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) {
959 /* Stack may not be empty here or something is wrong */
960 CHECK (IfIndex >= 0);
962 /* Remove the clause that needs a terminator */
963 Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0;
965 PPError ("Unexpected `#endif'");
980 Skip = DoIfDef (Skip, 1);
984 Skip = DoIfDef (Skip, 0);
994 /* Not allowed in strict ANSI mode */
996 PPError ("Preprocessor directive expected");
1003 /* Don't expand macros in this line */
1005 /* #pragma is handled on the scanner level */
1017 PPError ("Preprocessor directive expected");
1023 if (NextLine () == 0) {
1025 PPError ("`#endif' expected");
1034 Print (stdout, 2, "line: %s\n", line);