windows-nt/Source/XPSP1/NT/sdktools/build/builddb.c
2020-09-26 16:20:57 +08:00

2079 lines
58 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1989 - 1994
//
// File: builddb.c
//
// Contents: Contains the file and directory database manipulation
// functions.
//
// History: 16-May-94 SteveWo Created
// ... see SLM logs
// 26-Jul-94 LyleC Cleanup/Add Pass0 Support
//
//----------------------------------------------------------------------------
#include "build.h"
BOOL fAssertCleanTree = FALSE;
typedef struct _FLAGSTRINGS {
ULONG Mask;
LPSTR pszName;
} FLAGSTRINGS;
FLAGSTRINGS DirFlags[] = {
{ DIRDB_SOURCES, "Sources" },
{ DIRDB_DIRS, "Dirs" },
{ DIRDB_MAKEFILE, "Makefile" },
{ DIRDB_MAKEFIL0, "Makefil0" },
{ DIRDB_TARGETFILE0, "Targetfile0" },
{ DIRDB_TARGETFILES, "Targetfiles" },
{ DIRDB_RESOURCE, "Resource" },
{ DIRDB_PASS0, "PassZero" },
{ DIRDB_SOURCES_SET, "SourcesSet" },
{ DIRDB_CHICAGO_INCLUDES, "ChicagoIncludes" },
{ DIRDB_NEW, "New" },
{ DIRDB_SCANNED, "Scanned" },
{ DIRDB_SHOWN, "Shown" },
{ DIRDB_GLOBAL_INCLUDES, "GlobalIncludes" },
{ DIRDB_SYNCHRONIZE_BLOCK, "SynchronizeBlock" },
{ DIRDB_SYNCHRONIZE_DRAIN, "SynchronizeDrain" },
{ DIRDB_COMPILENEEDED, "CompileNeeded" },
{ DIRDB_COMPILEERRORS, "CompileErrors" },
{ DIRDB_SOURCESREAD, "SourcesRead" },
{ DIRDB_DLLTARGET, "DllTarget" },
{ DIRDB_LINKNEEDED, "LinkNeeded" },
{ DIRDB_FORCELINK, "ForceLink" },
{ DIRDB_PASS0NEEDED, "Pass0Needed" },
{ DIRDB_MAKEFIL1, "Makefil1" },
{ DIRDB_CHECKED_ALT_DIR, "CheckedAltDir" },
{ 0, NULL },
};
FLAGSTRINGS FileFlags[] = {
{ FILEDB_SOURCE, "Source" },
{ FILEDB_DIR, "Dir" },
{ FILEDB_HEADER, "Header" },
{ FILEDB_ASM, "Asm" },
{ FILEDB_MASM, "Masm" },
{ FILEDB_RC, "Rc" },
{ FILEDB_C, "C" },
{ FILEDB_MIDL, "Midl" },
{ FILEDB_ASN, "Asn" },
{ FILEDB_JAVA, "Java" },
{ FILEDB_MOF, "ManagedObjectFormat" },
{ FILEDB_CSHARP, "C#"},
{ FILEDB_SCANNED, "Scanned" },
{ FILEDB_OBJECTS_LIST, "ObjectsList" },
{ FILEDB_FILE_MISSING, "FileMissing" },
{ FILEDB_MKTYPLIB, "MkTypeLib" },
{ FILEDB_MULTIPLEPASS, "MultiplePass" },
{ FILEDB_PASS0, "PassZero" },
{ 0, NULL },
};
FLAGSTRINGS IncludeFlags[] = {
{ INCLUDEDB_LOCAL, "Local" },
{ INCLUDEDB_POST_HDRSTOP, "PostHdrStop" },
{ INCLUDEDB_MISSING, "Missing" },
{ INCLUDEDB_GLOBAL, "Global" },
{ INCLUDEDB_SNAPPED, "Snapped" },
{ INCLUDEDB_CYCLEALLOC, "CycleAlloc" },
{ INCLUDEDB_CYCLEROOT, "CycleRoot" },
{ INCLUDEDB_CYCLEORPHAN, "CycleOrphan" },
{ 0, NULL },
};
FLAGSTRINGS SourceFlags[] = {
{ SOURCEDB_SOURCES_LIST, "SourcesList" },
{ SOURCEDB_FILE_MISSING, "FileMissing" },
{ SOURCEDB_PCH, "Pch" },
{ SOURCEDB_OUT_OF_DATE, "OutOfDate" },
{ SOURCEDB_COMPILE_NEEDED, "CompileNeeded" },
{ 0, NULL },
};
//
// Function prototypes
//
VOID
FreeFileDB(PFILEREC *FileDB);
VOID
PrintFlags(FILE *pf, ULONG Flags, FLAGSTRINGS *pfs);
//+---------------------------------------------------------------------------
//
// Function: CheckSum
//
// Synopsis: Returns a checksum value for a string.
//
//----------------------------------------------------------------------------
USHORT
CheckSum(LPSTR psz)
{
USHORT sum = 0;
while (*psz != '\0') {
if (sum & 0x8000) {
sum = ((sum << 1) | 1) + *psz++;
}
else {
sum = (sum << 1) + *psz++;
}
}
return(sum);
}
//+---------------------------------------------------------------------------
//
// Function: FindSourceDirDB
//
// Synopsis: Builds a path from the two given components and returns
// a filled DIRREC structure from it.
//
// Arguments: [pszDir] -- Directory
// [pszRelPath] -- Path relative to [pszDir]
// [fTruncateFileName] -- Remove a filename from [pszRelPath]
//
// Returns: A filled DIRREC structure for the given directory.
//
// Notes: If the directory does not exist in the data base, then a
// DIRREC structure will be returned with the DIRDB_NEW flag
// set and no other data (i.e. the directory will not have
// been scanned.)
//
//----------------------------------------------------------------------------
DIRREC *
FindSourceDirDB(
LPSTR pszDir, // directory
LPSTR pszRelPath, // relative path
BOOL fTruncateFileName) // TRUE: drop last component of path
{
LPSTR pszFile;
char path[DB_MAX_PATH_LENGTH];
AssertPathString(pszDir);
AssertPathString(pszRelPath);
strcpy(path, pszDir);
if (path[0] != '\0')
strcat(path, "\\");
strcat(path, pszRelPath);
pszFile = path + strlen(path);
if (fTruncateFileName) {
while (pszFile > path) {
pszFile--;
if (*pszFile == '\\' || *pszFile == '/') {
*pszFile = '\0';
break;
}
}
}
if (!CanonicalizePathName(path, CANONICALIZE_ONLY, path)) {
return(NULL);
}
if (DEBUG_4) {
BuildMsgRaw(
"FindSourceDirDB(%s, %s, %u)\n",
path,
pszFile,
fTruncateFileName);
}
AssertPathString(path);
return(LoadDirDB(path));
}
//+---------------------------------------------------------------------------
//
// Function: FindSourceFileDB
//
// Synopsis: Returns a FILEREC with information about the given file.
//
// Arguments: [pdr] -- DIRREC giving directory from which to start
// looking for [pszRelPath]
// [pszRelPath] -- Relative path from [pdr] of the file
// [ppdr] -- [out] DIRREC of directory actually containing
// the file. Can be NULL.
//
// Returns: FILEREC for file of interest.
//
// Notes: If the directory containing the file has not yet been scanned,
// then it will be scanned using ScanDirectory().
//
//----------------------------------------------------------------------------
FILEREC *
FindSourceFileDB(
DIRREC *pdr,
LPSTR pszRelPath,
DIRREC **ppdr)
{
LPSTR p, pszFile;
AssertPathString(pszRelPath);
if (strchr(pszRelPath, '\\') != NULL) {
// There's a path component in this filename. Let's see where it points to.
if ( (pszRelPath[0] == '\\') || /* Absolute from root or UNC Path */
(pszRelPath[1] == ':' )) /* drive : path */
{
pdr = FindSourceDirDB("", pszRelPath, TRUE);
} else {
pdr = FindSourceDirDB(pdr->Name, pszRelPath, TRUE);
}
}
if (ppdr != NULL) {
*ppdr = pdr;
}
if (pdr == NULL ) {
return(NULL);
}
pszFile = pszRelPath;
for (p = pszFile; *p != '\0'; p++) {
if (*p == '\\') {
pszFile = p + 1;
}
}
if (DEBUG_4) {
BuildMsgRaw("FindSourceFileDB(%s, %s)\n", pdr->Name, pszFile);
}
//
// Scan the directory containing the file if we haven't already.
//
if ((pdr->DirFlags & DIRDB_SCANNED) == 0) {
if (DEBUG_1) {
BuildMsgRaw(
"FindSourceFileDB(%s, %s) Delayed scan\n",
pdr->Name,
pszFile);
}
pdr = ScanDirectory(pdr->Name);
if (pdr == NULL) {
return(NULL);
}
}
return(LookupFileDB(pdr, pszFile));
}
//+---------------------------------------------------------------------------
//
// Function: InsertSourceDB
//
// Synopsis: Insert a file listed in SOURCES= into a list of SOURCEREC
// structures.
//
// Arguments: [ppsrNext] -- Head of list of sources files to add to.
// [pfr] -- File to be inserted.
// [SubDirMask] -- Indicates what directory the file is in.
// (Current, Parent, or a machine-specific dir)
// [SrcFlags] -- SOURCEDB flags appropriate to this file.
//
// Returns: SOURCEREC that was inserted. May be ignored.
//
// Notes: InsertSourceDB maintains a sort order for PickFirst() based
// first on the filename extension, then on the subdirectory
// mask.
//
// Two exceptions to the alphabetic sort are:
// - No extension sorts last.
// - .rc extension sorts first.
//
// If the file is already in the list of sources then this
// function just updates the flags and returns.
//
//----------------------------------------------------------------------------
SOURCEREC *
InsertSourceDB(
SOURCEREC **ppsrNext,
FILEREC *pfr,
UCHAR SubDirMask,
UCHAR SrcFlags)
{
SOURCEREC *psr;
SOURCEREC **ppsrInsert;
LPSTR pszext;
BOOL fRC;
AssertFile(pfr);
ppsrInsert = NULL;
pszext = strrchr(pfr->Name, '.');
fRC = FALSE;
if (pszext != NULL && _stricmp(pszext, ".rc") == 0) {
fRC = TRUE;
}
for ( ; (psr = *ppsrNext) != NULL; ppsrNext = &psr->psrNext) {
LPSTR p;
int r;
AssertSource(psr);
if (psr->pfrSource == pfr) {
assert(psr->SourceSubDirMask == SubDirMask);
psr->SrcFlags = SrcFlags;
return(psr);
}
if (ppsrInsert == NULL && pszext != NULL) {
if ((p = strrchr(psr->pfrSource->Name, '.')) == NULL) {
r = -1; // insert new file here
}
else {
r = strcmp(pszext, p);
if (r != 0) {
if (fRC) {
r = -1; // insert new RC file here
}
else if (strcmp(p, ".rc") == 0) {
r = 1; // old RC file comes first
}
}
}
if (r < 0 || SubDirMask > psr->SourceSubDirMask) {
ppsrInsert = ppsrNext;
}
}
}
AllocMem(sizeof(SOURCEREC), &psr, MT_SOURCEDB);
memset(psr, 0, sizeof(*psr));
SigCheck(psr->Sig = SIG_SOURCEREC);
if (ppsrInsert != NULL) {
ppsrNext = ppsrInsert;
}
psr->psrNext = *ppsrNext;
*ppsrNext = psr;
psr->pfrSource = pfr;
pfr->FileFlags |= FILEDB_SOURCEREC_EXISTS;
psr->SourceSubDirMask = SubDirMask;
psr->SrcFlags = SrcFlags;
return(psr);
}
//+---------------------------------------------------------------------------
//
// Function: FindSourceDB
//
// Synopsis: Finds the SOURCEREC in a list which corresponds to the given
// FILEREC.
//
//----------------------------------------------------------------------------
SOURCEREC *
FindSourceDB(
SOURCEREC *psr,
FILEREC *pfr)
{
while (psr != NULL) {
AssertSource(psr);
if (psr->pfrSource == pfr) {
return(psr);
}
psr = psr->psrNext;
}
return(NULL);
}
//+---------------------------------------------------------------------------
//
// Function: FreeSourceDB
//
// Synopsis: Frees a list of SOURCERECs
//
// Arguments: [ppsr] -- List to free
//
//----------------------------------------------------------------------------
VOID
FreeSourceDB(SOURCEREC **ppsr)
{
if (*ppsr != NULL) {
SOURCEREC *psr;
SOURCEREC *psrNext;
psr = *ppsr;
AssertSource(psr);
psrNext = psr->psrNext;
SigCheck(psr->Sig = 0);
FreeMem(ppsr, MT_SOURCEDB);
*ppsr = psrNext;
}
}
//+---------------------------------------------------------------------------
//
// Function: FreeIncludeDB
//
//----------------------------------------------------------------------------
VOID
FreeIncludeDB(INCLUDEREC **ppir)
{
if (*ppir != NULL) {
INCLUDEREC *pir;
INCLUDEREC *pirNext;
pir = *ppir;
AssertInclude(pir);
AssertCleanTree(pir, NULL); // Tree must be clean
pirNext = pir->Next;
SigCheck(pir->Sig = 0);
FreeMem(ppir, MT_INCLUDEDB);
*ppir = pirNext;
}
}
//+---------------------------------------------------------------------------
//
// Function: FreeFileDB
//
//----------------------------------------------------------------------------
VOID
FreeFileDB(FILEREC **ppfr)
{
if (*ppfr != NULL) {
FILEREC *pfr;
FILEREC *pfrNext;
pfr = *ppfr;
AssertFile(pfr);
UnsnapIncludeFiles(pfr, TRUE);
while (pfr->IncludeFiles) {
FreeIncludeDB(&pfr->IncludeFiles);
}
pfrNext = pfr->Next;
SigCheck(pfr->Sig = 0);
FreeMem(ppfr, MT_FILEDB);
*ppfr = pfrNext;
}
}
//+---------------------------------------------------------------------------
//
// Function: FreeDirDB
//
//----------------------------------------------------------------------------
VOID
FreeDirDB(DIRREC **ppdr)
{
if (*ppdr != NULL) {
DIRREC *pdr;
DIRREC *pdrNext;
pdr = *ppdr;
AssertDir(pdr);
FreeDirData(pdr);
while (pdr->Files) {
FreeFileDB(&pdr->Files);
}
pdrNext = pdr->Next;
SigCheck(pdr->Sig = 0);
FreeMem(ppdr, MT_DIRDB);
*ppdr = pdrNext;
}
}
//+---------------------------------------------------------------------------
//
// Function: FreeAllDirs
//
//----------------------------------------------------------------------------
VOID
FreeAllDirs(VOID)
{
while (AllDirs != NULL) {
FreeDirDB(&AllDirs);
#if DBG
if (fDebug & 8) {
BuildMsgRaw("Freed one directory\n");
PrintAllDirs();
}
#endif
}
}
//+---------------------------------------------------------------------------
//
// Function: LookupDirDB
//
// Synopsis: Searches the database for a given directory.
//
// Arguments: [DirName] -- Directory to search for.
//
// Returns: DIRREC of the given directory. NULL if not found.
//
// Notes: If the directory is not in the database it will not be added.
// Use LoadDirDB in this case.
//
//----------------------------------------------------------------------------
PDIRREC
LookupDirDB(
LPSTR DirName
)
{
PDIRREC *DirDBNext = &AllDirs;
PDIRREC DirDB;
USHORT sum;
AssertPathString(DirName);
sum = CheckSum(DirName);
while (DirDB = *DirDBNext) {
if (sum == DirDB->CheckSum && strcmp(DirName, DirDB->Name) == 0) {
if (DirDB->FindCount == 0 && fForce) {
FreeDirDB(DirDBNext);
return(NULL);
}
DirDB->FindCount++;
// Move to head of list to make next lookup faster
// *DirDBNext = DirDB->Next;
// DirDB->Next = AllDirs;
// AllDirs = DirDB;
return(DirDB);
}
DirDBNext = &DirDB->Next;
}
return(NULL);
}
//+---------------------------------------------------------------------------
//
// Function: LoadDirDB
//
// Synopsis: Searches the database for a directory name, and if not found
// creates a new DIRREC entry in the database.
//
// Arguments: [DirName] -- Directory to search for.
//
// Returns: Filled in DIRREC structure for the given directory.
//
// Notes: The directory will not be scanned if it wasn't in the
// database. Use ScanDirectory to scan the directory,
// however, note that InsertSourceDB will automatically scan
// the directory only when necessary.
//
//----------------------------------------------------------------------------
PDIRREC
LoadDirDB(
LPSTR DirName
)
{
UINT i;
PDIRREC DirDB, *DirDBNext;
LPSTR s;
AssertPathString(DirName);
if (DirDB = LookupDirDB(DirName)) {
return(DirDB);
}
if (ProbeFile(NULL, DirName) == -1) {
return( NULL );
}
DirDBNext = &AllDirs;
while (DirDB = *DirDBNext) {
DirDBNext = &DirDB->Next;
}
AllDirsModified = TRUE;
AllocMem(sizeof(DIRREC) + strlen(DirName), &DirDB, MT_DIRDB);
memset(DirDB, 0, sizeof(*DirDB));
SigCheck(DirDB->Sig = SIG_DIRREC);
DirDB->DirFlags = DIRDB_NEW;
DirDB->FindCount = 1;
CopyString(DirDB->Name, DirName, TRUE);
DirDB->CheckSum = CheckSum(DirDB->Name);
if (DEBUG_1) {
BuildMsgRaw("LoadDirDB creating %s\n", DirDB->Name);
}
*DirDBNext = DirDB;
return( DirDB );
}
//+---------------------------------------------------------------------------
//
// Debug helper functions to print parts of the database.
//
//----------------------------------------------------------------------------
#if DBG
VOID
PrintAllDirs(VOID)
{
DIRREC **ppdr, *pdr;
for (ppdr = &AllDirs; (pdr = *ppdr) != NULL; ppdr = &pdr->Next) {
PrintDirDB(pdr, 1|2|4);
}
}
#endif
VOID
PrintFlags(FILE *pf, ULONG Flags, FLAGSTRINGS *pfs)
{
LPSTR p = ",";
while (pfs->pszName != NULL) {
if (pfs->Mask & Flags) {
fprintf(pf, "%s %s", p, pfs->pszName);
p = "";
}
pfs++;
}
fprintf(pf, szNewLine);
}
BOOL
PrintIncludes(FILE *pf, FILEREC *pfr, BOOL fTree)
{
INCLUDEREC *pir;
BOOL fMatch = pfr->IncludeFilesTree == pfr->IncludeFiles;
pir = fTree? pfr->IncludeFilesTree : pfr->IncludeFiles;
while (pir != NULL) {
LPSTR pszdir = "<No File Record>";
char OpenQuote, CloseQuote;
if (pir->IncFlags & INCLUDEDB_LOCAL) {
OpenQuote = CloseQuote = '"';
}
else {
OpenQuote = '<';
CloseQuote = '>';
}
fprintf(
pf,
" %c#include %c%s%c",
fMatch? ' ' : fTree? '+' : '-',
OpenQuote,
pir->Name,
CloseQuote);
if (pir->Version != 0) {
fprintf(pf, " (v%d)", pir->Version);
}
if (pir->pfrCycleRoot != NULL) {
fprintf(
pf,
" (root=%s\\%s)",
pir->pfrCycleRoot->Dir->Name,
pir->pfrCycleRoot->Name);
}
if (pir->pfrInclude != NULL) {
if (pir->pfrInclude->Dir == pfr->Dir) {
pszdir = ".";
}
else {
pszdir = pir->pfrInclude->Dir->Name;
}
}
fprintf(pf, " %s", pszdir);
PrintFlags(pf, pir->IncFlags, IncludeFlags);
if (pir->NextTree != pir->Next) {
fMatch = FALSE;
}
pir = fTree? pir->NextTree : pir->Next;
}
return(fMatch);
}
VOID
PrintSourceDBList(SOURCEREC *psr, int i)
{
TARGET_MACHINE_INFO *pMachine;
pMachine = i < 0 ? TargetMachines[0] : PossibleTargetMachines[i];
for ( ; psr != NULL; psr = psr->psrNext) {
assert(
(psr->SourceSubDirMask & ~TMIDIR_PARENT) == 0 ||
pMachine->SourceSubDirMask ==
(psr->SourceSubDirMask & ~TMIDIR_PARENT));
BuildMsgRaw(
" %s%s%s%s%s",
(psr->SourceSubDirMask & TMIDIR_PARENT)? "..\\" : "",
(psr->SourceSubDirMask & ~TMIDIR_PARENT)?
pMachine->SourceDirectory : "",
(psr->SourceSubDirMask & ~TMIDIR_PARENT)? "\\" : "",
psr->pfrSource->Name,
(psr->SrcFlags & SOURCEDB_PCH)?
" (pch)" :
(psr->SrcFlags & SOURCEDB_SOURCES_LIST) == 0?
" (From exe list)" : "");
PrintFlags(stderr, psr->SrcFlags, SourceFlags);
}
}
VOID
PrintFileDB(FILE *pf, FILEREC *pfr, int DetailLevel)
{
fprintf(pf, " File: %s", pfr->Name);
if (pfr->FileFlags & FILEDB_DIR) {
fprintf(pf, " (Sub-Directory)");
}
else
if (pfr->FileFlags & (FILEDB_SOURCE | FILEDB_HEADER)) {
LPSTR pszType = (pfr->FileFlags & FILEDB_SOURCE)? "Source" : "Header";
if (pfr->FileFlags & FILEDB_ASM) {
fprintf(pf, " (Assembler (CPP) %s File)", pszType);
}
else
if (pfr->FileFlags & FILEDB_MASM) {
fprintf(pf, " (Assembler (MASM) %s File)", pszType);
}
else
if (pfr->FileFlags & FILEDB_RC) {
fprintf(pf, " (Resource Compiler (RC) %s File)", pszType);
}
else
if (pfr->FileFlags & FILEDB_MIDL) {
fprintf(pf, " (MIDL %s File)", pszType);
}
else
if (pfr->FileFlags & FILEDB_ASN) {
fprintf(pf, " (ASN %s File)", pszType);
}
else
if (pfr->FileFlags & FILEDB_MKTYPLIB) {
fprintf(pf, " (Type Library (MkTypLib) %s File)", pszType);
}
else {
fprintf(pf, " (C %s File)", pszType);
}
if ((pfr->FileFlags & FILEDB_HEADER) && pfr->Version != 0) {
fprintf(pf, " (v%d)", pfr->Version);
}
if (pfr->GlobalSequence != 0) {
fprintf(pf, " (GlobalSeq=%d)", pfr->GlobalSequence);
}
if (pfr->LocalSequence != 0) {
fprintf(pf, " (LocalSeq=%d)", pfr->LocalSequence);
}
fprintf(pf, " - %u lines", pfr->SourceLines);
}
PrintFlags(pf, pfr->FileFlags, FileFlags);
if (pfr->IncludeFiles != NULL) {
BOOL fMatch;
fMatch = PrintIncludes(pf, pfr, FALSE);
if (pfr->IncludeFilesTree != NULL) {
fprintf(pf, " IncludeTree %s\n", fMatch? "matches" : "differs:");
if (!fMatch) {
PrintIncludes(pf, pfr, TRUE);
}
}
}
}
VOID
PrintDirDB(DIRREC *pdr, int DetailLevel)
{
FILE *pf = stderr;
FILEREC *pfr, **ppfr;
if (DetailLevel & 1) {
fprintf(pf, "Directory: %s", pdr->Name);
if (pdr->DirFlags & DIRDB_DIRS) {
fprintf(pf, " (Dirs Present)");
}
if (pdr->DirFlags & DIRDB_SOURCES) {
fprintf(pf, " (Sources Present)");
}
if (pdr->DirFlags & DIRDB_MAKEFILE) {
fprintf(pf, " (Makefile Present)");
}
PrintFlags(pf, pdr->DirFlags, DirFlags);
}
if (DetailLevel & 2) {
if (pdr->TargetPath != NULL) {
fprintf(pf, " TargetPath: %s\n", pdr->TargetPath);
}
if (pdr->TargetName != NULL) {
fprintf(pf, " TargetName: %s\n", pdr->TargetName);
}
if (pdr->TargetExt != NULL) {
fprintf(pf, " TargetExt: %s\n", pdr->TargetExt);
}
if (pdr->KernelTest != NULL) {
fprintf(pf, " KernelTest: %s\n", pdr->KernelTest);
}
if (pdr->UserAppls != NULL) {
fprintf(pf, " UserAppls: %s\n", pdr->UserAppls);
}
if (pdr->UserTests != NULL) {
fprintf(pf, " UserTests: %s\n", pdr->UserTests);
}
if (pdr->PchObj != NULL) {
fprintf(pf, " PchObj: %s\n", pdr->PchObj);
}
if (pdr->Pch != NULL) {
fprintf(pf, " Pch: %s\n", pdr->Pch);
}
}
if (DetailLevel & 4) {
for (ppfr = &pdr->Files; (pfr = *ppfr) != NULL; ppfr = &pfr->Next) {
PrintFileDB(pf, pfr, DetailLevel);
}
}
}
//+---------------------------------------------------------------------------
//
// Function: LookupFileDB
//
// Synopsis: Search the database for the given file.
//
// Arguments: [DirDB] -- Directory containing the file
// [FileName] -- File to look for
//
// Returns: FILEREC of file if found, NULL if not.
//
// Notes: The file will not be added to the database if not already
// there.
//
//----------------------------------------------------------------------------
PFILEREC
LookupFileDB(
PDIRREC DirDB,
LPSTR FileName
)
{
PFILEREC FileDB, *FileDBNext;
USHORT sum;
AssertPathString(FileName);
sum = CheckSum(FileName);
if (DEBUG_4) {
BuildMsgRaw("LookupFileDB(%s, %s) - ", DirDB->Name, FileName);
}
FileDBNext = &DirDB->Files;
while (FileDB = *FileDBNext) {
if (sum == FileDB->CheckSum && strcmp(FileName, FileDB->Name) == 0) {
if (DEBUG_4) {
BuildMsgRaw("success\n");
}
return(FileDB);
}
FileDBNext = &FileDB->Next;
}
if (DEBUG_4) {
BuildMsgRaw("failure\n");
}
return(NULL);
}
//+---------------------------------------------------------------------------
//
// FILEDESC
//
// FileDesc is a table describing file names and patterns that we recognize
// and handle specially. WARNING: This table is ordered so the patterns
// at the front are necessarily more specific than those later on.
//
//----------------------------------------------------------------------------
char szMakefile[] = "#";
char szClang[] = "//";
char szAsn[] = "--";
char szMasm[] = ";";
//
// N.B. The first entry in the file descriptor list is an entry that is
// optionally filled with the name of the target dirs file for the
// first build target.
//
FILEDESC FileDesc[] =
{ { "/0dirs", szMakefile, FALSE, 0, DIRDB_DIRS },
{ "makefile", szMakefile, FALSE, 0, DIRDB_MAKEFILE },
{ "makefil0", szMakefile, FALSE, 0, DIRDB_MAKEFIL0 | DIRDB_PASS0 },
{ "makefil1", szMakefile, FALSE, 0, DIRDB_MAKEFIL1 },
{ "sources", szMakefile, FALSE, 0, DIRDB_SOURCES },
{ "dirs", szMakefile, FALSE, 0, DIRDB_DIRS },
{ "mydirs", szMakefile, FALSE, 0, DIRDB_DIRS },
{ "makefile.inc", szMakefile, FALSE, 0, 0 },
{ "common.ver", szClang, TRUE, FILEDB_HEADER, 0 },
{ ".rc", szClang, TRUE, FILEDB_SOURCE | FILEDB_RC, DIRDB_RESOURCE },
{ ".rc2", szClang, TRUE, FILEDB_SOURCE | FILEDB_RC, DIRDB_RESOURCE },
{ ".rcs", szClang, TRUE, FILEDB_SOURCE | FILEDB_RC, DIRDB_RESOURCE },
{ ".rcv", szClang, TRUE, FILEDB_SOURCE | FILEDB_RC, DIRDB_RESOURCE },
{ ".ver", szClang, TRUE, FILEDB_SOURCE | FILEDB_RC, DIRDB_RESOURCE },
{ ".c", szClang, TRUE, FILEDB_SOURCE | FILEDB_C, 0 },
{ ".cxx", szClang, TRUE, FILEDB_SOURCE | FILEDB_C, 0 },
{ ".cpp", szClang, TRUE, FILEDB_SOURCE | FILEDB_C, 0 },
{ ".f", szClang, TRUE, FILEDB_SOURCE, 0 },
{ ".p", szClang, TRUE, FILEDB_SOURCE, 0 },
{ ".s", szClang, TRUE, FILEDB_SOURCE | FILEDB_ASM, 0 },
{ ".asm", szMasm, TRUE, FILEDB_SOURCE | FILEDB_MASM, 0 },
{ ".mc", szMasm, TRUE, FILEDB_SOURCE | FILEDB_RC |
FILEDB_PASS0, DIRDB_PASS0 },
{ ".idl", szClang, TRUE, FILEDB_SOURCE | FILEDB_MIDL |
FILEDB_PASS0, DIRDB_PASS0 },
{ ".asn", szAsn, TRUE, FILEDB_SOURCE | FILEDB_ASN |
FILEDB_MULTIPLEPASS | FILEDB_PASS0,
DIRDB_PASS0 },
{ ".tdl", szClang, TRUE, FILEDB_SOURCE | FILEDB_MKTYPLIB | FILEDB_PASS0, 0 },
{ ".odl", szClang, TRUE, FILEDB_SOURCE | FILEDB_MKTYPLIB | FILEDB_PASS0, 0 },
{ ".pdl", szClang, TRUE, FILEDB_SOURCE | FILEDB_PASS0, 0 },
{ ".h", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".hxx", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".hpp", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".hmd", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".hdl", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".inl", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".rh", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".dlg", szClang, TRUE, FILEDB_HEADER | FILEDB_RC, 0 },
{ ".inc", szMasm, TRUE, FILEDB_HEADER | FILEDB_MASM, 0 },
{ ".src", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 }, // see mvdm\softpc.new\obj.vdm\imlibdep.c
{ ".def", szClang, TRUE, FILEDB_HEADER | FILEDB_C, 0 },
{ ".thk", szClang, TRUE, FILEDB_SOURCE | FILEDB_MULTIPLEPASS |
FILEDB_PASS0, DIRDB_PASS0 },
{ ".java", szClang, TRUE, FILEDB_SOURCE | FILEDB_JAVA, 0 },
{ ".mof", szClang, TRUE, FILEDB_SOURCE | FILEDB_MOF |
FILEDB_PASS0, DIRDB_PASS0 },
{ ".cs", szClang, TRUE, FILEDB_SOURCE | FILEDB_CSHARP, 0 },
// MUST BE LAST
{ NULL, "", FALSE, 0, 0 }
};
//+---------------------------------------------------------------------------
//
// Function: MatchFileDesc
//
// Synopsis: Matches the given filename to an entry in FileDesc, if
// possible.
//
// Arguments: [pszFile] -- File to match
//
// Returns: A FILEDESC structure. If a match was not found the data
// in the FILEDESC will be empty.
//
//----------------------------------------------------------------------------
FILEDESC *
MatchFileDesc(LPSTR pszFile)
{
LPSTR pszExt = strrchr(pszFile, '.');
FILEDESC *pfd;
AssertPathString(pszFile);
pfd = &FileDesc[0];
while (pfd->pszPattern != NULL) {
if (pfd->pszPattern[0] == '.') {
if (pszExt != NULL && !strcmp(pszExt, pfd->pszPattern))
break;
}
else
if (!strcmp(pszFile, pfd->pszPattern))
break;
pfd++;
}
return pfd;
}
//+---------------------------------------------------------------------------
//
// Function: InsertFileDB
//
// Synopsis: Adds a file to the database.
//
// Arguments: [DirDB] -- Directory containing the file
// [FileName] -- File to add
// [DateTime] -- Timestamp of file
// [Attr] -- File attributes (directory or file)
// [FileFlags] -- FILEDB flags
//
// Returns: New FILEREC of file
//
//----------------------------------------------------------------------------
PFILEREC
InsertFileDB(
PDIRREC DirDB,
LPSTR FileName,
ULONG DateTime,
USHORT Attr,
ULONG FileFlags)
{
PFILEREC FileDB, *FileDBNext;
LPSTR pszCommentToEOL = NULL;
AssertPathString(FileName);
if (Attr & FILE_ATTRIBUTE_DIRECTORY) {
if (!strcmp(FileName, ".")) {
return(NULL);
}
if (!strcmp(FileName, "..")) {
return(NULL);
}
assert(FileFlags == 0);
FileFlags = FILEDB_DIR;
}
else {
FILEDESC *pfd = MatchFileDesc(FileName);
DirDB->DirFlags |= pfd->DirFlags;
FileFlags |= pfd->FileFlags;
if (!pfd->fNeedFileRec) {
return (NULL);
}
pszCommentToEOL = pfd->pszCommentToEOL;
}
FileDBNext = &DirDB->Files;
while ((FileDB = *FileDBNext) != NULL) {
FileDBNext = &(*FileDBNext)->Next;
if (strcmp(FileName, FileDB->Name) == 0) {
BuildError(
"%s: ignoring second instance of %s\n",
DirDB->Name,
FileName);
return(NULL);
}
}
AllocMem(sizeof(FILEREC) + strlen(FileName), &FileDB, MT_FILEDB);
memset(FileDB, 0, sizeof(*FileDB));
SigCheck(FileDB->Sig = SIG_FILEREC);
CopyString(FileDB->Name, FileName, TRUE);
FileDB->CheckSum = CheckSum(FileDB->Name);
FileDB->DateTime = DateTime;
FileDB->Attr = Attr;
FileDB->Dir = DirDB;
FileDB->FileFlags = FileFlags;
FileDB->NewestDependency = FileDB;
FileDB->pszCommentToEOL = pszCommentToEOL;
if ((FileFlags & FILEDB_FILE_MISSING) == 0) {
AllDirsModified = TRUE;
}
*FileDBNext = FileDB;
return(FileDB);
}
//+---------------------------------------------------------------------------
//
// Function: DeleteUnscannedFiles
//
// Synopsis: Removes unscanned files (leaving scanned files and directories)
// from the Files list of the given directory
//
// Arguments: [DirDB] -- Directory to clean up
//
//----------------------------------------------------------------------------
VOID
DeleteUnscannedFiles(
PDIRREC DirDB
)
{
PFILEREC FileDB, *FileDBNext;
FileDBNext = &DirDB->Files;
while (FileDB = *FileDBNext) {
//
// If a file has the missing flag set then it doesn't exist. But for
// it to be in the list of files it has to be listed in a SOURCES line
// (or some equivalent). This means there is a SOURCEREC somewhere
// which is pointing to the FILEREC for that file, so we don't want to
// free its memory.
//
if ( (FileDB->FileFlags & (FILEDB_SCANNED | FILEDB_FILE_MISSING | FILEDB_SOURCEREC_EXISTS)) ||
(FileDB->Attr & FILE_ATTRIBUTE_DIRECTORY) ) {
FileDBNext = &FileDB->Next;
}
else {
FreeFileDB( FileDBNext );
AllDirsModified = TRUE;
}
}
}
//+---------------------------------------------------------------------------
//
// Function: InsertIncludeDB
//
// Synopsis: Inserts an include file into the database
//
// Arguments: [FileDB] -- File which includes this file
// [IncludeFileName] -- Name of include file
// [IncFlags] -- INCLUDEDB flags for this file
//
// Returns: INCLUDEREC of previously existing or new entry one.
//
//----------------------------------------------------------------------------
PINCLUDEREC
InsertIncludeDB(
PFILEREC FileDB,
LPSTR IncludeFileName,
USHORT IncFlags
)
{
PINCLUDEREC IncludeDB, *IncludeDBNext;
AssertPathString(IncludeFileName);
IncludeDBNext = &FileDB->IncludeFiles;
while (IncludeDB = *IncludeDBNext) {
AssertCleanTree(IncludeDB, FileDB); // Tree must be clean
if (!strcmp(IncludeDB->Name, IncludeFileName)) {
IncludeDB->IncFlags &= ~INCLUDEDB_GLOBAL;
IncludeDB->pfrInclude = NULL;
return(IncludeDB);
}
IncludeDBNext = &IncludeDB->Next;
}
AllocMem(
sizeof(INCLUDEREC) + strlen(IncludeFileName),
IncludeDBNext,
MT_INCLUDEDB);
IncludeDB = *IncludeDBNext;
memset(IncludeDB, 0, sizeof(*IncludeDB));
SigCheck(IncludeDB->Sig = SIG_INCLUDEREC);
IncludeDB->IncFlags = IncFlags;
CopyString(IncludeDB->Name, IncludeFileName, TRUE);
AllDirsModified = TRUE;
return(IncludeDB);
}
//+---------------------------------------------------------------------------
//
// Function: LinkToCycleRoot
//
//----------------------------------------------------------------------------
VOID
LinkToCycleRoot(INCLUDEREC *pirOrg, FILEREC *pfrCycleRoot)
{
INCLUDEREC *pir;
AllocMem(
sizeof(INCLUDEREC) + strlen(pfrCycleRoot->Name),
&pir,
MT_INCLUDEDB);
memset(pir, 0, sizeof(*pir));
SigCheck(pir->Sig = SIG_INCLUDEREC);
pir->IncFlags = INCLUDEDB_SNAPPED | INCLUDEDB_CYCLEALLOC;
pir->pfrInclude = pfrCycleRoot;
CopyString(pir->Name, pfrCycleRoot->Name, TRUE);
if (DEBUG_1) {
BuildMsgRaw(
"%x CycleAlloc %s\\%s <- %s\\%s\n",
pir,
pir->pfrInclude->Dir->Name,
pir->pfrInclude->Name,
pirOrg->pfrInclude->Dir->Name,
pirOrg->pfrInclude->Name);
}
MergeIncludeFiles(pirOrg->pfrInclude, pir, NULL);
assert((pir->IncFlags & INCLUDEDB_CYCLEORPHAN) == 0);
assert(pir->IncFlags & INCLUDEDB_CYCLEROOT);
}
//+---------------------------------------------------------------------------
//
// Function: MergeIncludeFiles
//
//----------------------------------------------------------------------------
VOID
MergeIncludeFiles(FILEREC *pfr, INCLUDEREC *pirList, FILEREC *pfrRoot)
{
INCLUDEREC *pirT;
INCLUDEREC *pir, **ppir;
while ((pirT = pirList) != NULL) {
pirList = pirList->NextTree;
pirT->NextTree = NULL;
assert(pirT->pfrInclude != NULL);
for (ppir = &pfr->IncludeFilesTree;
(pir = *ppir) != NULL;
ppir = &pir->NextTree) {
if (pirT->pfrInclude == pir->pfrInclude) {
if (pirT->IncFlags & INCLUDEDB_CYCLEROOT) {
RemoveFromCycleRoot(pirT, pfrRoot);
}
pirT->IncFlags |= INCLUDEDB_CYCLEORPHAN;
if (DEBUG_1) {
BuildMsgRaw(
"%x CycleOrphan %s\\%s <- %s\\%s\n",
pirT,
pirT->pfrInclude->Dir->Name,
pirT->pfrInclude->Name,
pfr->Dir->Name,
pfr->Name);
}
break;
}
}
if (*ppir == NULL) {
*ppir = pirT;
pirT->pfrCycleRoot = pfr;
pirT->IncFlags |= INCLUDEDB_CYCLEROOT;
if (DEBUG_1) {
BuildMsgRaw(
"%x CycleRoot %s\\%s <- %s\\%s\n",
pirT,
pirT->pfrInclude->Dir->Name,
pirT->pfrInclude->Name,
pirT->pfrCycleRoot->Dir->Name,
pirT->pfrCycleRoot->Name);
}
}
}
if (fDebug & 16) {
PrintFileDB(stderr, pfr, 2);
}
}
//+---------------------------------------------------------------------------
//
// Function: RemoveFromCycleRoot
//
//----------------------------------------------------------------------------
VOID
RemoveFromCycleRoot(INCLUDEREC *pir, FILEREC *pfrRoot)
{
INCLUDEREC **ppir;
assert(pir->pfrCycleRoot != NULL);
// if pfrRoot was passed in, the caller knows it's on pfrRoot's list,
// and is already dealing with the linked list without our help.
if (pfrRoot != NULL) {
assert((pir->IncFlags & INCLUDEDB_CYCLEALLOC) == 0);
assert(pir->pfrCycleRoot == pfrRoot);
pir->pfrCycleRoot = NULL;
pir->IncFlags &= ~INCLUDEDB_CYCLEROOT;
if (DEBUG_1) {
BuildMsgRaw(
"%x CycleUnroot %s\\%s <- %s\\%s\n",
pir,
pir->pfrInclude->Dir->Name,
pir->pfrInclude->Name,
pfrRoot->Dir->Name,
pfrRoot->Name);
}
return;
}
ppir = &pir->pfrCycleRoot->IncludeFilesTree;
while (*ppir != NULL) {
if (*ppir == pir) {
*ppir = pir->NextTree; // remove from tree list
pir->NextTree = NULL;
pir->pfrCycleRoot = NULL;
pir->IncFlags &= ~INCLUDEDB_CYCLEROOT;
return;
}
ppir = &(*ppir)->NextTree;
}
BuildError(
"%s\\%s: %x %s: not on cycle root's list\n",
pir->pfrCycleRoot->Dir->Name,
pir->pfrCycleRoot->Name,
pir,
pir->Name);
assert(pir->pfrCycleRoot == NULL); // always asserts if loop exhausted
}
//+---------------------------------------------------------------------------
//
// Function: UnsnapIncludeFiles
//
// Synopsis: Removes pointers from INCLUDEREC to the actual FILEREC of
// the include file so we can 'resnap' them.
//
// Arguments: [pfr] -- FILEREC to unsnap
// [fUnsnapGlobal] -- If TRUE, global and local includes are
// unsnapped. Otherwise, just local ones are.
//
//----------------------------------------------------------------------------
VOID
UnsnapIncludeFiles(FILEREC *pfr, BOOL fUnsnapGlobal)
{
INCLUDEREC **ppir;
INCLUDEREC *pir;
// Dynamic Tree List:
// - no cycle orphans
// - cycle roots must belong to current file record
// - cycle allocs must be freed
AssertFile(pfr);
while (pfr->IncludeFilesTree != NULL) {
pir = pfr->IncludeFilesTree; // pick up next entry
AssertInclude(pir);
pfr->IncludeFilesTree = pir->NextTree; // remove from tree list
assert((pir->IncFlags & INCLUDEDB_CYCLEORPHAN) == 0);
if (pir->IncFlags & (INCLUDEDB_CYCLEROOT | INCLUDEDB_CYCLEALLOC)) {
// unsnap the record
pir->IncFlags &= ~(INCLUDEDB_SNAPPED | INCLUDEDB_GLOBAL);
pir->pfrInclude = NULL;
pir->NextTree = NULL;
}
if (pir->IncFlags & INCLUDEDB_CYCLEROOT) {
assert(pir->pfrCycleRoot == pfr);
pir->pfrCycleRoot = NULL;
pir->IncFlags &= ~INCLUDEDB_CYCLEROOT;
}
assert(pir->pfrCycleRoot == NULL);
if (pir->IncFlags & INCLUDEDB_CYCLEALLOC) {
pir->IncFlags &= ~INCLUDEDB_CYCLEALLOC;
assert(pir->Next == NULL);
FreeIncludeDB(&pir);
}
}
// Static List:
// - no cycle allocs
// - cycle roots must be removed from a different file's Dynamic list
// - cycle orphans are nops
for (ppir = &pfr->IncludeFiles; (pir = *ppir) != NULL; ppir = &pir->Next) {
assert((pir->IncFlags & INCLUDEDB_CYCLEALLOC) == 0);
if (pir->IncFlags & INCLUDEDB_CYCLEROOT) {
assert(pir->pfrCycleRoot != pfr);
RemoveFromCycleRoot(pir, NULL);
}
pir->IncFlags &= ~INCLUDEDB_CYCLEORPHAN;
if (pir->pfrInclude != NULL &&
(fUnsnapGlobal ||
(pir->pfrInclude->Dir->DirFlags & DIRDB_GLOBAL_INCLUDES) == 0)) {
// unsnap the record
pir->IncFlags &= ~(INCLUDEDB_SNAPPED | INCLUDEDB_GLOBAL);
pir->pfrInclude = NULL;
}
pir->NextTree = NULL;
}
}
#if DBG
//+---------------------------------------------------------------------------
//
// Function: AssertCleanTree
//
// Synopsis: Enforce that no include files are snapped.
//
// Arguments: [pir] - include record to test
// [pfr] - optional containing file record
//
//----------------------------------------------------------------------------
VOID
AssertCleanTree(INCLUDEREC *pir, OPTIONAL FILEREC *pfr)
{
if (IsCleanTree(pir)) {
return;
}
if (fAssertCleanTree) {
BuildMsgRaw("\n*************************************\n");
BuildMsgRaw("Persistent Cycle: pir=%x: %s\n", pir, pir->Name);
if (pfr != NULL) {
BuildMsgRaw(" pfr=%x: %s\n", pfr, pfr->Name);
if (pfr->Dir != NULL) {
BuildMsgRaw(" pdr=%x: %s\n", pfr->Dir, pfr->Dir->Name);
}
}
if (pir->pfrInclude != NULL) {
BuildMsgRaw(" pfrInclude=%x: %s\n", pir->pfrInclude, pir->pfrInclude->Name);
if (pir->pfrInclude->Dir != NULL) {
BuildMsgRaw(" pdrInclude=%x: %s\n", pir->pfrInclude->Dir, pir->pfrInclude->Dir->Name);
}
}
BuildMsgRaw("\n*************************************\n");
fflush(stdout);
fflush(stderr);
PrintAllDirs();
BuildMsgRaw("\n*************************************\n");
fflush(stdout);
fflush(stderr);
}
assert(IsCleanTree(pir));
}
#endif
//+---------------------------------------------------------------------------
//
// Function: UnsnapAllDirectories
//
// Synopsis: Removes pointers from all INCLUDERECs to the actual FILERECs
// of include files so we can 'resnap' them.
//
// Arguments: None
//----------------------------------------------------------------------------
VOID
UnsnapAllDirectories(VOID)
{
DIRREC *pdr;
UINT i;
GlobalSequence = LocalSequence = 0;
for (pdr = AllDirs; pdr != NULL; pdr = pdr->Next) {
FILEREC *pfr;
AssertDir(pdr);
// Clear unwanted flags on each directory
pdr->DirFlags &= ~(DIRDB_SCANNED |
DIRDB_PASS0NEEDED |
DIRDB_COMPILENEEDED |
DIRDB_NEW);
pdr->CountOfFilesToCompile = 0;
pdr->SourceLinesToCompile = 0;
pdr->CountOfPassZeroFiles = 0;
pdr->PassZeroLines = 0;
// Free all source records that point to missing files, because the
// file records may be freed when rescanning directories after pass 0.
if (pdr->pds != NULL)
{
for (i = 0; i < MAX_TARGET_MACHINES + 1; i++) {
SOURCEREC **ppsr;
SOURCEREC *psr;
ppsr = &pdr->pds->psrSourcesList[i];
while ((psr = *ppsr) != NULL)
{
if (psr->SrcFlags & SOURCEDB_FILE_MISSING)
{
FreeSourceDB(ppsr);
}
else
{
ppsr = &psr->psrNext;
}
}
}
}
// Clear out all snapped include files and sequence numbers
for (pfr = pdr->Files; pfr != NULL; pfr = pfr->Next) {
AssertFile(pfr);
UnsnapIncludeFiles(pfr, TRUE);
pfr->GlobalSequence = pfr->LocalSequence = 0;
}
}
}
//+---------------------------------------------------------------------------
//
// Function: MarkIncludeFileRecords
//
//----------------------------------------------------------------------------
VOID
MarkIncludeFileRecords(
PFILEREC FileDB
)
{
PINCLUDEREC IncludeDB, *IncludeDBNext;
IncludeDBNext = &FileDB->IncludeFiles;
while (IncludeDB = *IncludeDBNext) {
AssertCleanTree(IncludeDB, FileDB); // Tree must be clean
IncludeDB->pfrInclude = (PFILEREC) -1;
IncludeDBNext = &IncludeDB->Next;
}
}
//+---------------------------------------------------------------------------
//
// Function: DeleteIncludeFileRecords
//
//----------------------------------------------------------------------------
VOID
DeleteIncludeFileRecords(
PFILEREC FileDB
)
{
PINCLUDEREC IncludeDB, *IncludeDBNext;
IncludeDBNext = &FileDB->IncludeFiles;
while (IncludeDB = *IncludeDBNext) {
AssertCleanTree(IncludeDB, FileDB); // Tree must be clean
if (IncludeDB->pfrInclude == (PFILEREC) -1) {
FreeIncludeDB(IncludeDBNext);
}
else {
IncludeDBNext = &IncludeDB->Next;
}
}
}
//+---------------------------------------------------------------------------
//
// Function: FindIncludeFileDB
//
// Synopsis: Find the FILEREC for an include file that our compiland
// includes.
//
// Arguments: [pfrSource] -- FILEREC of file which includes the one
// we're looking for. Might be a header.
// [pfrCompiland] -- FILEREC of ultimate source file.
// [pdrBuild] -- DIRREC of directory being built
// [pszSourceDirectory] -- Name of machine-specific dir
// [IncludeDB] -- INCLUDEDB of include file we're looking
// for.
//
// Returns: FILEREC of include file, if found.
//
//----------------------------------------------------------------------------
PFILEREC
FindIncludeFileDB(
FILEREC *pfrSource,
FILEREC *pfrCompiland,
DIRREC *pdrBuild,
LPSTR pszSourceDirectory,
INCLUDEREC *IncludeDB)
{
DIRREC *pdr;
DIRREC *pdrMachine;
FILEREC *pfr;
UINT n;
AssertFile(pfrSource);
AssertFile(pfrCompiland);
AssertDir(pfrSource->Dir);
AssertDir(pfrCompiland->Dir);
AssertDir(pdrBuild);
assert(pfrSource->Dir->FindCount >= 1);
assert(pfrCompiland->Dir->FindCount >= 1);
assert(pdrBuild->FindCount >= 1);
AssertInclude(IncludeDB);
// The rules for #include "foo.h" and #include <foo.h> are:
// - "foo.h" searches in the directory of the source file that has the
// #include statement first, then falls into the INCLUDES= directories
// - <foo.h> simply searches the INCLUDES= directories
//
// - since makefile.def *always* passes -I. -ITargetMachines[i] first,
// that has to be handled here as well.
//
// - deal with #include <sys\types> and #include "..\foo\bar.h" by
// scanning those directories, too.
n = CountIncludeDirs;
pdrMachine = FindSourceDirDB(pdrBuild->Name, pszSourceDirectory, FALSE);
// If local ("foo.h"), search the current file's directory, too.
// The compiler also will search the directory of each higher level
// file in the include hierarchy, but we won't get quite so fancy here.
// Just search the directory of the current file and of the compiland.
//
// Skip these directories if they match the current build directory or
// the machine subdirectory, because that's handled below.
if (IncludeDB->IncFlags & INCLUDEDB_LOCAL) {
if (pfrCompiland->Dir != pdrBuild &&
pfrCompiland->Dir != pdrMachine &&
pfrCompiland->Dir != pfrSource->Dir) {
AddIncludeDir(pfrCompiland->Dir, &n);
}
if (pfrSource->Dir != pdrBuild && pfrSource->Dir != pdrMachine) {
AddIncludeDir(pfrSource->Dir, &n);
}
}
// Search the current target machine subdirectory of the build directory
// -- as per makefile.def
if (pdrMachine != NULL) {
AddIncludeDir(pdrMachine, &n);
}
// Search the current build directory -- as per makefile.def.
AddIncludeDir(pdrBuild, &n);
while (n--) {
pdr = IncludeDirs[n];
if (pdr == NULL) {
continue;
}
AssertDir(pdr);
assert(pdr->FindCount >= 1);
pfr = FindSourceFileDB(pdr, IncludeDB->Name, NULL);
if (pfr != NULL) {
if (DEBUG_1) {
BuildMsgRaw(
"Found include file %s\\%s\n",
pfr->Dir->Name,
pfr->Name);
}
return(pfr);
}
}
return(NULL);
}
//+---------------------------------------------------------------------------
//
// Function: SaveMasterDB
//
// Synopsis: Save the database to disk in build.dat
//
// Arguments: (none)
//
// Returns: TRUE if successful
//
//----------------------------------------------------------------------------
BOOL
SaveMasterDB(VOID)
{
PDIRREC DirDB, *DirDBNext;
PFILEREC FileDB, *FileDBNext;
PINCLUDEREC IncludeDB, *IncludeDBNext;
FILE *fh;
if (!AllDirsModified) {
return(TRUE);
}
if (!(fh = fopen(DbMasterName, "wb"))) {
return( FALSE );
}
setvbuf(fh, NULL, _IOFBF, 0x7000);
BuildMsg("Saving %s...", DbMasterName);
AllDirsModified = FALSE;
DirDBNext = &AllDirs;
fprintf(fh, "V %x\r\n", BUILD_VERSION);
while (DirDB = *DirDBNext) {
fprintf(fh, "D \"%s\" %x\r\n", DirDB->Name, DirDB->DirFlags);
FileDBNext = &DirDB->Files;
while (FileDB = *FileDBNext) {
if ((FileDB->FileFlags & FILEDB_FILE_MISSING) == 0) {
fprintf(
fh,
" F \"%s\" %x %x %lx %u %u\r\n",
FileDB->Name,
FileDB->FileFlags,
FileDB->Attr,
FileDB->DateTime,
FileDB->SourceLines,
FileDB->Version);
}
IncludeDBNext = &FileDB->IncludeFiles;
while (IncludeDB = *IncludeDBNext) {
fprintf(
fh,
" I \"%s\" %x %u\r\n",
IncludeDB->Name,
IncludeDB->IncFlags,
IncludeDB->Version);
IncludeDBNext= &IncludeDB->Next;
}
FileDBNext = &FileDB->Next;
}
fprintf(fh, "\r\n");
DirDBNext = &DirDB->Next;
}
fclose(fh);
BuildMsgRaw(szNewLine);
return(TRUE);
}
//+---------------------------------------------------------------------------
//
// Function: LoadMasterDB
//
// Synopsis: Load the master database from build.dat
//
// Arguments: (none)
//
//----------------------------------------------------------------------------
void
LoadMasterDB( void )
{
PDIRREC DirDB, *DirDBNext;
PFILEREC FileDB, *FileDBNext;
PINCLUDEREC IncludeDB, *IncludeDBNext;
FILE *fh;
LPSTR s;
char ch, ch2;
BOOL fFirst = TRUE;
UINT Version;
LPSTR pszerr = NULL;
AllDirs = NULL;
AllDirsModified = FALSE;
AllDirsInitialized = FALSE;
if (!SetupReadFile("", DbMasterName, ";", &fh)) {
return;
}
BuildMsg("Loading %s...", DbMasterName);
DirDBNext = &AllDirs;
FileDBNext = NULL;
IncludeDBNext = NULL;
while ((s = ReadLine(fh)) != NULL) {
ch = *s++;
if (ch == '\0') {
continue; // skip empty lines
}
ch2 = *s++; // should be a blank
if (ch2 == '\0') {
pszerr = "missing field";
break; // fail on single character lines
}
if (fFirst) {
if (ch != 'V' || ch2 != ' ' || !AToX(&s, &Version)) {
pszerr = "bad version format";
break;
}
if (Version != BUILD_VERSION) {
break;
}
fFirst = FALSE;
continue;
}
if (ch2 != ' ') {
pszerr = "bad separator";
break;
}
if (ch == 'D') {
DirDB = LoadMasterDirDB(s);
if (DirDB == NULL) {
pszerr = "Directory error";
break;
}
*DirDBNext = DirDB;
DirDBNext = &DirDB->Next;
FileDBNext = &DirDB->Files;
IncludeDBNext = NULL;
}
else
if (ch == 'F' && FileDBNext != NULL) {
FileDB = LoadMasterFileDB(s);
if (FileDB == NULL) {
pszerr = "File error";
break;
}
*FileDBNext = FileDB;
FileDBNext = &FileDB->Next;
FileDB->Dir = DirDB;
IncludeDBNext = &FileDB->IncludeFiles;
}
else
if (ch == 'I' && IncludeDBNext != NULL) {
IncludeDB = LoadMasterIncludeDB(s);
if (IncludeDB == NULL) {
pszerr = "Include error";
break;
}
*IncludeDBNext = IncludeDB;
IncludeDBNext = &IncludeDB->Next;
}
else {
pszerr = "bad entry type";
break;
}
}
if (s != NULL) {
if (pszerr == NULL) {
BuildMsgRaw(" - old version - recomputing.\n");
} else {
BuildMsgRaw(szNewLine);
BuildError("corrupt database (%s)\n", pszerr);
}
FreeAllDirs();
}
else {
BuildMsgRaw(szNewLine);
AllDirsInitialized = TRUE;
}
CloseReadFile(NULL);
return;
}
//+---------------------------------------------------------------------------
//
// Function: LoadMasterDirDB
//
// Synopsis: Load a directory entry from build.dat
//
// Arguments: [s] -- line containing text from file.
//
// Returns: DIRRECT
//
//----------------------------------------------------------------------------
PDIRREC
LoadMasterDirDB(
LPSTR s
)
{
PDIRREC DirDB;
LPSTR DirName;
ULONG DirFlags;
if (*s == '"') {
s++;
DirName = s;
while (*s != '"') {
s++;
}
*s++ = '\0';
}
else
{
DirName = s;
while (*s > ' ') {
s++;
}
}
*s++ = '\0';
if (!AToX(&s, &DirFlags)) {
return(NULL);
}
AllocMem(sizeof(DIRREC) + strlen(DirName), &DirDB, MT_DIRDB);
memset(DirDB, 0, sizeof(*DirDB));
SigCheck(DirDB->Sig = SIG_DIRREC);
DirDB->DirFlags = DirFlags & DIRDB_DBPRESERVE;
CopyString(DirDB->Name, DirName, TRUE);
DirDB->CheckSum = CheckSum(DirDB->Name);
return( DirDB );
}
//+---------------------------------------------------------------------------
//
// Function: LoadMasterFileDB
//
// Synopsis: Load a file entry from build.dat
//
// Arguments: [s] -- line containing text from file
//
// Returns: FILEREC
//
//----------------------------------------------------------------------------
PFILEREC
LoadMasterFileDB(
LPSTR s
)
{
PFILEREC FileDB;
LPSTR FileName;
ULONG Version;
ULONG FileFlags;
ULONG Attr;
ULONG SourceLines;
ULONG DateTime;
FILEDESC *pfd;
if (*s == '"') {
s++;
FileName = s;
while (*s != '"') {
s++;
}
*s++ = '\0';
}
else
{
FileName = s;
while (*s > ' ') {
s++;
}
}
*s++ = '\0';
if (!AToX(&s, &FileFlags) ||
!AToX(&s, &Attr) ||
!AToX(&s, &DateTime) ||
!AToD(&s, &SourceLines) ||
!AToD(&s, &Version) ||
strchr(FileName, '/') != NULL ||
strchr(FileName, '\\') != NULL) {
return(NULL);
}
AllocMem(sizeof(FILEREC) + strlen(FileName), &FileDB, MT_FILEDB);
memset(FileDB, 0, sizeof(*FileDB));
SigCheck(FileDB->Sig = SIG_FILEREC);
CopyString(FileDB->Name, FileName, TRUE);
FileDB->CheckSum = CheckSum(FileDB->Name);
FileDB->FileFlags = FileFlags & FILEDB_DBPRESERVE;
FileDB->Attr = (USHORT) Attr;
FileDB->DateTime = DateTime;
FileDB->Version = (USHORT) Version;
FileDB->SourceLines = SourceLines;
FileDB->NewestDependency = FileDB;
pfd = MatchFileDesc(FileDB->Name);
FileDB->pszCommentToEOL = pfd->pszCommentToEOL;
return(FileDB);
}
//+---------------------------------------------------------------------------
//
// Function: LoadMasterIncludeDB
//
// Synopsis: Loads an include file entry from build.dat
//
// Arguments: [s] -- line containing text from file.
//
// Returns: INCLUDEREC
//
//----------------------------------------------------------------------------
PINCLUDEREC
LoadMasterIncludeDB(
LPSTR s
)
{
PINCLUDEREC IncludeDB;
LPSTR FileName;
ULONG Version;
ULONG IncFlags;
if (*s == '"') {
s++;
FileName = s;
while (*s != '"') {
s++;
}
*s++ = '\0';
}
else
{
FileName = s;
while (*s > ' ') {
s++;
}
}
*s++ = '\0';
if (!AToX(&s, &IncFlags) || !AToD(&s, &Version)) {
return(NULL);
}
AllocMem(
sizeof(INCLUDEREC) + strlen(FileName),
&IncludeDB,
MT_INCLUDEDB);
memset(IncludeDB, 0, sizeof(*IncludeDB));
SigCheck(IncludeDB->Sig = SIG_INCLUDEREC);
IncludeDB->IncFlags = (USHORT) (IncFlags & INCLUDEDB_DBPRESERVE);
IncludeDB->Version = (USHORT) Version;
CopyString(IncludeDB->Name, FileName, TRUE);
return(IncludeDB);
}