/*----------------------------------------------------------------------------- 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 #include #include #include #include #include #include #include // 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 */ 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; } }