Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1490 lines
37 KiB

/*-----------------------------------------------------------------------------
Name: mkdep.c
Description:
Determine file dependencies
To Build:
cl /Ox /W3 mkdep.c
Revision History:
brendand (8/3/94) - Taken from GaryBu, merged files into a single unit
brendand (8/4/94) - Added .PCH and wild-card support
-----------------------------------------------------------------------------*/
// Includes -------------------------------------------------------------------
#define LINT_ARGS
#include <assert.h>
#include <ctype.h>
#include <io.h>
#include <malloc.h>
#include <process.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
// Types and Constants --------------------------------------------------------
#ifndef CDECL
#define CDECL
#endif
#ifndef CONST
#define CONST
#endif
#ifndef STATIC
#define STATIC static
#endif
#ifndef Assert
#define Assert(f) assert(f)
#endif
#define TRUE 1
#define FALSE 0
#define FOREVER while(1)
#define BLOCK
#define VOID void
#ifdef D86
#define szROText "rt"
#define szRWText "r+t"
#define szWOText "wt"
#define szROBin "rb"
#define szRWBin "r+b"
#define szWOBin "wb"
#endif
typedef int BOOL;
typedef char* SZ;
typedef unsigned char BYTE;
typedef BYTE* PB;
typedef unsigned short WORD;
typedef WORD* PW;
typedef unsigned long LONG;
#define lpbNull ((PB) NULL)
#define LOWORD(l) ((WORD)l)
#define HIWORD(l) ((WORD)(((LONG)l >> 16) & 0xffff))
#define LOBYTE(w) ((BYTE)w)
#define HIBYTE(w) (((WORD)w >> 8) & 0xff)
#define MAKEWORD(l,h) ((WORD)(l)|((WORD)(h)<<8))
#define MAKELONG(l,h) ((long)(((unsigned)l)|((unsigned long)((unsigned)h))<<16))
/* Args Record - MarkArgs, UnmarkArgs */
typedef struct
{
int cargArr;
SZ *pszArr;
} ARR;
/* drive usage types - getdt */
#define dtNil 0
#define dtLocal 1
#define dtUserNet 2
/* File attributes - getatr, setatr */
#define atrError 0xffff
#define atrReadOnly FILE_READONLY
#define atrHidden FILE_HIDDEN
#define atrSystem FILE_SYSTEM
#define atrVolume 0x08
#define atrDirectory FILE_DIRECTORY
#define atrArchive FILE_ARCHIVED
/* Macro for defining Linked list inertion */
#define AddToList(new,head,tail,link,null) { if(head==null) head=new; else tail->link = new; tail=new; new->link=null; }
/* & deletion */
#define DeleteFromList(item,head,tail,link,null,prev) { if(prev==null) head=item->link; else prev->link = item->link; \
if (tail==item) tail = prev; }
/* for MtimeOfFile() */
typedef long MTIME;
#define mtimeError ((MTIME) -1L)
typedef enum
{
langUnknown,
langC,
langAsm,
langRC
} LANG;
typedef struct _di
{
struct _di *pdiNext; /* next in list */
char *szPath; /* path name */
char *szName; /* full name */
BOOL fPathIsStd; /* name from standard includes (-I) */
} DI; /* dir info */
typedef struct _lk
{
struct _lk *plkNext; /* next in list */
struct _fi *pfi; /* file info for link */
} LK; /* File link */
typedef struct _fi
{
struct _fi *pfiNext; /* single link */
char *szPath; /* path name */
char *szName; /* full name */
LANG lang; /* language */
struct _lk *plkHead; /* included list */
struct _lk *plkTail; /* included list */
unsigned fIgnore:1; /* ignore: either -X and std include or -x <file> */
unsigned cout:15; /* output count */
} FI; /* file info */
typedef VOID (*PFN_ENUM)(char *, char *);
#define iszIncMax 40
char* szPrefix = "";
char* szSuffix = ".$O";
#define rmj 1
#define rmm 1
#define rup 0
#define szVerName "Forms3 Version"
// Globals --------------------------------------------------------------------
DI* pdiHead = NULL; /* stack of directories of files included */
FI* pfiHead = NULL;
FI* pfiTail = NULL;
WORD coutCur = 0;
int cchLine;
int iszIncMac = 0;
char* rgszIncPath[iszIncMax]; // actual path
char* rgszIncName[iszIncMax]; // name to output
BOOL fVerbose = FALSE;
BOOL fReplacePrefix = FALSE;
BOOL fNoGenHeaders = FALSE; // True if all header files must be present
BOOL fIgnoreStd = FALSE; // True if std include files should be ignored
BOOL fUseCurDir = FALSE; // When True: if a file doesn't exist and
// we are going to print a dependency for
// it, use the current directory rather
// than the directory of the source file.
char* szPrintDir = NULL; // If set, only print files in this dir.
char* szPCHFile = NULL; // .H which marks end of .PCH
// Prototypes -----------------------------------------------------------------
int main(int, char**);
VOID Usage(void);
char* SzIncludesC(char *, BOOL *), *SzIncludesAsm(char *), *SzIncludesRC(char *, BOOL *);
FI* PfiDependFn(char *, char *, BOOL, LANG, BOOL);
FI* PfiLookup(char *, char *, LANG);
FI* PfiAlloc(char *, char *, BOOL, LANG);
VOID FreeFi(FI *);
VOID AllocLk(FI *, FI *);
VOID FreeAllLk(FI *);
VOID StartReport(void);
VOID ContinueReport(void);
VOID EndReport(void);
VOID EndLine(void);
VOID Indent(void);
VOID Report(char *, char *);
VOID PrReverse(char *, char *);
BOOL FPrintFi(FI *);
VOID EnumChildren(FI *, PFN_ENUM, char *);
VOID Process(char *, BOOL);
VOID Fatal(char *);
SZ SzTransEnv(SZ);
VOID NormalizePath(SZ);
VOID MakeName(SZ, SZ, SZ);
VOID CopyPath(SZ, SZ);
VOID PushDir(char *, char *, BOOL);
VOID PopDir(void);
DI* PdiFromIdi(int);
int AddIncludeDir(char *);
VOID
Fatal(sz)
char *sz;
{
fprintf(stderr, "mkdep: error: %s\n", sz);
exit(1);
}
VOID
Usage()
{
if (rup == 0)
fprintf(stderr, "Mkdep V%d.%02d\n", rmj, rmm);
else
fprintf(stderr, "Mkdep V%d.%02d.%02d\n", rmj, rmm, rup);
fprintf(stderr,
"usage: mkdep [-v] [-r] [-n] [-X] [-C] [-I includeDir]*\n"
"\t[-p prefix] [-P replace_prefix] [-s suffix] \n"
"\t[-d file]* [-D printDir] files\n\n"
"\t-v Verbose\n"
"\t-r Reverse the dependencies that are output\n"
"\t-n Don't emit dependencies on files that don't now exist\n"
"\t-X Search, but don't print standard includes\n"
"\t-C If file doesn't exist, use .\\ not the directory of including file\n"
"\t-I Include directory to search for <> includes\n"
// "\t-J Search include directories from the INCLUDE environment variable\n"
"\t-p Prefix for all target-file names\n"
"\t-P Ditto, but first remove existing prefix from name\n"
"\t-s Suffix for all target-file names (default %s)\n"
"\t-d Search, but don't print named file\n"
"\t-D Only print files which are in named dir\n"
"\t-h Header which marks the end of the .PCH\n\n"
"A response file can be used by specifying '@filename' as an option.\n"
, szSuffix);
exit(1);
}
char **CmdArgs;
int cArgs;
int CurArg = 1;
FILE *pfileResponse = NULL;
char achBuf[256];
char * pBuf = NULL;
char *
GetNextArg()
{
char *pszTokens = " \t\n";
if (pfileResponse)
{
char * psz;
if (pBuf)
{
pBuf = strtok(NULL, pszTokens);
if (pBuf)
return pBuf;
}
do
{
psz = fgets(achBuf, 256, pfileResponse);
if (psz == NULL)
{
fclose(pfileResponse);
pfileResponse = NULL;
}
else if (achBuf[strlen(achBuf)-1] != '\n')
{
fclose(pfileResponse);
Fatal("Line too long in response file. Must be less "
"than 256 characters.");
}
else
{
pBuf = strtok(achBuf, pszTokens);
if (pBuf)
return pBuf;
}
} while (psz && !pBuf);
}
if (CurArg >= cArgs)
return NULL;
return CmdArgs[CurArg++];
}
#define FSwitchCh(ch) ((ch)=='-' || (ch) == '/' || (ch) == '@')
int
main(iszMax, rgsz)
int iszMax;
char *rgsz[];
{
BOOL fReverse = FALSE;
char *pszArg;
int i = 0;
if (iszMax == 1)
Usage();
CmdArgs = rgsz;
cArgs = iszMax;
/* Parse command line switches.
*/
while (((pszArg = GetNextArg()) != NULL) && FSwitchCh(pszArg[0]))
{
char chSwitch = pszArg[1];
if (pszArg[0] == '@')
{
if (pszArg[1] == '\0')
Usage();
pfileResponse = fopen(&pszArg[1], "rt");
if (!pfileResponse)
{
fprintf(stderr, "mkdep: error: Could not open response file "
"'%s'.\n", &pszArg[1]);
return(1);
}
continue;
}
// fprintf(stderr, "Arg %d: '%s' ", i++, pszArg);
switch (chSwitch)
{
case 'v':
fVerbose = TRUE;
break;
case 'r':
fReverse = TRUE;
break;
case 'n':
fNoGenHeaders = TRUE;
break;
case 'x':
case 'X':
fIgnoreStd = TRUE;
break;
case 'C':
fUseCurDir = TRUE;
break;
#if 0
case 'J':
{
SZ szInc = getenv("INCLUDE");
if (szInc)
{
char rgszDir[iszIncMax][_MAX_FNAME];
int nDirs,i;
char* psz;
// Convert embedded semicolons to blanks
for (psz=szInc; *psz; psz++)
if (*psz == ';')
*psz = ' ';
/* This is very bogus! a dynamic way of reading the dirs
should be done so up to iszIncMax dirs can be read. Also,
AddIncludeDir does not copy the strings and rgszDir is
an automatic variable!
*/
fprintf(stderr, "-J option: only first 16 include dirs parsed.\n");
nDirs =
sscanf(szInc,
"%s %s %s %s %s %s %s %s %s %s %s %s %s %s %s %s",
rgszDir[0],rgszDir[1],rgszDir[2],rgszDir[3],rgszDir[4],
rgszDir[5],rgszDir[6],rgszDir[7],rgszDir[8],rgszDir[9],
rgszDir[10],rgszDir[11],rgszDir[12],rgszDir[13],
rgszDir[14],rgszDir[15]);
for (i = 0; i < nDirs; i++)
AddIncludeDir(rgszDir[i]);
}
else
fprintf(stderr,"-J option: INCLUDE variable not set.\n");
}
break;
#endif
case 's':
case 'P':
case 'p':
case 'I':
case 'd':
case 'D':
case 'h':
{
char *sz = &pszArg[2];
if (sz[0] == '\0')
{
/* Allow "-I includefile"
* and "-IincludeFile"
*/
pszArg = GetNextArg();
if (!pszArg)
Usage();
sz = pszArg;
}
// fprintf(stderr, "File: '%s'.", sz);
sz = strdup(sz);
switch (chSwitch)
{
case 's':
szSuffix = sz;
break;
case 'P':
fReplacePrefix = TRUE;
// Drop through
case 'p':
szPrefix = sz;
break;
case 'I':
AddIncludeDir(sz);
break;
case 'd':
{
FI *pfi;
// exlude file given
// NOTE: the -C option if given, must appear before now
NormalizePath(sz);
if ((pfi = PfiDependFn(SzTransEnv(sz), sz, FALSE, langUnknown, FALSE)) != NULL)
// file existed; ignore it
pfi->fIgnore = TRUE;
else
// file doesn't exist, create FI
(void)PfiAlloc(SzTransEnv(sz), sz, TRUE, langUnknown);
break;
}
case 'D':
/* only print files from given directory */
NormalizePath(sz);
szPrintDir = sz;
break;
case 'h':
szPCHFile = sz;
break;
}
}
break;
default:
Usage();
break;
}
// fprintf(stderr, "\n");
}
while (pszArg)
{
long hf;
char szPath[_MAX_DIR];
char szName[_MAX_PATH];
struct _finddata_t fd;
// fprintf(stderr, "Reading path '%s' - ", pszArg);
NormalizePath(pszArg);
CopyPath(szPath, pszArg);
// fprintf(stderr, "'%s\\%s'\n", szPath, pszArg);
hf = _findfirst(pszArg, &fd);
if (hf > -1)
{
do
{
MakeName(szName, szPath, fd.name);
// fprintf(stderr, " -- '%s'\n", szName);
Process(szName, fReverse);
}
while (!_findnext(hf, &fd));
_findclose(hf);
}
// else
// fprintf(stderr, "Unable to find source file: %s\n", pszArg);
pszArg = GetNextArg();
}
return( 0 );
}
/*****************************************************************************/
/* standard dependency report */
VOID
StartReport()
/*
-- prepare for a new line
*/
{
cchLine = 77;
}
VOID
EndLine()
/*
-- Make it so that the next Report starts on a new line.
*/
{
cchLine = 0;
}
VOID
ContinueReport()
/*
-- Output continuation character, new line, then indent.
*/
{
printf(" \\\n");
StartReport();
Indent();
}
VOID
EndReport()
/*
-- Finish off this line.
*/
{
printf("\n\n");
}
VOID
Indent()
/*
-- Indent a tab at the beginning of a line.
*/
{
printf("\t");
cchLine -= 8; /* for tab */
}
VOID
Report(sz, szParm)
/*
-- report string
-- if too many characters extend line
*/
register char * sz;
char * szParm; /* ignored */
{
int cch = strlen(sz);
if (cch > cchLine)
{
ContinueReport();
while (isspace(sz[0]))
{
sz++;
cch--;
}
}
while (*sz != '\0')
{
if (*sz == '#')
{
putchar('\\'); /* escape any # in path */
cch++;
}
putchar(*sz);
sz++;
}
cchLine -= cch;
}
/*****************************************************************************/
/* Reverse dependency printing */
VOID
PrReverse(szHdr, szSource)
/*
-- report reverse dependency
*/
char * szHdr;
char * szSource;
{
printf("%s: %s\n", szHdr, szSource);
}
/*****************************************************************************/
BOOL FPrintFi(pfi)
/*
-- returns true if we should print this file; false if ignore; false if
szPrintDir is != 0 and it is a prefix of szName. The current directory
is a zero length string and is handled specially
*/
FI *pfi;
{
if (pfi->fIgnore)
return FALSE;
if (szPrintDir == NULL)
return TRUE;
if (*szPrintDir == '\0')
// only print current directory (check for / in name)
return strchr(pfi->szName, '/') == 0;
else
// print if szPrintDir is prefix of name
return strncmp(szPrintDir, pfi->szName, strlen(szPrintDir)) == 0;
}
VOID
EnumChildren(pfi, pfnDo, szParm)
/*
-- enumerate children, call *pfnDo for each element
*/
FI * pfi;
PFN_ENUM pfnDo;
char * szParm;
{
LK *plk;
for (plk = pfi->plkHead; plk != NULL; plk = plk->plkNext)
{
FI *pfi = plk->pfi;
if (pfi->cout < coutCur)
{
/* Mark that we've visited this node, to prevent
* infinite recursion should we have a self referential
* dependency graph.
*/
pfi->cout = coutCur;
if (FPrintFi(pfi))
{
if (szParm == NULL)
(*pfnDo)(" ", szParm);
(*pfnDo)(pfi->szName, szParm);
}
// recurse on nested includes; may include a non-standard includes
EnumChildren(pfi, pfnDo, szParm);
}
}
}
VOID
Process(szPath, fReverse)
/*
-- process a file
-- reverse => show headers as depending on files
*/
char * szPath; // path name to file
BOOL fReverse;
{
FI * pfi;
strlwr(szPath);
/* Build a list of all dependencies. */
pfi = PfiDependFn(szPath, szPath, FALSE, langUnknown, FALSE);
if (pfi == NULL)
{
if (fVerbose)
fprintf(stderr, "mkdep: warning: file %s ignored\n", szPath);
}
else if (pfi->plkHead != NULL)
{
/* file depends on something */
if (!fReverse)
{
/* normal dependencies */
char * pch;
/* truncate any suffix */
pch = strrchr(szPath, '.');
if (pch)
{
if (strchr(pch, '/') || strchr(pch, '\\'))
pch = NULL;
}
if (pch != NULL)
*pch = '\0';
StartReport();
Report(szPrefix, NULL);
if (fReplacePrefix)
{
/* prefix replaces any name prefix */
char * szName = szPath;
while (*szPath != '\0')
{
if (*szPath == '\\' || *szPath == '/')
szName = szPath+1;
szPath++;
}
Report(szName, NULL);
}
else
{
Report(szPath, NULL);
}
Report(szSuffix, NULL);
Report(" :", NULL);
EndLine();
coutCur++;
EnumChildren(pfi, Report, NULL);
EndReport();
}
else
{
/* reverse dependencies */
coutCur++;
EnumChildren(pfi, PrReverse, szPath);
}
}
if (pfi != NULL)
// free top level FI (presumably for .c/.asm file which won't be needed)
FreeFi(pfi);
}
FI *
PfiDependFn(szPath, szName, fPathIsStd, lang, fIsPCHFile)
/*
-- given a file name & language, return a filled in FI
-- return NULL if error
*/
char * szPath; // path name to file
char * szName; // official name of file
BOOL fPathIsStd; // path portion of szPath is from standard includes (-I)
LANG lang; // propagate parent language
BOOL fIsPCHFile; // Is .PCH marker file
{
FILE * pfile;
char rgch[256];
char * sz;
char * szSuffix;
FI * pfi;
/* first check to see if already in list */
if ((pfi = PfiLookup(szPath, szName, lang)) != NULL)
return pfi;
if (lang != langUnknown)
{
/* do nothing -- keep old language */
}
else if ((szSuffix = strrchr(szPath, '.')) == NULL)
return NULL;
else if (strcmp(szSuffix, ".asm") == 0 || strcmp(szSuffix, ".inc") == 0)
lang = langAsm;
else if (strcmp(szSuffix, ".rc") == 0)
lang = langRC;
else
lang = langC;
if ((pfile = fopen(szPath, "rt")) == NULL)
{
// fprintf(stderr, "Could not open file '%s'.\n", szPath);
return NULL;
}
pfi = PfiAlloc(szPath, szName, fPathIsStd && fIgnoreStd, lang);
if (lang == langRC)
{
//
// Make sure we don't try to parse binary files - major waste of time!
//
static char *aszBinary[] = { ".ico", ".sqz", ".bmp", ".tlb", ".cur",
".odg", ".ppg", ".otb" };
static int cBinary = sizeof(aszBinary)/sizeof(aszBinary[0]);
int i;
if (!szSuffix)
{
if ((szSuffix = strrchr(szPath, '.')) == NULL)
goto Cleanup;
}
for (i = cBinary; i && stricmp(szSuffix, aszBinary[i-1]); i--)
;
if (i != 0)
goto Cleanup;
}
// Don't search inside of the .PCH marker file
if (!fIsPCHFile)
{
BLOCK
{
/* Push the directory of this file on the list of directories for
* include searches. Save an indication as to whether this include is
* from a standard place.
*/
char szPathT[256];
char szNameT[256];
CopyPath(szPathT, szPath);
CopyPath(szNameT, szName);
PushDir(szPathT, szNameT, fPathIsStd);
}
while ((sz = fgets(rgch, 256, pfile)) != NULL)
{
char * szInc;
BOOL fThisDirNew = FALSE; /* must be in this directory */
int cch = strlen(sz);
if (cch < 2)
continue;
if (sz[cch-1] == '\n')
sz[cch-1] = '\0'; /* note : will truncate long lines */
if ((lang == langC && (szInc = SzIncludesC(sz, &fThisDirNew)) != NULL) ||
(lang == langAsm && (szInc = SzIncludesAsm(sz)) != NULL) ||
(lang == langRC && (szInc = SzIncludesRC(sz, &fThisDirNew)) != NULL))
{
FI * pfiNew = NULL;
char szPathNew[256];
char szNameNew[256];
BOOL fIsPCH;
fIsPCH = (szPCHFile && !_stricmp(szInc, szPCHFile));
/* if file can be found in current directory, cycle
* through all current directories possible.
*/
if (fThisDirNew)
{
int idi;
DI * pdi;
for (idi = 0; (pdi = PdiFromIdi(idi)) != NULL; idi++)
{
MakeName(szPathNew, pdi->szPath, szInc);
MakeName(szNameNew, pdi->szName, szInc);
/* Do recursive call to include file */
pfiNew = PfiDependFn(szPathNew, szNameNew, pdi->fPathIsStd, lang, fIsPCH);
/* If we found it, get out of loop */
if (pfiNew != NULL)
break;
}
}
/* If the file hasn't been found yet, look for it
* in the standard include directories.
*/
if (pfiNew == NULL)
{
int isz;
for (isz = 0; isz < iszIncMac; isz++)
{
MakeName(szPathNew, rgszIncPath[isz], szInc);
MakeName(szNameNew, rgszIncName[isz], szInc);
/* Do recursive call to include file */
pfiNew = PfiDependFn(szPathNew, szNameNew, TRUE, lang, fIsPCH);
/* If we found it, mark it and get out of loop */
if (pfiNew != NULL)
break;
}
}
/* The file doesn't exist anywhere. If it was included
* with quote marks and the user didn't specify -n, we
* will pretend the file is in the same directory as
* the file that's including it.
*/
if (pfiNew == NULL && fThisDirNew && !fNoGenHeaders)
{
BOOL fPathIsStd;
if (fUseCurDir)
{
MakeName(szPathNew, ".\\", szInc);
MakeName(szNameNew, ".\\", szInc);
fPathIsStd = FALSE;
/* Look for -d names */
if ((pfiNew = PfiLookup(szPathNew, szNameNew,lang)) == NULL)
pfiNew = PfiAlloc(szPathNew, szNameNew, FALSE, lang);
}
else
{
DI * pdi;
pdi = PdiFromIdi(0);
if (pdi == NULL)
Fatal("mkdep: internal error");
MakeName(szPathNew, pdi->szPath, szInc);
MakeName(szNameNew, pdi->szName, szInc);
// in this case we already look through existing FI list
pfiNew = PfiAlloc(szPathNew, szNameNew,
pdi->fPathIsStd && fIgnoreStd, lang);
}
}
// If the .PCH marker file has been found, truncate all preceeding .H files
if (pfiNew && fIsPCH)
{
FreeAllLk(pfi);
FreeFi(pfi->pfiNext);
pfi->pfiNext = NULL;
}
/* If we found the file, add it to the list of files */
if (pfiNew != NULL)
{
/* add if not already in list */
LK * plk;
BOOL fRedundant = FALSE;
for (plk = pfi->plkHead; plk != NULL;
plk = plk->plkNext)
{
if (plk->pfi == pfiNew)
{
fRedundant = TRUE;
break;
}
}
if (!fRedundant)
AllocLk(pfi, pfiNew);
}
}
}
PopDir();
}
Cleanup:
fclose(pfile);
return pfi;
}
char *
SzIncludesC(sz, pfThisDir)
/*
-- return file name of include file or NULL
-- if returning non-NULL, set *pfThisDir if file should exist in this
directory (i.e. #include "...").
*/
char *sz;
BOOL *pfThisDir;
{
char *szLine = sz;
while (isspace(*sz))
sz++;
if (sz[0] == '#')
{
/* Allow space after '#' but before directive.
*/
sz++;
while (isspace(sz[0]))
sz++;
if (strncmp(sz, "include", 7) == 0)
{
/* found it */
char * pchEnd;
sz += 7;
while (isspace(*sz))
sz++;
if ((*sz == '<' && (pchEnd =strchr(sz+1,'>')) !=NULL) ||
(*sz == '"' && (pchEnd =strchr(sz+1, '"')) !=NULL))
{
*pfThisDir = *sz == '"';
*pchEnd = '\0';
return sz+1;
}
else
{
fprintf(stderr, "mkdep: warning: ignoring line : %s\n", szLine);
return NULL;
}
}
}
return NULL;
}
char *
SzIncludesAsm(sz)
/*
-- return file name of include file or NULL
*/
char *sz;
{
char *szLine = sz;
strlwr(szLine);
while (isspace(*sz))
sz++;
if (strncmp(sz, "include", 7) == 0)
{
/* found it */
char *pchEnd;
sz += 7;
while (isspace(*sz))
sz++;
pchEnd = sz;
while (*pchEnd && !isspace(*pchEnd) && *pchEnd != ';')
pchEnd++;
if (pchEnd == sz)
{
fprintf(stderr, "mkdep: warning: ignoring line : %s\n", szLine);
return NULL;
}
*pchEnd = '\0';
return sz;
}
return NULL;
}
char *
SzIncludesRC(sz, pfThisDir)
/*
-- return name of include file or resource file for an RC file
-- if returning non-NULL, set *pfThisDir if file should exist in this
directory (i.e. #include "...").
*/
char *sz;
BOOL *pfThisDir;
{
static char *aszValidTypes[] =
{
"CURSOR", "ICON", "RT_DOCFILE", "TYPELIB", "BITMAP"
};
static int cValidTypes = sizeof(aszValidTypes)/sizeof(aszValidTypes[0]);
char achIdent[255] = { 0 };
char achType[255] = { 0 };
char achFile[255] = { 0 };
char * pch;
int cch;
char * szC;
int n, i;
szC = SzIncludesC(sz, pfThisDir);
if (szC)
return szC;
*pfThisDir = TRUE;
n = sscanf(sz, "%[a-zA-Z0-9_] %[a-zA-Z0-9_] %n%[a-zA-Z0-9.\"]",
achIdent, achType, &cch, achFile);
if (n < 3)
return NULL;
for (i = cValidTypes; i && stricmp(achType, aszValidTypes[i-1]); i--)
;
if (i == 0)
return NULL;
sz += cch;
while (isspace(*sz))
sz++;
sz[strlen(achFile)] = '\0';
if (*sz == '\"')
sz++;
if ((pch = strrchr(sz, '\"')) != NULL)
*pch = '\0';
return sz;
}
FI *
PfiLookup(szPath, szName, lang)
/*
-- lookup name in current list of FI; if file is of an unknown language and
lang is not, set the language of this file.
*/
char * szPath; // path name to file
char * szName; // official name of file
LANG lang; // lang desired; langUnknown means any acceptible
{
FI *pfi;
for (pfi = pfiHead; pfi != NULL; pfi = pfi->pfiNext)
{
if (strcmp(szPath, pfi->szPath) == 0)
{
/* got one */
if (lang != langUnknown && lang != pfi->lang)
{
// want a specific language and that is not what the file is
if (pfi->lang != langUnknown)
fprintf(stderr,
"mkdep: warning: language conflict for file %s\n",
pfi->szPath);
else
pfi->lang = lang; // was unknown, set to known
}
return pfi;
}
}
return NULL;
}
FI *
PfiAlloc(szPath, szName, fIgnore, lang)
/*
-- allocate an FI
*/
char * szPath; // path name to file
char * szName; // official name of file
BOOL fIgnore; // true -> don't print this file
LANG lang; // lang for file; can be langUnknown
{
FI *pfi;
if ((pfi = (FI *) malloc(sizeof(FI))) == NULL ||
(pfi->szName = strdup(szName)) == NULL ||
(pfi->szPath = strdup(szPath)) == NULL)
Fatal("out of memory");
pfi->lang = lang;
pfi->fIgnore = fIgnore;
pfi->plkHead = pfi->plkTail = NULL;
AddToList(pfi, pfiHead, pfiTail, pfiNext, NULL);
pfi->cout = coutCur;
return pfi;
}
VOID
FreeFi(pfiFree)
/*
-- free an FI and all associated LK and remove from FI list
*/
FI *pfiFree;
{
FI *pfiT, *pfiPrev;
FreeAllLk(pfiFree);
for (pfiT = pfiHead, pfiPrev = 0; pfiT != pfiFree; pfiPrev = pfiT, pfiT = pfiT->pfiNext)
{
// should find it on list
// Assert(pfiT != NULL);
}
DeleteFromList(pfiFree, pfiHead, pfiTail, pfiNext, NULL, pfiPrev);
free(pfiFree);
}
VOID
AllocLk(pfiOwner, pfiNew)
/*
-- allocate a LK - add to owner list - point to pfiNew
*/
FI *pfiOwner;
FI *pfiNew;
{
LK *plk;
if ((plk = (LK *) malloc(sizeof(LK))) == NULL)
Fatal("out of memory");
plk->plkNext = NULL;
AddToList(plk, pfiOwner->plkHead, pfiOwner->plkTail, plkNext, NULL);
plk->pfi = pfiNew;
}
VOID
FreeAllLk(pfi)
/*
-- free all lk attached to FI
*/
FI *pfi;
{
LK * plk;
LK * plkNext;
for (plk = pfi->plkHead; plk != NULL; plk = plkNext)
{
plkNext = plk->plkNext;
free(plk);
}
pfi->plkHead = NULL;
pfi->plkTail = NULL;
}
SZ
SzTransEnv(sz)
/*
-- return a path string with optional $(...) in it
*/
SZ sz;
{
SZ szEnv;
char * pch;
char szT[256];
if (sz[0] != '$' || sz[1] != '(')
return sz;
sz += 2;
if ((pch = strchr(sz, ')')) == NULL)
return sz; // something wrong
*pch = '\0';
if ((szEnv = getenv(sz)) == NULL)
{
fprintf(stderr,
"mkdep: warning: environment variable %s not defined\n");
Fatal("incomplete path");
}
*pch = ')'; // restore string
/* copy the environment variable into buffer */
strcpy(szT, szEnv);
strcat(szT, pch+1); // and rest of string
NormalizePath(szT); // normalize again with new prefix
return strdup(szT);
}
VOID NormalizePath(sz)
/*
-- convert path to a normal form in place: forward slashes, no ../, etc.
*/
char *sz;
{
char *pch, *pch2;
/* change all backslashes to forward slashes */
for (pch=sz; *pch; ++pch)
if (*pch == '\\')
*pch = '/';
/* Remove ".." entries. (The algorithm below doesn't find all
* possible cases, but it's good enuff.)
*/
while ((pch=strstr(sz, "/../")) != NULL)
{
*pch = '\0';
pch2 = strrchr(sz, '/');
if (pch2 != NULL && pch2[1] != '$' && pch2[1] != '.')
memmove(pch2+1, pch+4, strlen(pch+1)+1);
else
{
*pch = '/';
break;
}
}
// remove single . and leading ./
if (sz[0] == '.')
{
if (sz[1] == '\0')
sz[0] = '\0';
else if (sz[1] == '/')
memmove(sz, sz+2, strlen(sz)-2+1);
}
}
VOID
MakeName(szDest, szSrcPath, szSrcFile)
/*
-- copy a path plus filename into a complete filename
-- normalizes when done
*/
char * szDest; // where to store complete filename
char * szSrcPath; // path
char * szSrcFile; // filename
{
if (szSrcFile[0] && szSrcFile[1]==':')
{
if (!(szSrcPath[0] && szSrcPath[1]==':') ||
tolower(szSrcPath[0]) != tolower(szSrcFile[0]))
{
strcpy(szDest, szSrcFile);
NormalizePath(szDest);
return;
}
*szDest++ = *szSrcFile++; *szDest++ = *szSrcFile++;
}
if (szSrcFile[0] == '/' || szSrcFile[0] == '\\')
{
strcpy(szDest, szSrcFile);
NormalizePath(szDest);
return;
}
strcpy(szDest, szSrcPath);
if (szDest[0] != '\0')
{
char ch = szDest[strlen(szDest)-1];
if (ch != ':' && ch != '/' && ch != '\\')
strcat(szDest, "/");
}
strcat(szDest, szSrcFile);
NormalizePath(szDest);
}
VOID
CopyPath(szDestPath, szSrcFullName)
/*
-- copy the path part of szSrcFullName into szDestPath
*/
char * szDestPath;
char * szSrcFullName;
{
int ich;
int ichPathEnd; // index to end of path part of szSrcFullName
char ch;
/* Figure out where the path part of szSrcFullName ends and the
* name part begins.
*/
for (ich = ichPathEnd = 0; (ch=szSrcFullName[ich]) != 0; ++ich)
if (ch == ':' || ch == '/' || ch == '\\')
ichPathEnd = ich+1;
/* Copy the path */
for (ich = 0; ich < ichPathEnd; ++ich)
szDestPath[ich] = szSrcFullName[ich];
szDestPath[ich] = 0;
}
VOID
PushDir(szPath, szName, fPathIsStd)
/*
-- push a directory name on the stack of directories for all nested
includes
*/
char * szPath; // path name of file (e.g. "c:\foo\bar")
char * szName; // official name of file (e.g. "$(INCL)")
BOOL fPathIsStd; // path portion of szPath is from standard includes (-I)
{
DI * pdi;
if ((pdi = malloc(sizeof(DI))) == NULL)
Fatal("out of memory");
pdi->szPath = strdup(szPath);
pdi->szName = strdup(szName);
pdi->fPathIsStd = fPathIsStd;
/* Insert at head of list */
pdi->pdiNext = pdiHead;
pdiHead = pdi;
}
VOID
PopDir(void)
/*
-- pop a directory name from the stack of directories for all nested
includes
*/
{
DI *pdiFree;
if (pdiHead == NULL)
Fatal("mkdep: internal error");
pdiFree = pdiHead;
pdiHead = pdiHead->pdiNext;
free(pdiFree->szPath);
free(pdiFree->szName);
free(pdiFree);
}
DI *
PdiFromIdi(idi)
/*
-- return a pointer to one element from the stack, or NULL
*/
int idi; // index of element to get (0 = top of stack)
{
DI * pdi;
for (pdi = pdiHead; pdi && idi; idi--)
pdi = pdi->pdiNext;
return pdi;
}
int
AddIncludeDir(szFile)
char * szFile;
{
if (iszIncMac+1 >= iszIncMax)
{
fprintf(stderr,
"mkdep: warning"
": too many include directories"
"; ignoring %s\n", szFile);
return 0;
}
else
{
/* normal include */
NormalizePath(szFile);
rgszIncPath[iszIncMac] = SzTransEnv(szFile);
rgszIncName[iszIncMac] = szFile;
// fprintf(stderr,"Added include: %s\n",szFile);
iszIncMac++;
return 1;
}
}