2 /* C pre-processor functions */
10 #include "../common/xmalloc.h"
26 /*****************************************************************************/
28 /*****************************************************************************/
32 static int Pass1 (const char* From, char* To);
33 /* Preprocessor pass 1. Remove whitespace and comments. */
37 /*****************************************************************************/
39 /*****************************************************************************/
43 /* Set when the pp calls expr() recursively */
44 unsigned char Preprocessing = 0;
46 /* Management data for #if */
48 static int i_ifdef = -1;
49 static char s_ifdef[N_IFDEF];
51 /* Buffer for macro expansion */
52 static char mlinebuf [LINESIZE];
53 static char* mline = mlinebuf;
56 /* Flag: Expand macros in this line */
57 static int ExpandMacros = 1;
61 /*****************************************************************************/
63 /*****************************************************************************/
67 static int keepch (char c)
68 /* Put character c into translation buffer. */
75 static void keepstr (const char* S)
76 /* Put string str into translation buffer. */
85 static void Comment (void)
86 /* Remove a C comment from line. */
88 /* Remember the current line number, so we can output better error
89 * messages if the comment is not terminated in the current file.
91 unsigned StartingLine = GetCurrentLine();
93 /* Skip the start of comment chars */
97 /* Skip the comment */
98 while (CurC != '*' || NextC != '/') {
100 if (NextLine () == 0) {
101 PPError ("End-of-file reached in comment starting at line %u",
106 if (CurC == '/' && NextC == '*') {
107 PPWarning ("`/*' found inside a comment");
113 /* Skip the end of comment chars */
120 static void SkipBlank (void)
121 /* Skip blanks and tabs in the input stream. */
123 while (IsBlank (CurC)) {
130 static char* CopyQuotedString (char* Target)
131 /* Copy a single or double quoted string from the input to Target. Return the
132 * new target pointer. Target will not be terminated after the copy.
135 /* Remember the quote character, copy it to the target buffer and skip it */
140 /* Copy the characters inside the string */
141 while (CurC != '\0' && CurC != Quote) {
142 /* Keep an escaped char */
147 /* Copy the character */
152 /* If we had a terminating quote, copy it */
158 /* Return the new target pointer */
164 /*****************************************************************************/
166 /*****************************************************************************/
170 static int MacName (char* Ident)
171 /* Get macro symbol name. If error, print message and clear line. */
173 if (IsSym (Ident) == 0) {
174 PPError ("Identifier expected");
184 static void ExpandMacroArgs (Macro* M)
185 /* Preprocessor pass 2. Perform macro substitution. */
188 const char* Replacement;
191 /* Save the current line pointer and setup the new ones */
193 InitLine (M->Replacement);
195 /* Copy the macro replacement checking for parameters to replace */
196 while (CurC != '\0') {
197 /* If the next token is an identifier, check for a macro arg */
198 if (IsIdent (CurC)) {
200 Replacement = FindMacroArg (M, Ident);
202 /* Macro arg, keep the replacement */
203 keepstr (Replacement);
205 /* No macro argument, keep the original identifier */
208 } else if (CurC == '#' && IsIdent (NextC)) {
211 Replacement = FindMacroArg (M, Ident);
214 keepstr (Replacement);
220 } else if (IsQuoteChar (CurC)) {
221 mptr = CopyQuotedString (mptr);
228 /* Reset the line pointer */
234 static int MacroCall (Macro* M)
235 /* Process a function like macro */
237 unsigned ArgCount; /* Macro argument count */
238 unsigned ParCount; /* Number of open parenthesis */
239 char Buf[LINESIZE]; /* Argument buffer */
240 const char* ArgStart;
243 /* Expect an argument list */
246 PPError ("Illegal macro call");
250 /* Eat the left paren */
253 /* Read the actual macro arguments and store pointers to these arguments
254 * into the array of actual arguments in the macro definition.
262 /* Nested parenthesis */
266 } else if (IsQuoteChar (CurC)) {
267 B = CopyQuotedString (B);
268 } else if (CurC == ',' || CurC == ')') {
270 /* End of actual argument */
272 while (IsBlank(*ArgStart)) {
275 if (ArgCount < M->ArgCount) {
276 M->ActualArgs[ArgCount++] = ArgStart;
277 } else if (CurC != ')' || *ArgStart != '\0' || M->ArgCount > 0) {
278 /* Be sure not to count the single empty argument for a
279 * macro that does not have arguments.
284 /* Check for end of macro param list */
290 /* Start the next param */
294 /* Comma or right paren inside nested parenthesis */
301 } else if (IsBlank (CurC)) {
302 /* Squeeze runs of blanks */
305 } else if (CurC == '\0') {
306 /* End of line inside macro argument list - read next line */
307 if (NextLine () == 0) {
311 /* Just copy the character */
317 /* Compare formal argument count with actual */
318 if (M->ArgCount != ArgCount) {
319 PPError ("Macro argument count mismatch");
320 /* Be sure to make enough empty arguments available */
321 while (ArgCount < M->ArgCount) {
322 M->ActualArgs [ArgCount++] = "";
326 /* Preprocess the line, replacing macro parameters */
335 static void ExpandMacro (Macro* M)
338 /* Check if this is a function like macro */
339 if (M->ArgCount >= 0) {
340 /* Function like macro */
341 if (MacroCall (M) == 0) {
345 /* Just copy the replacement text */
346 keepstr (M->Replacement);
352 static void addmac (void)
353 /* Add a macro to the macro table. */
361 /* Read the macro name */
363 if (!MacName (Ident)) {
367 /* Get an existing macro definition with this name */
368 Existing = FindMacro (Ident);
370 /* Create a new macro definition */
371 M = NewMacro (Ident);
373 /* Check if this is a function like macro */
376 /* Skip the left paren */
379 /* Set the marker that this is a function like macro */
382 /* Read the formal parameter list */
387 if (MacName (Ident) == 0) {
390 AddMacroArg (M, Ident);
397 /* Check for a right paren and eat it if we find one */
399 PPError ("`)' expected");
406 /* Insert the macro into the macro table and allocate the ActualArgs array */
409 /* Remove whitespace and comments from the line, store the preprocessed
417 /* Create a copy of the replacement */
418 M->Replacement = xstrdup (Buf);
420 /* If we have an existing macro, check if the redefinition is identical.
421 * Print a diagnostic if not.
424 if (MacroCmp (M, Existing) != 0) {
425 PPError ("Macro redefinition is not identical");
432 /*****************************************************************************/
434 /*****************************************************************************/
438 static int Pass1 (const char* From, char* To)
439 /* Preprocessor pass 1. Remove whitespace and comments. */
445 /* Initialize reading from "From" */
451 /* Loop removing ws and comments */
453 while (CurC != '\0') {
454 if (IsBlank (CurC)) {
457 } else if (IsIdent (CurC)) {
459 if (Preprocessing && strcmp(Ident, "defined") == 0) {
460 /* Handle the "defined" operator */
468 if (!IsIdent (CurC)) {
469 PPError ("Identifier expected");
473 *mptr++ = IsMacro (Ident)? '1' : '0';
477 PPError ("`)' expected");
484 if (MaybeMacro (Ident[0])) {
489 } else if (IsQuoteChar (CurC)) {
490 mptr = CopyQuotedString (mptr);
491 } else if (CurC == '/' && NextC == '*') {
494 } else if (ANSI == 0 && CurC == '/' && NextC == '/') {
496 /* Beware: Because line continuation chars are handled when reading
497 * lines, we may only skip til the end of the source line, which
498 * may not be the same as the end of the input line. The end of the
499 * source line is denoted by a lf (\n) character.
503 } while (CurC != '\n' && CurC != '\0');
518 static int Pass2 (const char* From, char* To)
519 /* Preprocessor pass 2. Perform macro substitution. */
525 /* Initialize reading from "From" */
531 /* Loop substituting macros */
533 while (CurC != '\0') {
534 /* If we have an identifier, check if it's a macro */
535 if (IsIdent (CurC)) {
537 M = FindMacro (Ident);
544 } else if (IsQuoteChar(CurC)) {
545 mptr = CopyQuotedString (mptr);
556 static void xlateline (void)
557 /* Translate one line. */
562 Done = Pass1 (line, mline);
563 if (ExpandMacros == 0) {
565 ExpandMacros = 1; /* Reset to default */
569 /* Swap mline and line */
575 Done = Pass2 (line, mline);
579 /* Reinitialize line parsing */
585 static void doundef (void)
586 /* Process #undef directive */
591 if (MacName (Ident)) {
592 UndefineMacro (Ident);
598 static int setmflag (int skip, int flag, int cond)
599 /* setmflag( skip, flag, cond ) */
602 s_ifdef[++i_ifdef] = 3;
605 s_ifdef[++i_ifdef] = 6;
606 return (flag ^ cond);
612 static int doiff (int skip)
613 /* Process #if directive */
618 /* We're about to abuse the compiler expression parser to evaluate the
619 * #if expression. Save the current tokens to come back here later.
624 /* Remove the #if from the line and add two semicolons as sentinels */
627 while (CurC != '\0') {
635 /* Start over parsing from line */
638 /* Switch into special preprocessing mode */
641 /* Expand macros in this line */
644 /* Prime the token pump (remove old tokens from the stream) */
648 /* Call the expression parser */
651 /* End preprocessing mode */
654 /* Reset the old tokens */
658 /* Set the #if condition according to the expression result */
659 return (setmflag (skip, 1, lval.e_const != 0));
664 static int doifdef (int skip, int flag)
665 /* Process #ifdef if flag == 1, or #ifndef if flag == 0. */
670 if (MacName (Ident) == 0) {
673 return setmflag (skip, flag, IsMacro(Ident));
679 static void doinclude (void)
680 /* Open an include file. */
689 /* Get the next char and check for a valid file name terminator. Setup
690 * the include directory spec (SYS/USR) by looking at the terminator.
705 PPError ("`\"' or `<' expected");
710 /* Copy the filename into mline. Since mline has the same size as the
711 * input line, we don't need to check for an overflow here.
714 while (CurC != '\0' && CurC != RTerm) {
720 /* Check if we got a terminator */
722 /* No terminator found */
723 PPError ("Missing terminator or file name too long");
727 /* Open the include file */
728 OpenIncludeFile (mline, DirSpec);
731 /* Clear the remaining line so the next input will come from the new
739 static void doerror (void)
744 PPError ("Invalid #error directive");
746 PPError ("#error: %s", lptr);
749 /* clear rest of line */
755 /* C preprocessor. */
757 /* stuff used to bum the keyword dispatching stuff */
773 static const struct tok_elt pre_toks[] = {
774 { "define", PP_DEFINE },
776 { "endif", PP_ENDIF },
777 { "error", PP_ERROR },
779 { "ifdef", PP_IFDEF },
780 { "ifndef", PP_IFNDEF },
781 { "include", PP_INCLUDE },
783 { "pragma", PP_PRAGMA },
784 { "undef", PP_UNDEF },
790 static int searchtok (const char *sym, const struct tok_elt *toks)
791 /* Search a token in a table */
793 while (toks->toknam && strcmp (toks->toknam, sym))
795 return (toks->toknbr);
800 void Preprocess (void)
801 /* Preprocess a line */
806 /* Skip white space at the beginning of the line */
809 /* Check for stuff to skip */
811 while (CurC == '\0' || CurC == '#' || Skip) {
813 /* Check for preprocessor lines lines */
818 /* Ignore the empty preprocessor directive */
821 if (!IsSym (Directive)) {
822 PPError ("Preprocessor directive expected");
825 switch (searchtok (Directive, pre_toks)) {
834 if (s_ifdef[i_ifdef] & 2) {
835 if (s_ifdef[i_ifdef] & 4) {
838 s_ifdef[i_ifdef] ^= 2;
840 PPError ("Unexpected `#else'");
846 Skip = s_ifdef[i_ifdef--] & 1;
848 PPError ("Unexpected `#endif'");
863 Skip = doifdef (Skip, 1);
867 Skip = doifdef (Skip, 0);
877 /* Not allowed in strict ANSI mode */
879 PPError ("Preprocessor directive expected");
886 /* Don't expand macros in this line */
888 /* #pragma is handled on the scanner level */
900 PPError ("Preprocessor directive expected");
906 if (NextLine () == 0) {
908 PPError ("`#endif' expected");
918 printf ("line: %s\n", line);