Merge pull request #2743 from kugelfuhr/kugelfuhr/include-guard

Do not read a file twice if it has an include guard
This commit is contained in:
Bob Andrews
2025-06-27 16:16:51 +02:00
committed by GitHub
4 changed files with 278 additions and 176 deletions

View File

@@ -40,6 +40,7 @@
/* common */ /* common */
#include "check.h" #include "check.h"
#include "coll.h" #include "coll.h"
#include "debugflag.h"
#include "filestat.h" #include "filestat.h"
#include "fname.h" #include "fname.h"
#include "print.h" #include "print.h"
@@ -77,17 +78,6 @@ char NextC = '\0';
/* Maximum count of nested includes */ /* Maximum count of nested includes */
#define MAX_INC_NESTING 16 #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 */ /* Struct that describes an active input file */
typedef struct AFile AFile; typedef struct AFile AFile;
struct AFile { struct AFile {
@@ -137,6 +127,8 @@ static IFile* NewIFile (const char* Name, InputType Type)
IF->Size = 0; IF->Size = 0;
IF->MTime = 0; IF->MTime = 0;
IF->Type = Type; IF->Type = Type;
IF->GFlags = IG_NONE;
SB_Init (&IF->GuardMacro);
memcpy (IF->Name, Name, Len+1); memcpy (IF->Name, Name, Len+1);
/* Insert the new structure into the IFile collection */ /* Insert the new structure into the IFile collection */
@@ -280,7 +272,7 @@ void OpenMainFile (const char* Name)
SetPPIfStack (&MainFile->IfStack); SetPPIfStack (&MainFile->IfStack);
/* Begin PP for this file */ /* Begin PP for this file */
PreprocessBegin (); PreprocessBegin (IF);
/* Allocate the input line buffer */ /* Allocate the input line buffer */
Line = NewStrBuf (); 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 /* 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 = FindFile (N);
if (IF == 0) { if (IF == 0) {
IF = NewIFile (N, IT); 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 */ /* 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); SetPPIfStack (&AF->IfStack);
/* Begin PP for this file */ /* Begin PP for this file */
PreprocessBegin (); PreprocessBegin (IF);
} }
@@ -356,27 +358,24 @@ void CloseIncludeFile (void)
** NULL if this was the main file. ** 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 */ /* Determine the file that is active after closing this one. We never
unsigned AFileCount = CollCount (&AFiles); ** 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 */ /* End preprocessing for the current input file */
PRECONDITION (AFileCount > 0); PreprocessEnd (NextInput->Input);
/* End preprocessor in this file */
PreprocessEnd ();
/* Get the current active input file */
Input = CollLast (&AFiles);
/* Close the current input file (we're just reading so no error check) */ /* Close the current input file (we're just reading so no error check) */
fclose (Input->F); 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 we had added an extra search path for this AFile, remove it */
if (Input->SearchPath) { if (Input->SearchPath) {
PopSearchPath (UsrIncSearchPath); PopSearchPath (UsrIncSearchPath);
@@ -385,10 +384,9 @@ void CloseIncludeFile (void)
/* Delete the active file structure */ /* Delete the active file structure */
FreeAFile (Input); FreeAFile (Input);
/* Use previous file with PP if it is not the main file */ /* If we've switched files, use the if stack from the previous file */
if (AFileCount > 0) { if (Input != NextInput) {
Input = CollLast (&AFiles); SetPPIfStack (&NextInput->IfStack);
SetPPIfStack (&Input->IfStack);
} }
} }

View File

@@ -65,6 +65,28 @@ typedef enum {
IT_USRINC = 0x04, /* User include file (using "") */ IT_USRINC = 0x04, /* User include file (using "") */
} InputType; } 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 */ /* The current input line */
extern StrBuf* Line; extern StrBuf* Line;

View File

