1491 lines
37 KiB
C
1491 lines
37 KiB
C
/*-----------------------------------------------------------------------------
|
|
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;
|
|
}
|
|
}
|