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

816 lines
24 KiB
C

//+---------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1989 - 1994
//
// File: buildscn.c
//
// Contents: Directory and File scanning functions for Build.exe
//
//
// History: 16-May-89 SteveWo Created
// ... see SLM logs
// 26-Jul-94 LyleC Cleanup/Add pass0 support
//
//----------------------------------------------------------------------------
#include "build.h"
//+---------------------------------------------------------------------------
//
// Function: AddShowDir
//
// Synopsis: Add a directory to the ShowDir array
//
//----------------------------------------------------------------------------
VOID
AddShowDir(DIRREC *pdr)
{
AssertDir(pdr);
if (CountShowDirs >= MAX_BUILD_DIRECTORIES) {
static BOOL fError = FALSE;
if (!fError) {
BuildError(
"Show Directory table overflow, using first %u entries\n",
MAX_BUILD_DIRECTORIES);
fError = TRUE;
}
}
else {
ShowDirs[CountShowDirs++] = pdr;
}
pdr->DirFlags |= DIRDB_SHOWN;
}
//+---------------------------------------------------------------------------
//
// Function: AddIncludeDir
//
// Synopsis: Add a directory to the IncludeDirs array
//
//----------------------------------------------------------------------------
VOID
AddIncludeDir(DIRREC *pdr, UINT *pui)
{
AssertDir(pdr);
assert(pdr->FindCount >= 1);
assert(*pui <= MAX_INCLUDE_DIRECTORIES);
if (*pui >= MAX_INCLUDE_DIRECTORIES) {
BuildError(
"Include Directory table overflow, %u entries allowed\n",
MAX_INCLUDE_DIRECTORIES);
exit(16);
}
IncludeDirs[(*pui)++] = pdr;
}
//+---------------------------------------------------------------------------
//
// Function: ScanGlobalIncludeDirectory
//
// Synopsis: Scans a global include directory and adds it to the
// IncludeDir array.
//
//----------------------------------------------------------------------------
VOID
ScanGlobalIncludeDirectory(LPSTR path)
{
DIRREC *pdr;
if ((pdr = ScanDirectory(path)) != NULL) {
AddIncludeDir(pdr, &CountIncludeDirs);
pdr->DirFlags |= DIRDB_GLOBAL_INCLUDES;
if (fShowTreeIncludes && !(pdr->DirFlags & DIRDB_SHOWN)) {
AddShowDir(pdr);
}
}
}
//+---------------------------------------------------------------------------
//
// Function: ScanIncludeEnv
//
// Synopsis: Scans all include directories specified in the INCLUDE
// environment variable.
//
// Arguments: [IncludeEnv] -- value of the INCLUDE environment variable.
//
// Notes: The INCLUDE variable is a string with a list of directories
// separated by semicolons (;).
//
//----------------------------------------------------------------------------
VOID
ScanIncludeEnv(
LPSTR IncludeEnv
)
{
char path[DB_MAX_PATH_LENGTH] = {0};
LPSTR IncDir, IncDirEnd;
UINT cb;
if (!IncludeEnv) {
return;
}
if (DEBUG_1) {
BuildMsgRaw("ScanIncludeEnv(%s)\n", IncludeEnv);
}
IncDir = IncludeEnv;
while (*IncDir) {
IncDir++;
}
while (IncDir > IncludeEnv) {
IncDirEnd = IncDir;
while (IncDir > IncludeEnv) {
if (*--IncDir == ';') {
break;
}
}
if (*IncDir == ';') {
if (cb = (UINT)(IncDirEnd-IncDir-1)) {
strncpy( path, IncDir+1, cb );
}
} else {
if (cb = (UINT)(IncDirEnd-IncDir)) {
strncpy( path, IncDir, cb );
}
}
if (cb) {
path[ cb ] = '\0';
while (path[ 0 ] == ' ') {
strcpy( path, &path[ 1 ] );
cb--;
}
while (cb && path[--cb] == ' ') {
path[ cb ] = '\0';
}
if (path[0]) {
ScanGlobalIncludeDirectory(path);
}
}
}
}
//+---------------------------------------------------------------------------
//
// Function: ScanSubDir
//
// Synopsis: Scans all the files in the given directory, sets the
// directory flags appropriately (e.g. DIRDB_SOURCES, etc),
// and adds a list of interesting files to the Files list in
// the DirDB structure for the directory.
//
// Arguments: [pszDir] -- Name of directory to scan
// [pdr] -- [out] Filled in DIRREC on return
//
// Notes: An 'interesting' file is one which has a recognized
// extension. See the InsertFileDB and MatchFileDesc
// functions for more info.
//
//----------------------------------------------------------------------------
VOID
ScanSubDir(LPSTR pszDir, DIRREC *pdr)
{
char FileName[DB_MAX_PATH_LENGTH];
FILEREC *FileDB, **FileDBNext;
WIN32_FIND_DATA FindFileData;
HDIR FindHandle;
ULONG DateTime;
USHORT Attr;
strcat(pszDir, "\\");
strcat(pszDir, "*.*");
pdr->DirFlags |= DIRDB_SCANNED;
FindHandle = FindFirstFile(pszDir, &FindFileData);
if (FindHandle == (HDIR)INVALID_HANDLE_VALUE) {
if (DEBUG_1) {
BuildMsg("FindFirstFile(%s) failed.\n", pszDir);
}
return;
}
do {
Attr = (USHORT)(FindFileData.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY);
if ((Attr & FILE_ATTRIBUTE_DIRECTORY) &&
(!strcmp(FindFileData.cFileName, ".") ||
!strcmp(FindFileData.cFileName, ".."))) {
continue;
}
CopyString(FileName, FindFileData.cFileName, TRUE);
FileTimeToDosDateTime(&FindFileData.ftLastWriteTime,
((LPWORD) &DateTime) + 1,
(LPWORD) &DateTime);
if ((pdr->DirFlags & DIRDB_NEW) == 0 &&
(FileDB = LookupFileDB(pdr, FileName)) != NULL) {
if (FileDB->FileFlags & FILEDB_PASS0)
pdr->DirFlags |= DIRDB_PASS0;
//
// Clear the file missing flag, since we know the file exists now.
// This flag may be set if the file was generated during pass zero.
//
if (FileDB->FileFlags & FILEDB_FILE_MISSING)
FileDB->FileFlags &= ~FILEDB_FILE_MISSING;
//
// The time we last stored for this file is different than the
// actual time on the file, so force it to be rescanned.
//
if (FileDB->DateTime != DateTime) {
if (FileDB->FileFlags & (FILEDB_SOURCE | FILEDB_HEADER)) {
FileDB->FileFlags &= ~FILEDB_SCANNED;
}
else {
FileDB->FileFlags |= FILEDB_SCANNED;
}
if (DEBUG_1) {
BuildMsg(
"%s - DateTime (%lx != %lx)\n",
FileDB->Name,
FileDB->DateTime,
DateTime);
}
FileDB->DateTime = DateTime;
FileDB->Attr = Attr;
}
else {
FileDB->FileFlags |= FILEDB_SCANNED;
}
}
else {
FileDB = InsertFileDB(pdr, FileName, DateTime, Attr, 0);
}
} while (FindNextFile(FindHandle, &FindFileData));
FindClose(FindHandle);
if ((pdr->DirFlags & DIRDB_DIRS) && (pdr->DirFlags & DIRDB_SOURCES))
{
BuildError("%s\\sources. unexpected in directory with DIRS file\n",
pdr->Name);
BuildError("Ignoring %s\\sources.\n", pdr->Name);
pdr->DirFlags &= ~DIRDB_SOURCES;
}
//
// Scan each file in this directory unless using QuickZero
//
if (fQuickZero && fFirstScan)
{
return;
}
FileDBNext = &pdr->Files;
while (FileDB = *FileDBNext) {
if (!(FileDB->FileFlags & (FILEDB_DIR | FILEDB_SCANNED))) {
if (ScanFile(FileDB)) {
AllDirsModified = TRUE;
}
}
FileDBNext = &FileDB->Next;
}
DeleteUnscannedFiles(pdr);
}
//+---------------------------------------------------------------------------
//
// Function: ScanDirectory
//
// Synopsis: Tries to find the given directory in the database, and if
// not found calls ScanSubDir.
//
// Arguments: [pszDir] -- Directory to scan
//
// Returns: Filled in DIRREC structure for the directory.
//
// Notes: If fQuicky (-z or -Z options) are set, then instead of calling
// ScanSubDir, which is long, it just checks for known files, like
// 'sources' for 'makefile' to determine whether or not the
// directory should be compiled.
//
//----------------------------------------------------------------------------
PDIRREC
ScanDirectory(LPSTR pszDir)
{
DIRREC *pdr;
char FullPath[DB_MAX_PATH_LENGTH];
if (DEBUG_1) {
BuildMsgRaw("ScanDirectory(%s)\n", pszDir);
}
if (!CanonicalizePathName(pszDir, CANONICALIZE_DIR, FullPath)) {
if (DEBUG_1) {
BuildMsgRaw("CanonicalizePathName failed\n");
}
return(NULL);
}
pszDir = FullPath;
if ((pdr = LoadDirDB(pszDir)) == NULL) {
return(NULL);
}
if (fQuicky && (!fQuickZero)) {
if (!(pdr->DirFlags & DIRDB_SCANNED)) {
pdr->DirFlags |= DIRDB_SCANNED;
if (ProbeFile(pdr->Name, "sources") != -1) {
pdr->DirFlags |= DIRDB_SOURCES | DIRDB_MAKEFILE;
}
else
if (ProbeFile(pdr->Name, "mydirs") != -1 ||
ProbeFile(pdr->Name, "dirs") != -1 ||
ProbeFile(pdr->Name, pszTargetDirs) != -1) {
pdr->DirFlags |= DIRDB_DIRS;
if (ProbeFile(pdr->Name, "makefil0") != -1) {
pdr->DirFlags |= DIRDB_MAKEFIL0;
}
if (ProbeFile(pdr->Name, "makefil1") != -1) {
pdr->DirFlags |= DIRDB_MAKEFIL1;
}
if (ProbeFile(pdr->Name, "makefile") != -1) {
pdr->DirFlags |= DIRDB_MAKEFILE;
}
}
}
return(pdr);
}
if (pdr->DirFlags & DIRDB_SCANNED) {
return(pdr);
}
if (!RecurseLevel && fNoisyScan) {
ClearLine();
BuildMsgRaw(" Scanning %s ", pszDir);
if (fDebug || !(BOOL) _isatty(_fileno(stderr))) {
BuildMsgRaw(szNewLine);
}
}
ScanSubDir(pszDir, pdr);
if (!RecurseLevel) {
ClearLine();
}
return(pdr);
}
#define BUILD_TLIB_INCLUDE_STMT "importlib"
#define BUILD_TLIB_INCLUDE_STMT_LENGTH (sizeof( BUILD_TLIB_INCLUDE_STMT )-1)
#define BUILD_MIDL_INCLUDE_STMT "import"
#define BUILD_MIDL_INCLUDE_STMT_LENGTH (sizeof( BUILD_MIDL_INCLUDE_STMT )-1)
#define BUILD_RC_INCLUDE_STMT "rcinclude"
#define BUILD_RC_INCLUDE_STMT_LENGTH (sizeof( BUILD_RC_INCLUDE_STMT )-1)
#define BUILD_ASN_INCLUDE_STMT "--<"
#define BUILD_ASN_INCLUDE_STMT_LENGTH (sizeof( BUILD_ASN_INCLUDE_STMT )-1)
#define BUILD_INCLUDE_STMT "include"
#define BUILD_INCLUDE_STMT_LENGTH (sizeof( BUILD_INCLUDE_STMT )-1)
#define BUILD_VER_COMMENT "/*++ BUILD Version: "
#define BUILD_VER_COMMENT_LENGTH (sizeof( BUILD_VER_COMMENT )-1)
#define BUILD_MASM_VER_COMMENT ";;;; BUILD Version: "
#define BUILD_MASM_VER_COMMENT_LENGTH (sizeof( BUILD_MASM_VER_COMMENT )-1)
//+---------------------------------------------------------------------------
//
// Function: IsIncludeStatement
//
// Synopsis: Tries to determine whether or not a given line contains an
// include statement (e.g. #include <foobar.h> ).
//
// Arguments: [pfr] -- FILEREC of file being scanned
// [p] -- Current line of file
//
// Returns: NULL if line is not an include statment. Returns pointer to
// beginning of filename if it is (e.g. <foobar.h> ).
//
// Notes: The returned filename includes the surrounding quotes or
// brackets, if any. Also, the pointer is just a pointer into
// the given string, not a separate copy.
//
// Supported statements are:
// All file types: #include <filename> and #include "filename"
// MIDL files: import "filename"
// RC files: rcinclude filename
// MKTYPLIB files: importlib("filename")
//
//----------------------------------------------------------------------------
#define IsTokenPrefix0(psz, szToken, cchToken) \
(strncmp((psz), (szToken), (cchToken)) == 0)
#define IsTokenPrefix(psz, szToken, cchToken) \
(IsTokenPrefix0((psz), (szToken), (cchToken)) && \
(psz)[cchToken] != '\0')
#define IsTokenMatch(psz, szToken, cchToken) \
(IsTokenPrefix((psz), (szToken), (cchToken)) && \
!iscsym((psz)[cchToken]))
#define IsCiTokenPrefix0(psz, szToken, cchToken) \
(_strnicmp((psz), (szToken), (cchToken)) == 0)
#define IsCiTokenPrefix(psz, szToken, cchToken) \
(IsCiTokenPrefix0((psz), (szToken), (cchToken)) && \
(psz)[cchToken] != '\0')
#define IsCiTokenMatch(psz, szToken, cchToken) \
(IsCiTokenPrefix((psz), (szToken), (cchToken)) && \
!iscsym((psz)[cchToken]))
LPSTR
IsIncludeStatement(FILEREC *pfr, LPSTR p)
{
if (!p || *p == '\0')
return NULL;
if (!(pfr->FileFlags & (FILEDB_MASM | FILEDB_MIDL | FILEDB_MKTYPLIB | FILEDB_RC | FILEDB_ASN))) {
if (*p != '#') {
return(NULL);
}
}
if (*p == '#')
p++;
while (isspace(*p)) {
p++;
}
if (IsTokenMatch(p, BUILD_INCLUDE_STMT, BUILD_INCLUDE_STMT_LENGTH)) {
p += BUILD_INCLUDE_STMT_LENGTH;
}
else
if ((pfr->FileFlags & FILEDB_MASM) &&
IsCiTokenMatch(p, BUILD_INCLUDE_STMT, BUILD_INCLUDE_STMT_LENGTH)) {
p += BUILD_INCLUDE_STMT_LENGTH;
}
else
if ((pfr->FileFlags & FILEDB_MIDL) &&
IsTokenMatch(p, BUILD_MIDL_INCLUDE_STMT, BUILD_MIDL_INCLUDE_STMT_LENGTH)) {
p += BUILD_MIDL_INCLUDE_STMT_LENGTH;
}
else
if ((pfr->FileFlags & FILEDB_RC) &&
IsTokenMatch(p, BUILD_RC_INCLUDE_STMT, BUILD_RC_INCLUDE_STMT_LENGTH)) {
p += BUILD_RC_INCLUDE_STMT_LENGTH;
}
else
if ((pfr->FileFlags & FILEDB_ASN) &&
IsTokenPrefix0(p, BUILD_ASN_INCLUDE_STMT, BUILD_ASN_INCLUDE_STMT_LENGTH)) {
p += BUILD_ASN_INCLUDE_STMT_LENGTH;
}
else
if ((pfr->FileFlags & FILEDB_MKTYPLIB) &&
IsTokenMatch(p, BUILD_TLIB_INCLUDE_STMT, BUILD_TLIB_INCLUDE_STMT_LENGTH)) {
p += BUILD_TLIB_INCLUDE_STMT_LENGTH;
while (isspace(*p)) {
p++;
}
if (*p == '(') // Skip the open paren and get to the quote.
p++;
}
else {
return(NULL);
}
while (isspace(*p)) {
p++;
}
return(p);
}
//+---------------------------------------------------------------------------
//
// Function: IsPragmaHdrStop
//
// Synopsis: Determines if the given line is a #pragma hdrstop line
//
// Arguments: [p] -- String to analyze
//
// Returns: TRUE if the line is a pragma hdrstop
//
//----------------------------------------------------------------------------
BOOL
IsPragmaHdrStop(LPSTR p)
{
static char szPragma[] = "pragma";
static char szHdrStop[] = "hdrstop";
if (*p == '#') {
while (*++p == ' ') {
;
}
if (strncmp(p, szPragma, sizeof(szPragma) - 1) == 0 &&
*(p += sizeof(szPragma) - 1) == ' ') {
while (*p == ' ') {
p++;
}
if (strncmp(p, szHdrStop, sizeof(szHdrStop) - 1) == 0 &&
!iscsym(p[sizeof(szHdrStop) - 1])) {
return(TRUE);
}
}
}
return(FALSE);
}
//+---------------------------------------------------------------------------
//
// Function: ScanFile
//
// Synopsis: Scans the given file to determine files which it includes.
//
// Arguments: [FileDB] -- File to scan.
//
// Returns: TRUE if successful
//
// Notes: This function is a nop if the given file does not have either
// the FILEDB_SOURCE or FILEDB_HEADER flag set.
// (see InsertSourceDB)
//
// Note that the performance of this function is critical since
// it is called for every file in each directory.
//
//----------------------------------------------------------------------------
#define ASN_NONE 0 // not in Asn INCLUDES statement
#define ASN_START 1 // expectimg "INCLUDES" token
#define ASN_FILENAME 2 // expecting a quoted "filename"
#define ASN_COMMA 3 // expecting end token (">--") or comma
#define ASN_CONTINUATION 8 // expecting comment token first
char *
AsnStateToString(UINT AsnState)
{
static char buf[100];
char *psz;
switch (AsnState & ~ASN_CONTINUATION) {
case ASN_NONE: psz = "None"; break;
case ASN_START: psz = "Start"; break;
case ASN_FILENAME: psz = "Filename"; break;
case ASN_COMMA: psz = "Comma"; break;
default: psz = "???"; break;
}
sprintf(buf, "%s%s", psz, (AsnState & ASN_CONTINUATION)? "+Cont" : "");
return(buf);
}
BOOL
ScanFile(
PFILEREC FileDB
)
{
FILE *FileHandle;
char CloseQuote;
LPSTR p;
LPSTR IncludeFileName, TextLine;
BOOL fFirst = TRUE;
USHORT IncFlags = 0;
UINT i, cline;
UINT AsnState = ASN_NONE;
if ((FileDB->FileFlags & (FILEDB_SOURCE | FILEDB_HEADER)) == 0) {
FileDB->FileFlags |= FILEDB_SCANNED;
return(TRUE);
}
//
// Don't scan non-pass-zero files if we're doing pass zero.
//
if (fPassZero && (FileDB->FileFlags & FILEDB_PASS0) == 0)
return TRUE;
if (!SetupReadFile(
FileDB->Dir->Name,
FileDB->Name,
FileDB->pszCommentToEOL,
&FileHandle)) {
return(FALSE);
}
if (!RecurseLevel && fNoisyScan) {
ClearLine();
BuildMsgRaw(
" Scanning %s ",
FormatPathName(FileDB->Dir->Name, FileDB->Name));
if (!(BOOL) _isatty(_fileno(stderr))) {
BuildMsgRaw(szNewLine);
}
}
FileDB->SourceLines = 0;
FileDB->Version = 0;
MarkIncludeFileRecords( FileDB );
FileDB->FileFlags |= FILEDB_SCANNED;
AllDirsModified = TRUE;
while ((TextLine = ReadLine(FileHandle)) != NULL) {
if (fFirst) {
fFirst = FALSE;
if (FileDB->FileFlags & FILEDB_HEADER) {
if (FileDB->FileFlags & FILEDB_MASM) {
if (!strncmp( TextLine,
BUILD_MASM_VER_COMMENT,
BUILD_MASM_VER_COMMENT_LENGTH)) {
FileDB->Version = (USHORT)
atoi( TextLine + BUILD_MASM_VER_COMMENT_LENGTH);
}
}
else
if (!strncmp( TextLine,
BUILD_VER_COMMENT,
BUILD_VER_COMMENT_LENGTH)) {
FileDB->Version = (USHORT)
atoi( TextLine + BUILD_VER_COMMENT_LENGTH);
}
}
}
if (AsnState != ASN_NONE) {
p = TextLine;
}
else {
p = IsIncludeStatement(FileDB, TextLine);
}
if (p != NULL) {
USHORT IncFlagsNew = IncFlags;
if (FileDB->FileFlags & FILEDB_ASN) {
if (AsnState & ASN_CONTINUATION) {
if (p[0] != '-' || p[1] != '-') {
AsnState = ASN_NONE; // ignore includes and ...
p = NULL;
break; // get next line
}
p += 2;
AsnState &= ~ASN_CONTINUATION;
}
moreasn:
while (p != NULL) {
while (isspace(*p)) {
p++;
}
if (*p == '\0') {
AsnState |= ASN_CONTINUATION;
goto nextline; // get next line
}
switch (AsnState) {
case ASN_NONE:
AsnState = ASN_START;
continue; // re-enter state machine
case ASN_START:
if (!IsTokenPrefix0(
p,
"INCLUDES",
sizeof("INCLUDES") - 1)) {
goto terminate;
}
AsnState = ASN_FILENAME;
p += sizeof("INCLUDES") - 1;
continue; // re-enter state machine
case ASN_FILENAME:
if (*p != '"') {
goto terminate;
}
AsnState = ASN_COMMA;
goto parsefilename;
case ASN_COMMA:
if (*p == '>' && p[1] == '-' && p[2] == '-') {
goto terminate;
}
if (*p != ',') {
goto terminate;
}
p++;
AsnState = ASN_FILENAME;
continue; // re-enter state machine
}
assert(FALSE); // Bad AsnState
terminate:
AsnState = ASN_NONE; // ignore includes statement, & ...
nextline:
p = NULL; // get next line
break;
}
}
parsefilename:
if (p != NULL) {
CloseQuote = (UCHAR) 0xff;
if (*p == '<') {
p++;
CloseQuote = '>';
}
else
if (*p == '"') {
p++;
IncFlagsNew |= INCLUDEDB_LOCAL;
CloseQuote = '"';
}
else
if (FileDB->FileFlags & FILEDB_MASM) {
IncFlagsNew |= INCLUDEDB_LOCAL;
CloseQuote = ';';
}
IncludeFileName = p;
while (*p != '\0' && *p != CloseQuote && *p != ' ') {
p++;
}
if (CloseQuote == ';' && (*p == ' ' || *p == '\0')) {
CloseQuote = *p;
}
if (*p != CloseQuote || CloseQuote == (UCHAR) 0xff) {
BuildError(
"%s - invalid include statement: %s\n",
FormatPathName(FileDB->Dir->Name, FileDB->Name),
TextLine);
break;
}
*p = '\0';
CopyString(IncludeFileName, IncludeFileName, TRUE);
for (i = 0; i < CountExcludeIncs; i++) {
if (!strcmp(IncludeFileName, ExcludeIncs[i])) {
IncludeFileName = NULL;
break;
}
}
if (IncludeFileName != NULL) {
InsertIncludeDB(FileDB, IncludeFileName, IncFlagsNew);
}
if (FileDB->FileFlags & FILEDB_ASN) {
p++;
goto moreasn;
}
}
}
else
if (IncFlags == 0 &&
(FileDB->FileFlags & (FILEDB_ASM | FILEDB_MASM | FILEDB_MIDL | FILEDB_ASN | FILEDB_RC | FILEDB_HEADER)) == 0 &&
IsPragmaHdrStop(TextLine)) {
IncFlags = INCLUDEDB_POST_HDRSTOP;
}
}
CloseReadFile(&cline);
FileDB->SourceLines = cline;
DeleteIncludeFileRecords( FileDB );
if (!RecurseLevel) {
ClearLine();
}
return(TRUE);
}