@@ -93,6 +93,10 @@
#define IFCOND_SKIP 0x01U #define IFCOND_SKIP 0x01U
#define IFCOND_ELSE 0x02U #define IFCOND_ELSE 0x02U
#define IFCOND_NEEDTERM 0x04U #define IFCOND_NEEDTERM 0x04U
#define IFCOND_ISGUARD 0x08U
/* Current input file */
static IFile* CurInput = 0;
/* Current PP if stack */ /* Current PP if stack */
static PPIfStack* PPStack; 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 */ /* Push a new if level onto the if stack */
{ {
/* Check for an overflow of 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 */ /* Push the #if condition */
++PPStack->Index; ++PPStack->Index;
if (Skip) { if (Skip) {
PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM; PPStack->Stack[PPStack->Index] = IFCOND_SKIP | IFCOND_NEEDTERM | Flags;
return 1; return 1;
} else { } else {
PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM; PPStack->Stack[PPStack->Index] = IFCOND_NONE | IFCOND_NEEDTERM | Flags;
return (Invert ^ Cond); return (Invert ^ Cond);
} }
} }
@@ -2794,29 +2798,40 @@ static int DoIf (int Skip)
} }
/* Set the #if condition according to the expression result */ /* 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) static int DoIfDef (int Skip, int Flag)
/* Process #ifdef if flag == 1, or #ifndef if flag == 0. */ /* 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; ident Ident;
SkipWhitespace (0); SkipWhitespace (0);
if (MacName (Ident)) { if (MacName (Ident)) {
CheckForBadIdent (Ident, IS_Get (&Standard), 0); CheckForBadIdent (Ident, IS_Get (&Standard), 0);
Value = IsMacro (Ident); IsDef = IsMacro (Ident);
/* Check for extra tokens */ /* 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; int PPSkip = 0;
ident Directive; 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); int Whitespace = SkipWhitespace (0);
/* Check for stuff to skip */ /* Check for stuff to skip */
while (CurC == '\0' || CurC == '#' || PPSkip) { 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 */ /* Check for preprocessor lines lines */
if (CurC == '#') { if (CurC == '#') {
unsigned IfCond;
/* Skip the hash and following white space */
NextChar (); NextChar ();
SkipWhitespace (0); SkipWhitespace (0);
if (CurC == '\0') { if (CurC == '\0') {
/* Ignore the empty preprocessor directive */ /* Ignore the empty preprocessor directive */
CurInput->GFlags &= ~IG_NEWFILE;
continue; continue;
} }
if (!IsSym (Directive)) { if (!IsSym (Directive)) {
@@ -3089,20 +3117,25 @@ static int ParseDirectives (unsigned ModeFlags)
PPError ("Preprocessor directive expected"); PPError ("Preprocessor directive expected");
} }
ClearLine (); ClearLine ();
} else { CurInput->GFlags &= ~IG_NEWFILE;
continue;
}
switch (FindPPDirectiveType (Directive)) { switch (FindPPDirectiveType (Directive)) {
case PPD_DEFINE: case PPD_DEFINE:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
DoDefine (); DoDefine ();
} }
break; break;
case PPD_ELIF: case PPD_ELIF:
CurInput->GFlags &= ~IG_NEWFILE;
if (PPStack->Index >= 0) { if (PPStack->Index >= 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { unsigned char PPCond = PPStack->Stack[PPStack->Index];
if ((PPCond & IFCOND_ELSE) == 0) {
/* Handle as #else/#if combination */ /* Handle as #else/#if combination */
if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { if ((PPCond & IFCOND_SKIP) == 0) {
PPSkip = !PPSkip; PPSkip = !PPSkip;
} }
PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; PPStack->Stack[PPStack->Index] |= IFCOND_ELSE;
@@ -3110,6 +3143,11 @@ static int ParseDirectives (unsigned ModeFlags)
/* #elif doesn't need a terminator */ /* #elif doesn't need a terminator */
PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM; PPStack->Stack[PPStack->Index] &= ~IFCOND_NEEDTERM;
/* An include guard cannot have a #elif */
if (PPCond & IFCOND_ISGUARD) {
CurInput->GFlags &= ~IG_ISGUARDED;
}
} else { } else {
PPError ("Duplicate #else/#elif"); PPError ("Duplicate #else/#elif");
} }
@@ -3119,13 +3157,20 @@ static int ParseDirectives (unsigned ModeFlags)
break; break;
case PPD_ELSE: case PPD_ELSE:
CurInput->GFlags &= ~IG_NEWFILE;
if (PPStack->Index >= 0) { if (PPStack->Index >= 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_ELSE) == 0) { unsigned char PPCond = PPStack->Stack[PPStack->Index];
if ((PPStack->Stack[PPStack->Index] & IFCOND_SKIP) == 0) { if ((PPCond & IFCOND_ELSE) == 0) {
if ((PPCond & IFCOND_SKIP) == 0) {
PPSkip = !PPSkip; PPSkip = !PPSkip;
} }
PPStack->Stack[PPStack->Index] |= IFCOND_ELSE; PPStack->Stack[PPStack->Index] |= IFCOND_ELSE;
/* An include guard cannot have a #else */
if (PPCond & IFCOND_ISGUARD) {
CurInput->GFlags &= ~IG_ISGUARDED;
}
/* Check for extra tokens */ /* Check for extra tokens */
CheckExtraTokens ("else"); CheckExtraTokens ("else");
} else { } else {
@@ -3137,6 +3182,7 @@ static int ParseDirectives (unsigned ModeFlags)
break; break;
case PPD_ENDIF: case PPD_ENDIF:
CurInput->GFlags &= ~IG_NEWFILE;
if (PPStack->Index >= 0) { if (PPStack->Index >= 0) {
/* Remove any clauses on top of stack that do not /* Remove any clauses on top of stack that do not
** need a terminating #endif. ** need a terminating #endif.
@@ -3150,7 +3196,11 @@ static int ParseDirectives (unsigned ModeFlags)
CHECK (PPStack->Index >= 0); CHECK (PPStack->Index >= 0);
/* Remove the clause that needs a terminator */ /* Remove the clause that needs a terminator */
PPSkip = (PPStack->Stack[PPStack->Index--] & IFCOND_SKIP) != 0; IfCond = PPStack->Stack[PPStack->Index--];
PPSkip = (IfCond & IFCOND_SKIP) != 0;
if (IfCond & IFCOND_ISGUARD) {
CurInput->GFlags |= IG_GUARDCLOSED;
}
/* Check for extra tokens */ /* Check for extra tokens */
CheckExtraTokens ("endif"); CheckExtraTokens ("endif");
@@ -3160,36 +3210,43 @@ static int ParseDirectives (unsigned ModeFlags)
break; break;
case PPD_ERROR: case PPD_ERROR:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
DoError (); DoError ();
} }
break; break;
case PPD_IF: case PPD_IF:
CurInput->GFlags &= ~IG_NEWFILE;
PPSkip = DoIf (PPSkip); PPSkip = DoIf (PPSkip);
break; break;
case PPD_IFDEF: case PPD_IFDEF:
CurInput->GFlags &= ~IG_NEWFILE;
PPSkip = DoIfDef (PPSkip, 1); PPSkip = DoIfDef (PPSkip, 1);
break; break;
case PPD_IFNDEF: case PPD_IFNDEF:
PPSkip = DoIfDef (PPSkip, 0); PPSkip = DoIfDef (PPSkip, 0);
CurInput->GFlags &= ~IG_NEWFILE;
break; break;
case PPD_INCLUDE: case PPD_INCLUDE:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
DoInclude (); DoInclude ();
} }
break; break;
case PPD_LINE: case PPD_LINE:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
DoLine (); DoLine ();
} }
break; break;
case PPD_PRAGMA: case PPD_PRAGMA:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
if ((ModeFlags & MSM_IN_ARG_LIST) == 0) { if ((ModeFlags & MSM_IN_ARG_LIST) == 0) {
DoPragma (); DoPragma ();
@@ -3201,12 +3258,14 @@ static int ParseDirectives (unsigned ModeFlags)
break; break;
case PPD_UNDEF: case PPD_UNDEF:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
DoUndef (); DoUndef ();
} }
break; break;
case PPD_WARNING: case PPD_WARNING:
CurInput->GFlags &= ~IG_NEWFILE;
/* #warning is a non standard extension */ /* #warning is a non standard extension */
if (IS_Get (&Standard) > STD_C99) { if (IS_Get (&Standard) > STD_C99) {
if (!PPSkip) { if (!PPSkip) {
@@ -3221,14 +3280,13 @@ static int ParseDirectives (unsigned ModeFlags)
break; break;
default: default:
CurInput->GFlags &= ~IG_NEWFILE;
if (!PPSkip) { if (!PPSkip) {
PPError ("Preprocessor directive expected"); PPError ("Preprocessor directive expected");
} }
ClearLine (); ClearLine ();
} }
} }
}
if (NextLine () == 0) { if (NextLine () == 0) {
break; break;
} }
@@ -3236,6 +3294,15 @@ static int ParseDirectives (unsigned ModeFlags)
Whitespace = SkipWhitespace (0) || Whitespace; 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; return Whitespace;
} }
@@ -3346,6 +3413,7 @@ void Preprocess (void)
OLine = PLine; OLine = PLine;
ParseDirectives (MSM_MULTILINE); ParseDirectives (MSM_MULTILINE);
OLine = 0; OLine = 0;
CurInput->GFlags &= ~IG_NEWFILE;
/* Add the source info to preprocessor output if needed */ /* Add the source info to preprocessor output if needed */
AddPreLine (PLine); AddPreLine (PLine);
@@ -3414,9 +3482,13 @@ void ContinueLine (void)
void PreprocessBegin (void) void PreprocessBegin (IFile* Input)
/* Initialize preprocessor with current file */ /* 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 */ /* Reset #if depth */
PPStack->Index = -1; PPStack->Index = -1;
@@ -3429,9 +3501,14 @@ void PreprocessBegin (void)
void PreprocessEnd (void) void PreprocessEnd (IFile* Input)
/* Preprocessor done with current file */ /* 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 */ /* Check for missing #endif */
while (PPStack->Index >= 0) { while (PPStack->Index >= 0) {
if ((PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) != 0) { if ((PPStack->Stack[PPStack->Index] & IFCOND_NEEDTERM) != 0) {

View File

@@ -54,6 +54,9 @@ struct PPIfStack {
int Index; 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); void Preprocess (void);
/* Preprocess a line */ /* 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); void InitPreprocess (void);
/* Init preprocessor */ /* Init preprocessor */
void DonePreprocess (void); void DonePreprocess (void);
/* Done with preprocessor */ /* Done with preprocessor */
void HandleSpecialMacro (Macro* M, const char* Name); void SetPPIfStack (PPIfStack* Stack);
/* Handle special "magic" macros that may change */ /* 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.
*/