diff --git a/src/cc65/input.c b/src/cc65/input.c index fcf7f32f3..4faf2aa87 100644 --- a/src/cc65/input.c +++ b/src/cc65/input.c @@ -40,6 +40,7 @@ /* common */ #include "check.h" #include "coll.h" +#include "debugflag.h" #include "filestat.h" #include "fname.h" #include "print.h" @@ -77,17 +78,6 @@ char NextC = '\0'; /* Maximum count of nested includes */ #define MAX_INC_NESTING 16 -/* Struct that describes an input file */ -typedef struct IFile IFile; -struct IFile { - unsigned Index; /* File index */ - unsigned Usage; /* Usage counter */ - unsigned long Size; /* File size */ - unsigned long MTime; /* Time of last modification */ - InputType Type; /* Type of input file */ - char Name[1]; /* Name of file (dynamically allocated) */ -}; - /* Struct that describes an active input file */ typedef struct AFile AFile; struct AFile { @@ -132,11 +122,13 @@ static IFile* NewIFile (const char* Name, InputType Type) IFile* IF = (IFile*) xmalloc (sizeof (IFile) + Len); /* Initialize the fields */ - IF->Index = CollCount (&IFiles) + 1; - IF->Usage = 0; - IF->Size = 0; - IF->MTime = 0; - IF->Type = Type; + IF->Index = CollCount (&IFiles) + 1; + IF->Usage = 0; + IF->Size = 0; + IF->MTime = 0; + IF->Type = Type; + IF->GFlags = IG_NONE; + SB_Init (&IF->GuardMacro); memcpy (IF->Name, Name, Len+1); /* Insert the new structure into the IFile collection */ @@ -280,7 +272,7 @@ void OpenMainFile (const char* Name) SetPPIfStack (&MainFile->IfStack); /* Begin PP for this file */ - PreprocessBegin (); + PreprocessBegin (IF); /* Allocate the input line buffer */ Line = NewStrBuf (); @@ -318,11 +310,21 @@ void OpenIncludeFile (const char* Name, InputType IT) } /* Search the list of all input files for this file. If we don't find - ** it, create a new IFile object. + ** it, create a new IFile object. If we do already know the file and it + ** has an include guard, check for the include guard before opening the + ** file. */ IF = FindFile (N); if (IF == 0) { IF = NewIFile (N, IT); + } else if ((IF->GFlags & IG_ISGUARDED) != 0 && + IsMacro (SB_GetConstBuf (&IF->GuardMacro))) { + if (Debug) { + printf ("Include guard %s found for \"%s\" - won't include it again\n", + SB_GetConstBuf (&IF->GuardMacro), + Name); + } + return; } /* We don't need N any longer, since we may now use IF->Name */ @@ -346,7 +348,7 @@ void OpenIncludeFile (const char* Name, InputType IT) SetPPIfStack (&AF->IfStack); /* Begin PP for this file */ - PreprocessBegin (); + PreprocessBegin (IF); } @@ -356,27 +358,24 @@ void CloseIncludeFile (void) ** NULL if this was the main file. */ { - AFile* Input; + /* Get the currently active input file and remove it from set of active + ** files. CollPop will FAIL if the collection is empty so no need to + ** check this here. + */ + AFile* Input = CollPop (&AFiles); - /* Get the number of active input files */ - unsigned AFileCount = CollCount (&AFiles); + /* Determine the file that is active after closing this one. We never + ** actually close the main file, since it is needed for errors found after + ** compilation is completed. + */ + AFile* NextInput = (CollCount (&AFiles) > 0)? CollLast (&AFiles) : Input; - /* Must have an input file when called */ - PRECONDITION (AFileCount > 0); - - /* End preprocessor in this file */ - PreprocessEnd (); - - /* Get the current active input file */ - Input = CollLast (&AFiles); + /* End preprocessing for the current input file */ + PreprocessEnd (NextInput->Input); /* Close the current input file (we're just reading so no error check) */ fclose (Input->F); - /* Delete the last active file from the active file collection */ - --AFileCount; - CollDelete (&AFiles, AFileCount); - /* If we had added an extra search path for this AFile, remove it */ if (Input->SearchPath) { PopSearchPath (UsrIncSearchPath); @@ -385,10 +384,9 @@ void CloseIncludeFile (void) /* Delete the active file structure */ FreeAFile (Input); - /* Use previous file with PP if it is not the main file */ - if (AFileCount > 0) { - Input = CollLast (&AFiles); - SetPPIfStack (&Input->IfStack); + /* If we've switched files, use the if stack from the previous file */ + if (Input != NextInput) { + SetPPIfStack (&NextInput->IfStack); } } diff --git a/src/cc65/input.h b/src/cc65/input.h index 9a5a76949..5d7d051e8 100644 --- a/src/cc65/input.h +++ b/src/cc65/input.h @@ -65,6 +65,28 @@ typedef enum { IT_USRINC = 0x04, /* User include file (using "") */ } InputType; +/* A bitmapped set of flags for include guard processing in the preprocessor */ +typedef enum { + IG_NONE = 0x00, + IG_NEWFILE = 0x01, /* File processing started */ + IG_ISGUARDED = 0x02, /* File contains an include guard */ + IG_GUARDCLOSED = 0x04, /* Include guard was closed */ + IG_COMPLETE = IG_ISGUARDED | IG_GUARDCLOSED, +} GuardFlags; + +/* Struct that describes an input file */ +typedef struct IFile IFile; +struct IFile { + unsigned Index; /* File index */ + unsigned Usage; /* Usage counter */ + unsigned long Size; /* File size */ + unsigned long MTime; /* Time of last modification */ + InputType Type; /* Type of input file */ + GuardFlags GFlags; /* Flags for include guard processing */ + StrBuf GuardMacro; /* Include guard macro name */ + char Name[1]; /* Name of file (dynamically allocated) */ +}; + /* The current input line */ extern StrBuf* Line; diff --git a/src/cc65/preproc.c b/src/cc65/preproc.c index 65db5ea57..bc21a69d1 100644 --- a/src/cc65/preproc.c +++ b/src/cc65/preproc.c @@ -93,6 +93,10 @@ #define IFCOND_SKIP 0x01U #define IFCOND_ELSE 0x02U #define IFCOND_NEEDTERM 0x04U +#define IFCOND_ISGUARD 0x08U + +/* Current input file */ +static IFile* CurInput = 0; /* Current PP if stack */ static PPIfStack* PPStack; @@ -2701,7 +2705,7 @@ Error_Handler: -static int PushIf (int Skip, int Invert, int Cond) +static int PushIf (int Skip, int Invert, int Cond, unsigned Flags) /* Push a new if level onto the if stack */ { /* Check for an overflow of the if stack */ @@ -2713,10 +2717,10 @@ static int PushIf (int Skip, int Invert, int Cond) /* Push the #if condition */ ++PPStack->Index; if (Skip) { - PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM | Flags; return 1; } else { - PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM; + PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM | Flags; return (Invert ^ Cond); } } @@ -2794,29 +2798,40 @@ static int DoIf (int Skip) } /* Set the #if condition according to the expression result */ - return PushIf (Skip, 1, Expr.IVal != 0); + return PushIf (Skip, 1, Expr.IVal != 0, IFCOND_NONE); } -static int DoIfDef (int skip, int flag) -/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ +static int DoIfDef (int Skip, int Flag) +/* Process #ifdef if Flag == 1, or #ifndef if Flag == 0. */ { - int Value = 0; + int IsDef = 0; + unsigned GuardFlag = IFCOND_NONE; - if (!skip) { + if (!Skip) { ident Ident; SkipWhitespace (0); if (MacName (Ident)) { CheckForBadIdent (Ident, IS_Get (&Standard), 0); - Value = IsMacro (Ident); + IsDef = IsMacro (Ident); /* Check for extra tokens */ - CheckExtraTokens (flag ? "ifdef" : "ifndef"); + CheckExtraTokens (Flag ? "ifdef" : "ifndef"); + /* Check if this is an include guard. This is the case if we have + ** a #ifndef directive at the start of a new file and the macro + ** is not defined. If so, remember it. + */ + if (Flag == 0 && (CurInput->GFlags & IG_NEWFILE) != 0 && !IsDef) { + CurInput->GFlags |= IG_ISGUARDED; + SB_CopyStr (&CurInput->GuardMacro, Ident); + SB_Terminate (&CurInput->GuardMacro); + GuardFlag = IFCOND_ISGUARD; + } } } - return PushIf (skip, flag, Value); + return PushIf (Skip, Flag, IsDef, GuardFlag); } @@ -3070,18 +3085,31 @@ static int ParseDirectives (unsigned ModeFlags) int PPSkip = 0; ident Directive; - /* Skip white space at the beginning of the first line */ + /* Skip white space at the beginning of the line */ int Whitespace = SkipWhitespace (0); /* Check for stuff to skip */ while (CurC == '\0' || CurC == '#' || PPSkip) { + /* If a #ifndef that was assumed to be an include guard was closed + ** recently, we may not have anything else following in the file + ** besides whitespace otherwise the assumption was false and we don't + ** actually have an include guard. + */ + if (CurC != '\0' && (CurInput->GFlags & IG_COMPLETE) == IG_COMPLETE) { + CurInput->GFlags &= ~IG_ISGUARDED; + } + /* Check for preprocessor lines lines */ if (CurC == '#') { + unsigned IfCond; + /* Skip the hash and following white space */ NextChar (); SkipWhitespace (0); + if (CurC == '\0') { /* Ignore the empty preprocessor directive */ + CurInput->GFlags &= ~IG_NEWFILE; continue; } if (!IsSym (Directive)) { @@ -3089,145 +3117,175 @@ static int ParseDirectives (unsigned ModeFlags) PPError ("Preprocessor directive expected"); } ClearLine (); - } else { - switch (FindPPDirectiveType (Directive)) { + CurInput->GFlags &= ~IG_NEWFILE; + continue; + } + switch (FindPPDirectiveType (Directive)) { - case PPD_DEFINE: - if (!PPSkip) { - DoDefine (); - } - break; + case PPD_DEFINE: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + DoDefine (); + } + break; - case PPD_ELIF: - if (PPStack->Index >= 0) { - if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { - /* Handle as #else/#if combination */ - if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { - PPSkip = !PPSkip; - } - PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; - PPSkip = DoIf (PPSkip); + case PPD_ELIF: + CurInput->GFlags &= ~IG_NEWFILE; + if (PPStack->Index >= 0) { + unsigned char PPCond = PPStack->Stack[PPStack->Index]; + if ((PPCond & IFCOND_ELSE) == 0) { + /* Handle as #else/#if combination */ + if ((PPCond & IFCOND_SKIP) == 0) { + PPSkip = !PPSkip; + } + PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; + PPSkip = DoIf (PPSkip); - /* #elif doesn't need a terminator */ - PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM; - } else { - PPError ("Duplicate #else/#elif"); + /* #elif doesn't need a terminator */ + PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM; + + /* An include guard cannot have a #elif */ + if (PPCond & IFCOND_ISGUARD) { + CurInput->GFlags &= ~IG_ISGUARDED; } } else { - PPError ("Unexpected #elif"); + PPError ("Duplicate #else/#elif"); } - break; + } else { + PPError ("Unexpected #elif"); + } + break; - case PPD_ELSE: - if (PPStack->Index >= 0) { - if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { - if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { - PPSkip = !PPSkip; - } - PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; - - /* Check for extra tokens */ - CheckExtraTokens ("else"); - } else { - PPError ("Duplicate #else"); + case PPD_ELSE: + CurInput->GFlags &= ~IG_NEWFILE; + if (PPStack->Index >= 0) { + unsigned char PPCond = PPStack->Stack[PPStack->Index]; + if ((PPCond & IFCOND_ELSE) == 0) { + if ((PPCond & IFCOND_SKIP) == 0) { + PPSkip = !PPSkip; } - } else { - PPError ("Unexpected '#else'"); - } - break; + PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; - case PPD_ENDIF: - if (PPStack->Index >= 0) { - /* Remove any clauses on top of stack that do not - ** need a terminating #endif. - */ - while (PPStack->Index >= 0 && - (PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) == 0) { - --PPStack->Index; + /* An include guard cannot have a #else */ + if (PPCond & IFCOND_ISGUARD) { + CurInput->GFlags &= ~IG_ISGUARDED; } - /* Stack may not be empty here or something is wrong */ - CHECK (PPStack->Index >= 0); - - /* Remove the clause that needs a terminator */ - PPSkip = (PPStack->Stack[PPStack->Index--] & IFCOND_SKIP) != 0; - /* Check for extra tokens */ - CheckExtraTokens ("endif"); + CheckExtraTokens ("else"); } else { - PPError ("Unexpected '#endif'"); + PPError ("Duplicate #else"); } - break; + } else { + PPError ("Unexpected '#else'"); + } + break; - case PPD_ERROR: - if (!PPSkip) { - DoError (); + case PPD_ENDIF: + CurInput->GFlags &= ~IG_NEWFILE; + if (PPStack->Index >= 0) { + /* Remove any clauses on top of stack that do not + ** need a terminating #endif. + */ + while (PPStack->Index >= 0 && + (PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) == 0) { + --PPStack->Index; } - break; - case PPD_IF: - PPSkip = DoIf (PPSkip); - break; + /* Stack may not be empty here or something is wrong */ + CHECK (PPStack->Index >= 0); - case PPD_IFDEF: - PPSkip = DoIfDef (PPSkip, 1); - break; - - case PPD_IFNDEF: - PPSkip = DoIfDef (PPSkip, 0); - break; - - case PPD_INCLUDE: - if (!PPSkip) { - DoInclude (); + /* Remove the clause that needs a terminator */ + IfCond = PPStack->Stack[PPStack->Index--]; + PPSkip = (IfCond & IFCOND_SKIP) != 0; + if (IfCond & IFCOND_ISGUARD) { + CurInput->GFlags |= IG_GUARDCLOSED; } - break; - case PPD_LINE: - if (!PPSkip) { - DoLine (); - } - break; + /* Check for extra tokens */ + CheckExtraTokens ("endif"); + } else { + PPError ("Unexpected '#endif'"); + } + break; - case PPD_PRAGMA: - if (!PPSkip) { - if ((ModeFlags & MSM_IN_ARG_LIST) == 0) { - DoPragma (); - return Whitespace; - } else { - PPError ("Embedded #pragma directive within macro arguments is unsupported"); - } - } - break; + case PPD_ERROR: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + DoError (); + } + break; - case PPD_UNDEF: - if (!PPSkip) { - DoUndef (); - } - break; + case PPD_IF: + CurInput->GFlags &= ~IG_NEWFILE; + PPSkip = DoIf (PPSkip); + break; - case PPD_WARNING: - /* #warning is a non standard extension */ - if (IS_Get (&Standard) > STD_C99) { - if (!PPSkip) { - DoWarning (); - } + case PPD_IFDEF: + CurInput->GFlags &= ~IG_NEWFILE; + PPSkip = DoIfDef (PPSkip, 1); + break; + + case PPD_IFNDEF: + PPSkip = DoIfDef (PPSkip, 0); + CurInput->GFlags &= ~IG_NEWFILE; + break; + + case PPD_INCLUDE: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + DoInclude (); + } + break; + + case PPD_LINE: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + DoLine (); + } + break; + + case PPD_PRAGMA: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + if ((ModeFlags & MSM_IN_ARG_LIST) == 0) { + DoPragma (); + return Whitespace; } else { - if (!PPSkip) { - PPError ("Preprocessor directive expected"); - } - ClearLine (); + PPError ("Embedded #pragma directive within macro arguments is unsupported"); } - break; + } + break; - default: + case PPD_UNDEF: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + DoUndef (); + } + break; + + case PPD_WARNING: + CurInput->GFlags &= ~IG_NEWFILE; + /* #warning is a non standard extension */ + if (IS_Get (&Standard) > STD_C99) { + if (!PPSkip) { + DoWarning (); + } + } else { if (!PPSkip) { PPError ("Preprocessor directive expected"); } ClearLine (); - } - } + } + break; + default: + CurInput->GFlags &= ~IG_NEWFILE; + if (!PPSkip) { + PPError ("Preprocessor directive expected"); + } + ClearLine (); + } } if (NextLine () == 0) { break; @@ -3236,6 +3294,15 @@ static int ParseDirectives (unsigned ModeFlags) Whitespace = SkipWhitespace (0) || Whitespace; } + /* If a #ifndef that was assumed to be an include guard was closed + ** recently, we may not have anything else following in the file + ** besides whitespace otherwise the assumption was false and we don't + ** actually have an include guard. + */ + if (CurC != '\0' && (CurInput->GFlags & IG_COMPLETE) == IG_COMPLETE) { + CurInput->GFlags &= ~IG_ISGUARDED; + } + return Whitespace; } @@ -3346,6 +3413,7 @@ void Preprocess (void) OLine = PLine; ParseDirectives (MSM_MULTILINE); OLine = 0; + CurInput->GFlags &= ~IG_NEWFILE; /* Add the source info to preprocessor output if needed */ AddPreLine (PLine); @@ -3414,9 +3482,13 @@ void ContinueLine (void) -void PreprocessBegin (void) -/* Initialize preprocessor with current file */ +void PreprocessBegin (IFile* Input) +/* Initialize the preprocessor for a new input file */ { + /* Remember the new input file and flag it as new */ + CurInput = Input; + CurInput->GFlags |= IG_NEWFILE; + /* Reset #if depth */ PPStack->Index = -1; @@ -3429,9 +3501,14 @@ void PreprocessBegin (void) -void PreprocessEnd (void) -/* Preprocessor done with current file */ +void PreprocessEnd (IFile* Input) +/* Preprocessor done with current file. The parameter is the file we're +** switching back to. +*/ { + /* Switch back to the old input file */ + CurInput = Input; + /* Check for missing #endif */ while (PPStack->Index >= 0) { if ((PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) != 0) { diff --git a/src/cc65/preproc.h b/src/cc65/preproc.h index e2a1b073c..44e35f30e 100644 --- a/src/cc65/preproc.h +++ b/src/cc65/preproc.h @@ -54,6 +54,9 @@ struct PPIfStack { int Index; }; +/* Forward */ +struct IFile; + /*****************************************************************************/ @@ -62,29 +65,31 @@ struct PPIfStack { +void HandleSpecialMacro (Macro* M, const char* Name); +/* Handle special "magic" macros that may change */ + void Preprocess (void); /* Preprocess a line */ -void SetPPIfStack (PPIfStack* Stack); -/* Specify which PP #if stack to use */ - -void ContinueLine (void); -/* Continue the current line ended with a '\\' */ - -void PreprocessBegin (void); -/* Initialize preprocessor with current file */ - -void PreprocessEnd (void); -/* Preprocessor done with current file */ - void InitPreprocess (void); /* Init preprocessor */ void DonePreprocess (void); /* Done with preprocessor */ -void HandleSpecialMacro (Macro* M, const char* Name); -/* Handle special "magic" macros that may change */ +void SetPPIfStack (PPIfStack* Stack); +/* Specify which PP #if stack to use */ + +void ContinueLine (void); +/* Continue the current line ended with a '\\' */ + +void PreprocessBegin (struct IFile* Input); +/* Initialize the preprocessor for a new input file */ + +void PreprocessEnd (struct IFile* Input); +/* Preprocessor done with current file. The parameter is the file we're +** switching back to. +*/