static void DoInclude (void) /* Open an include file. */ { char RTerm; InputType IT; StrBuf Filename = STATIC_STRBUF_INITIALIZER; /* Preprocess the remainder of the line */ PreprocessLine (); /* Skip blanks */ SkipWhitespace (0); /* Get the next char and check for a valid file name terminator. Setup * the include directory spec (SYS/USR) by looking at the terminator. */ switch (CurC) { case '\"': RTerm = '\"'; IT = IT_USRINC; break; case '<': RTerm = '>'; IT = IT_SYSINC; break; default: PPError ("`\"' or `<' expected"); goto Done; } NextChar (); /* Get a copy of the filename */ while (CurC != '\0' && CurC != RTerm) { SB_AppendChar (&Filename, CurC); NextChar (); } SB_Terminate (&Filename); /* Check if we got a terminator */ if (CurC == RTerm) { /* Open the include file */ OpenIncludeFile (SB_GetConstBuf (&Filename), IT); } else if (CurC == '\0') { /* No terminator found */ PPError ("#include expects \"FILENAME\" or <FILENAME>"); } Done: /* Free the allocated filename data */ SB_Done (&Filename); /* Clear the remaining line so the next input will come from the new * file (if open) */ ClearLine (); }
static void DoError (void) /* Print an error */ { SkipWhitespace (0); if (CurC == '\0') { PPError ("Invalid #error directive"); } else { PPError ("#error: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); } /* Clear the rest of line */ ClearLine (); }
static void OldStyleComment (void) /* Remove an old style C comment from line. */ { /* Remember the current line number, so we can output better error * messages if the comment is not terminated in the current file. */ unsigned StartingLine = GetCurrentLine(); /* Skip the start of comment chars */ NextChar (); NextChar (); /* Skip the comment */ while (CurC != '*' || NextC != '/') { if (CurC == '\0') { if (NextLine () == 0) { PPError ("End-of-file reached in comment starting at line %u", StartingLine); return; } } else { if (CurC == '/' && NextC == '*') { PPWarning ("`/*' found inside a comment"); } NextChar (); } } /* Skip the end of comment chars */ NextChar (); NextChar (); }
void OpenIncludeFile (const char* Name, InputType IT) /* Open an include file and insert it into the tables. */ { char* N; FILE* F; IFile* IF; /* Check for the maximum include nesting */ if (CollCount (&AFiles) > MAX_INC_NESTING) { PPError ("Include nesting too deep"); return; } /* Search for the file */ N = SearchFile ((IT == IT_SYSINC)? SysIncSearchPath : UsrIncSearchPath, Name); if (N == 0) { PPError ("Include file `%s' not found", Name); return; } /* Search the list of all input files for this file. If we don't find * it, create a new IFile object. */ IF = FindFile (N); if (IF == 0) { IF = NewIFile (N, IT); } /* We don't need N any longer, since we may now use IF->Name */ xfree (N); /* Open the file */ F = fopen (IF->Name, "r"); if (F == 0) { /* Error opening the file */ PPError ("Cannot open include file `%s': %s", IF->Name, strerror (errno)); return; } /* Debugging output */ Print (stdout, 1, "Opened include file `%s'\n", IF->Name); /* Allocate a new AFile structure */ (void) NewAFile (IF, F); }
static int MacName (char* Ident) /* Get a macro symbol name into Ident. If we have an error, print a * diagnostic message and clear the line. */ { if (IsSym (Ident) == 0) { PPError ("Identifier expected"); ClearLine (); return 0; } else { return 1; } }
static void DoWarning (void) /* Print a warning */ { SkipWhitespace (0); if (CurC == '\0') { PPError ("Invalid #warning directive"); } else { PPWarning ("#warning: %s", SB_GetConstBuf (Line) + SB_GetIndex (Line)); } /* Clear the rest of line */ ClearLine (); }
static int PushIf (int Skip, int Invert, int Cond) /* Push a new if level onto the if stack */ { /* Check for an overflow of the if stack */ if (IfIndex >= MAX_IFS-1) { PPError ("Too many nested #if clauses"); return 1; } /* Push the #if condition */ ++IfIndex; if (Skip) { IfStack[IfIndex] = IFCOND_SKIP | IFCOND_NEEDTERM; return 1; } else { IfStack[IfIndex] = IFCOND_NONE | IFCOND_NEEDTERM; return (Invert ^ Cond); } }
static void MacroCall (StrBuf* Target, Macro* M) /* Process a function like macro */ { MacroExp E; /* Eat the left paren */ NextChar (); /* Initialize our MacroExp structure */ InitMacroExp (&E, M); /* Read the actual macro arguments */ ReadMacroArgs (&E); /* Compare formal and actual argument count */ if (CollCount (&E.ActualArgs) != (unsigned) M->ArgCount) { StrBuf Arg = STATIC_STRBUF_INITIALIZER; /* Argument count mismatch */ PPError ("Macro argument count mismatch"); /* Be sure to make enough empty arguments available */ while (CollCount (&E.ActualArgs) < (unsigned) M->ArgCount) { ME_AppendActual (&E, &Arg); } } /* Replace macro arguments handling the # and ## operators */ MacroArgSubst (&E); /* Do macro replacement on the macro that already has the parameters * substituted. */ M->Expanding = 1; MacroReplacement (&E.Replacement, Target); M->Expanding = 0; /* Free memory allocated for the macro expansion structure */ DoneMacroExp (&E); }
static unsigned Pass1 (StrBuf* Source, StrBuf* Target) /* Preprocessor pass 1. Remove whitespace. Handle old and new style comments * and the "defined" operator. */ { unsigned IdentCount; ident Ident; int HaveParen; /* Switch to the new input source */ StrBuf* OldSource = InitLine (Source); /* Loop removing ws and comments */ IdentCount = 0; while (CurC != '\0') { if (SkipWhitespace (0)) { /* Squeeze runs of blanks */ if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } } else if (IsSym (Ident)) { if (Preprocessing && strcmp (Ident, "defined") == 0) { /* Handle the "defined" operator */ SkipWhitespace (0); HaveParen = 0; if (CurC == '(') { HaveParen = 1; NextChar (); SkipWhitespace (0); } if (IsSym (Ident)) { SB_AppendChar (Target, IsMacro (Ident)? '1' : '0'); if (HaveParen) { SkipWhitespace (0); if (CurC != ')') { PPError ("`)' expected"); } else { NextChar (); } } } else { PPError ("Identifier expected"); SB_AppendChar (Target, '0'); } } else { ++IdentCount; SB_AppendStr (Target, Ident); } } else if (IsQuote (CurC)) { CopyQuotedString (Target); } else if (CurC == '/' && NextC == '*') { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { if (!IsSpace (SB_LookAtLast (Target))) { SB_AppendChar (Target, ' '); } NewStyleComment (); } else { SB_AppendChar (Target, CurC); NextChar (); } } /* Switch back to the old source */ InitLine (OldSource); /* Return the number of identifiers found in the line */ return IdentCount; }
static void DefineMacro (void) /* Handle a macro definition. */ { ident Ident; Macro* M; Macro* Existing; int C89; /* Read the macro name */ SkipWhitespace (0); if (!MacName (Ident)) { return; } /* Remember if we're in C89 mode */ C89 = (IS_Get (&Standard) == STD_C89); /* Get an existing macro definition with this name */ Existing = FindMacro (Ident); /* Create a new macro definition */ M = NewMacro (Ident); /* Check if this is a function like macro */ if (CurC == '(') { /* Skip the left paren */ NextChar (); /* Set the marker that this is a function like macro */ M->ArgCount = 0; /* Read the formal parameter list */ while (1) { /* Skip white space and check for end of parameter list */ SkipWhitespace (0); if (CurC == ')') { break; } /* The next token must be either an identifier, or - if not in * C89 mode - the ellipsis. */ if (!C89 && CurC == '.') { /* Ellipsis */ NextChar (); if (CurC != '.' || NextC != '.') { PPError ("`...' expected"); ClearLine (); return; } NextChar (); NextChar (); /* Remember that the macro is variadic and use __VA_ARGS__ as * the argument name. */ AddMacroArg (M, "__VA_ARGS__"); M->Variadic = 1; } else { /* Must be macro argument name */ if (MacName (Ident) == 0) { return; } /* __VA_ARGS__ is only allowed in C89 mode */ if (!C89 && strcmp (Ident, "__VA_ARGS__") == 0) { PPWarning ("`__VA_ARGS__' can only appear in the expansion " "of a C99 variadic macro"); } /* Add the macro argument */ AddMacroArg (M, Ident); } /* If we had an ellipsis, or the next char is not a comma, we've * reached the end of the macro argument list. */ SkipWhitespace (0); if (M->Variadic || CurC != ',') { break; } NextChar (); } /* Check for a right paren and eat it if we find one */ if (CurC != ')') { PPError ("`)' expected"); ClearLine (); return; } NextChar (); } /* Skip whitespace before the macro replacement */ SkipWhitespace (0); /* Insert the macro into the macro table and allocate the ActualArgs array */ InsertMacro (M); /* Remove whitespace and comments from the line, store the preprocessed * line into the macro replacement buffer. */ Pass1 (Line, &M->Replacement); /* Remove whitespace from the end of the line */ while (IsSpace (SB_LookAtLast (&M->Replacement))) { SB_Drop (&M->Replacement, 1); } #if 0 printf ("%s: <%.*s>\n", M->Name, SB_GetLen (&M->Replacement), SB_GetConstBuf (&M->Replacement)); #endif /* If we have an existing macro, check if the redefinition is identical. * Print a diagnostic if not. */ if (Existing && MacroCmp (M, Existing) != 0) { PPError ("Macro redefinition is not identical"); } }
static void MacroArgSubst (MacroExp* E) /* Argument substitution according to ISO/IEC 9899:1999 (E), 6.10.3.1ff */ { ident Ident; int ArgIdx; StrBuf* OldSource; StrBuf* Arg; int HaveSpace; /* Remember the current input and switch to the macro replacement. */ int OldIndex = SB_GetIndex (&E->M->Replacement); SB_Reset (&E->M->Replacement); OldSource = InitLine (&E->M->Replacement); /* Argument handling loop */ while (CurC != '\0') { /* If we have an identifier, check if it's a macro */ if (IsSym (Ident)) { /* Check if it's a macro argument */ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { /* A macro argument. Get the corresponding actual argument. */ Arg = ME_GetActual (E, ArgIdx); /* Copy any following whitespace */ HaveSpace = SkipWhitespace (0); /* If a ## operator follows, we have to insert the actual * argument as is, otherwise it must be macro replaced. */ if (CurC == '#' && NextC == '#') { /* ### Add placemarker if necessary */ SB_Append (&E->Replacement, Arg); } else { /* Replace the formal argument by a macro replaced copy * of the actual. */ SB_Reset (Arg); MacroReplacement (Arg, &E->Replacement); /* If we skipped whitespace before, re-add it now */ if (HaveSpace) { SB_AppendChar (&E->Replacement, ' '); } } } else { /* An identifier, keep it */ SB_AppendStr (&E->Replacement, Ident); } } else if (CurC == '#' && NextC == '#') { /* ## operator. */ NextChar (); NextChar (); SkipWhitespace (0); /* Since we need to concatenate the token sequences, remove * any whitespace that was added to target, since it must come * from the input. */ while (IsSpace (SB_LookAtLast (&E->Replacement))) { SB_Drop (&E->Replacement, 1); } /* If the next token is an identifier which is a macro argument, * replace it, otherwise do nothing. */ if (IsSym (Ident)) { /* Check if it's a macro argument */ if ((ArgIdx = FindMacroArg (E->M, Ident)) >= 0) { /* Get the corresponding actual argument and add it. */ SB_Append (&E->Replacement, ME_GetActual (E, ArgIdx)); } else { /* Just an ordinary identifier - add as is */ SB_AppendStr (&E->Replacement, Ident); } } } else if (CurC == '#' && E->M->ArgCount >= 0) { /* A # operator within a macro expansion of a function like * macro. Read the following identifier and check if it's a * macro parameter. */ NextChar (); SkipWhitespace (0); if (!IsSym (Ident) || (ArgIdx = FindMacroArg (E->M, Ident)) < 0) { PPError ("`#' is not followed by a macro parameter"); } else { /* Make a valid string from Replacement */ Arg = ME_GetActual (E, ArgIdx); SB_Reset (Arg); Stringize (Arg, &E->Replacement); } } else if (IsQuote (CurC)) { CopyQuotedString (&E->Replacement); } else { SB_AppendChar (&E->Replacement, CurC); NextChar (); } } #if 0 /* Remove whitespace from the end of the line */ while (IsSpace (SB_LookAtLast (&E->Replacement))) { SB_Drop (&E->Replacement, 1); } #endif /* Switch back the input */ InitLine (OldSource); SB_SetIndex (&E->M->Replacement, OldIndex); }
static void ReadMacroArgs (MacroExp* E) /* Identify the arguments to a macro call */ { unsigned Parens; /* Number of open parenthesis */ StrBuf Arg = STATIC_STRBUF_INITIALIZER; /* Read the actual macro arguments */ Parens = 0; while (1) { if (CurC == '(') { /* Nested parenthesis */ SB_AppendChar (&Arg, CurC); NextChar (); ++Parens; } else if (IsQuote (CurC)) { /* Quoted string - just copy */ CopyQuotedString (&Arg); } else if (CurC == ',' || CurC == ')') { if (Parens) { /* Comma or right paren inside nested parenthesis */ if (CurC == ')') { --Parens; } SB_AppendChar (&Arg, CurC); NextChar (); } else if (CurC == ',' && ME_ArgIsVariadic (E)) { /* It's a comma, but we're inside a variadic macro argument, so * just copy it and proceed. */ SB_AppendChar (&Arg, CurC); NextChar (); } else { /* End of actual argument. Remove whitespace from the end. */ while (IsSpace (SB_LookAtLast (&Arg))) { SB_Drop (&Arg, 1); } /* If this is not the single empty argument for a macro with * an empty argument list, remember it. */ if (CurC != ')' || SB_NotEmpty (&Arg) || E->M->ArgCount > 0) { ME_AppendActual (E, &Arg); } /* Check for end of macro param list */ if (CurC == ')') { NextChar (); break; } /* Start the next param */ NextChar (); SB_Clear (&Arg); } } else if (SkipWhitespace (1)) { /* Squeeze runs of blanks within an arg */ if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } } else if (CurC == '/' && NextC == '*') { if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } OldStyleComment (); } else if (IS_Get (&Standard) >= STD_C99 && CurC == '/' && NextC == '/') { if (SB_NotEmpty (&Arg)) { SB_AppendChar (&Arg, ' '); } NewStyleComment (); } else if (CurC == '\0') { /* End of input inside macro argument list */ PPError ("Unterminated argument list invoking macro `%s'", E->M->Name); ClearLine (); break; } else { /* Just copy the character */ SB_AppendChar (&Arg, CurC); NextChar (); } } /* Deallocate string buf resources */ SB_Done (&Arg); }
void Preprocess (void) /* Preprocess a line */ { int Skip; ident Directive; /* Create the output buffer if we don't already have one */ if (MLine == 0) { MLine = NewStrBuf (); } /* Skip white space at the beginning of the line */ SkipWhitespace (0); /* Check for stuff to skip */ Skip = 0; while (CurC == '\0' || CurC == '#' || Skip) { /* Check for preprocessor lines lines */ if (CurC == '#') { NextChar (); SkipWhitespace (0); if (CurC == '\0') { /* Ignore the empty preprocessor directive */ continue; } if (!IsSym (Directive)) { PPError ("Preprocessor directive expected"); ClearLine (); } else { switch (FindPPToken (Directive)) { case PP_DEFINE: if (!Skip) { DefineMacro (); } break; case PP_ELIF: if (IfIndex >= 0) { if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { /* Handle as #else/#if combination */ if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { Skip = !Skip; } IfStack[IfIndex] |= IFCOND_ELSE; Skip = DoIf (Skip); /* #elif doesn't need a terminator */ IfStack[IfIndex] &= ~IFCOND_NEEDTERM; } else { PPError ("Duplicate #else/#elif"); } } else { PPError ("Unexpected #elif"); } break; case PP_ELSE: if (IfIndex >= 0) { if ((IfStack[IfIndex] & IFCOND_ELSE) == 0) { if ((IfStack[IfIndex] & IFCOND_SKIP) == 0) { Skip = !Skip; } IfStack[IfIndex] |= IFCOND_ELSE; } else { PPError ("Duplicate #else"); } } else { PPError ("Unexpected `#else'"); } break; case PP_ENDIF: if (IfIndex >= 0) { /* Remove any clauses on top of stack that do not * need a terminating #endif. */ while (IfIndex >= 0 && (IfStack[IfIndex] & IFCOND_NEEDTERM) == 0) { --IfIndex; } /* Stack may not be empty here or something is wrong */ CHECK (IfIndex >= 0); /* Remove the clause that needs a terminator */ Skip = (IfStack[IfIndex--] & IFCOND_SKIP) != 0; } else { PPError ("Unexpected `#endif'"); } break; case PP_ERROR: if (!Skip) { DoError (); } break; case PP_IF: Skip = DoIf (Skip); break; case PP_IFDEF: Skip = DoIfDef (Skip, 1); break; case PP_IFNDEF: Skip = DoIfDef (Skip, 0); break; case PP_INCLUDE: if (!Skip) { DoInclude (); } break; case PP_LINE: /* Should do something in C99 at least, but we ignore it */ if (!Skip) { ClearLine (); } break; case PP_PRAGMA: if (!Skip) { DoPragma (); goto Done; } break; case PP_UNDEF: if (!Skip) { DoUndef (); } break; case PP_WARNING: /* #warning is a non standard extension */ if (IS_Get (&Standard) > STD_C99) { if (!Skip) { DoWarning (); } } else { if (!Skip) { PPError ("Preprocessor directive expected"); } ClearLine (); } break; default: if (!Skip) { PPError ("Preprocessor directive expected"); } ClearLine (); } } } if (NextLine () == 0) { if (IfIndex >= 0) { PPError ("`#endif' expected"); } return; } SkipWhitespace (0); } PreprocessLine (); Done: if (Verbosity > 1 && SB_NotEmpty (Line)) { printf ("%s(%u): %.*s\n", GetCurrentFile (), GetCurrentLine (), (int) SB_GetLen (Line), SB_GetConstBuf (Line)); } }