diff options
Diffstat (limited to 'dir.c')
| -rw-r--r-- | dir.c | 1975 |
1 files changed, 1058 insertions, 917 deletions
@@ -1,4 +1,4 @@ -/* $NetBSD: dir.c,v 1.210 2020/11/14 21:29:44 rillig Exp $ */ +/* $NetBSD: dir.c,v 1.255 2021/01/10 21:20:46 rillig Exp $ */ /* * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. @@ -69,7 +69,8 @@ * SUCH DAMAGE. */ -/* Directory searching using wildcards and/or normal names. +/* + * Directory searching using wildcards and/or normal names. * Used both for source wildcarding in the makefile and for finding * implicit sources. * @@ -106,7 +107,8 @@ * * Dir_AddDir Add a directory to a search path. * - * Dir_MakeFlags Given a search path and a command flag, create + * SearchPath_ToFlags + * Given a search path and a command flag, create * a string with each of the directories in the path * preceded by the command flag and all of them * separated by a space. @@ -116,7 +118,8 @@ * as the element is no longer referenced by any other * search path. * - * Dir_ClearPath Resets a search path to the empty list. + * SearchPath_Clear + * Resets a search path to the empty list. * * For debugging: * Dir_PrintDirectories @@ -134,13 +137,10 @@ #include "job.h" /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ -MAKE_RCSID("$NetBSD: dir.c,v 1.210 2020/11/14 21:29:44 rillig Exp $"); - -#define DIR_DEBUG0(text) DEBUG0(DIR, text) -#define DIR_DEBUG1(fmt, arg1) DEBUG1(DIR, fmt, arg1) -#define DIR_DEBUG2(fmt, arg1, arg2) DEBUG2(DIR, fmt, arg1, arg2) +MAKE_RCSID("$NetBSD: dir.c,v 1.255 2021/01/10 21:20:46 rillig Exp $"); -/* A search path is a list of CachedDir structures. A CachedDir has in it the +/* + * A search path is a list of CachedDir structures. A CachedDir has in it the * name of the directory and the names of all the files in the directory. * This is used to cut down on the number of system calls necessary to find * implicit dependents and their like. Since these searches are made before @@ -207,179 +207,277 @@ MAKE_RCSID("$NetBSD: dir.c,v 1.210 2020/11/14 21:29:44 rillig Exp $"); * the mtime in a cache for when Dir_UpdateMTime was actually called. */ + +/* A cache for the filenames in a directory. */ +struct CachedDir { + /* + * Name of directory, either absolute or relative to the current + * directory. The name is not normalized in any way, that is, "." + * and "./." are different. + * + * Not sure what happens when .CURDIR is assigned a new value; see + * Parse_DoVar. + */ + char *name; + + /* + * The number of SearchPaths that refer to this directory. + * Plus the number of global variables that refer to this directory. + * References from openDirs do not count though. + */ + int refCount; + + /* The number of times a file in this directory has been found. */ + int hits; + + /* The names of the directory entries. */ + HashSet files; +}; + typedef List CachedDirList; typedef ListNode CachedDirListNode; typedef ListNode SearchPathNode; -SearchPath *dirSearchPath; /* main search path */ - /* A list of cached directories, with fast lookup by directory name. */ typedef struct OpenDirs { - CachedDirList *list; - HashTable /* of CachedDirListNode */ table; + CachedDirList list; + HashTable /* of CachedDirListNode */ table; } OpenDirs; +typedef enum CachedStatsFlags { + CST_NONE = 0, + CST_LSTAT = 1 << 0, /* call lstat(2) instead of stat(2) */ + CST_UPDATE = 1 << 1 /* ignore existing cached entry */ +} CachedStatsFlags; + + +SearchPath dirSearchPath = LST_INIT; /* main search path */ + +static OpenDirs openDirs; /* all cached directories */ + +/* + * Variables for gathering statistics on the efficiency of the caching + * mechanism. + */ +static int hits; /* Found in directory cache */ +static int misses; /* Sad, but not evil misses */ +static int nearmisses; /* Found under search path */ +static int bigmisses; /* Sought by itself */ + +/* The cached contents of ".", the relative current directory. */ +static CachedDir *dot = NULL; +/* The cached contents of the absolute current directory. */ +static CachedDir *cur = NULL; +/* A fake path entry indicating we need to look for '.' last. */ +static CachedDir *dotLast = NULL; + +/* + * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to + * the system to find the file, we might as well have its mtime on record. + * + * XXX: If this is done way early, there's a chance other rules will have + * already updated the file, in which case we'll update it again. Generally, + * there won't be two rules to update a single file, so this should be ok, + * but... + */ +static HashTable mtimes; + +static HashTable lmtimes; /* same as mtimes but for lstat */ + + +static void OpenDirs_Remove(OpenDirs *, const char *); + + +static CachedDir * +CachedDir_New(const char *name) +{ + CachedDir *dir = bmake_malloc(sizeof *dir); + + dir->name = bmake_strdup(name); + dir->refCount = 0; + dir->hits = 0; + HashSet_Init(&dir->files); + +#ifdef DEBUG_REFCNT + DEBUG2(DIR, "CachedDir %p new for \"%s\"\n", dir, dir->name); +#endif + + return dir; +} + +static CachedDir * +CachedDir_Ref(CachedDir *dir) +{ + dir->refCount++; + +#ifdef DEBUG_REFCNT + DEBUG3(DIR, "CachedDir %p ++ %d for \"%s\"\n", + dir, dir->refCount, dir->name); +#endif + + return dir; +} + +static void +CachedDir_Unref(CachedDir *dir) +{ + dir->refCount--; + +#ifdef DEBUG_REFCNT + DEBUG3(DIR, "CachedDir %p -- %d for \"%s\"\n", + dir, dir->refCount, dir->name); +#endif + + if (dir->refCount > 0) + return; + +#ifdef DEBUG_REFCNT + DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); +#endif + + OpenDirs_Remove(&openDirs, dir->name); + + free(dir->name); + HashSet_Done(&dir->files); + free(dir); +} + +/* Update the value of the CachedDir variable, updating the reference counts. */ +static void +CachedDir_Assign(CachedDir **var, CachedDir *dir) +{ + CachedDir *prev; + + prev = *var; + *var = dir; + if (dir != NULL) + CachedDir_Ref(dir); + if (prev != NULL) + CachedDir_Unref(prev); +} + static void OpenDirs_Init(OpenDirs *odirs) { - odirs->list = Lst_New(); - HashTable_Init(&odirs->table); + Lst_Init(&odirs->list); + HashTable_Init(&odirs->table); } #ifdef CLEANUP static void OpenDirs_Done(OpenDirs *odirs) { - CachedDirListNode *ln = odirs->list->first; - while (ln != NULL) { - CachedDirListNode *next = ln->next; - CachedDir *dir = ln->datum; - Dir_Destroy(dir); /* removes the dir from odirs->list */ - ln = next; - } - Lst_Free(odirs->list); - HashTable_Done(&odirs->table); + CachedDirListNode *ln = odirs->list.first; + DEBUG1(DIR, "OpenDirs_Done: %u entries to remove\n", + odirs->table.numEntries); + while (ln != NULL) { + CachedDirListNode *next = ln->next; + CachedDir *dir = ln->datum; + DEBUG2(DIR, "OpenDirs_Done: refCount %d for \"%s\"\n", + dir->refCount, dir->name); + CachedDir_Unref(dir); /* removes the dir from odirs->list */ + ln = next; + } + Lst_Done(&odirs->list); + HashTable_Done(&odirs->table); } #endif static CachedDir * OpenDirs_Find(OpenDirs *odirs, const char *name) { - CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name); - return ln != NULL ? ln->datum : NULL; + CachedDirListNode *ln = HashTable_FindValue(&odirs->table, name); + return ln != NULL ? ln->datum : NULL; } static void OpenDirs_Add(OpenDirs *odirs, CachedDir *cdir) { - if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL) - return; - Lst_Append(odirs->list, cdir); - HashTable_Set(&odirs->table, cdir->name, odirs->list->last); + if (HashTable_FindEntry(&odirs->table, cdir->name) != NULL) + return; + Lst_Append(&odirs->list, cdir); + HashTable_Set(&odirs->table, cdir->name, odirs->list.last); } static void OpenDirs_Remove(OpenDirs *odirs, const char *name) { - HashEntry *he = HashTable_FindEntry(&odirs->table, name); - CachedDirListNode *ln; - if (he == NULL) - return; - ln = HashEntry_Get(he); - HashTable_DeleteEntry(&odirs->table, he); - Lst_Remove(odirs->list, ln); + HashEntry *he = HashTable_FindEntry(&odirs->table, name); + CachedDirListNode *ln; + if (he == NULL) + return; + ln = HashEntry_Get(he); + HashTable_DeleteEntry(&odirs->table, he); + Lst_Remove(&odirs->list, ln); } -static OpenDirs openDirs; /* all cached directories */ - /* - * Variables for gathering statistics on the efficiency of the caching - * mechanism. + * Returns 0 and the result of stat(2) or lstat(2) in *out_cst, + * or -1 on error. */ -static int hits; /* Found in directory cache */ -static int misses; /* Sad, but not evil misses */ -static int nearmisses; /* Found under search path */ -static int bigmisses; /* Sought by itself */ - -static CachedDir *dot; /* contents of current directory */ -static CachedDir *cur; /* contents of current directory, if not dot */ -static CachedDir *dotLast; /* a fake path entry indicating we need to - * look for . last */ - -/* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to - * the system to find the file, we might as well have its mtime on record. - * - * XXX: If this is done way early, there's a chance other rules will have - * already updated the file, in which case we'll update it again. Generally, - * there won't be two rules to update a single file, so this should be ok, - * but... */ -static HashTable mtimes; - -static HashTable lmtimes; /* same as mtimes but for lstat */ - -typedef enum CachedStatsFlags { - CST_NONE = 0, - CST_LSTAT = 1 << 0, /* call lstat(2) instead of stat(2) */ - CST_UPDATE = 1 << 1 /* ignore existing cached entry */ -} CachedStatsFlags; - -/* Returns 0 and the result of stat(2) or lstat(2) in *out_cst, - * or -1 on error. */ static int cached_stats(const char *pathname, struct cached_stat *out_cst, CachedStatsFlags flags) { - HashTable *tbl = flags & CST_LSTAT ? &lmtimes : &mtimes; - struct stat sys_st; - struct cached_stat *cst; - int rc; + HashTable *tbl = flags & CST_LSTAT ? &lmtimes : &mtimes; + struct stat sys_st; + struct cached_stat *cst; + int rc; - if (pathname == NULL || pathname[0] == '\0') - return -1; /* This can happen in meta mode. */ + if (pathname == NULL || pathname[0] == '\0') + return -1; /* This can happen in meta mode. */ - cst = HashTable_FindValue(tbl, pathname); - if (cst != NULL && !(flags & CST_UPDATE)) { - *out_cst = *cst; - DIR_DEBUG2("Using cached time %s for %s\n", - Targ_FmtTime(cst->cst_mtime), pathname); - return 0; - } + cst = HashTable_FindValue(tbl, pathname); + if (cst != NULL && !(flags & CST_UPDATE)) { + *out_cst = *cst; + DEBUG2(DIR, "Using cached time %s for %s\n", + Targ_FmtTime(cst->cst_mtime), pathname); + return 0; + } - rc = (flags & CST_LSTAT ? lstat : stat)(pathname, &sys_st); - if (rc == -1) - return -1; /* don't cache negative lookups */ + rc = (flags & CST_LSTAT ? lstat : stat)(pathname, &sys_st); + if (rc == -1) + return -1; /* don't cache negative lookups */ - if (sys_st.st_mtime == 0) - sys_st.st_mtime = 1; /* avoid confusion with missing file */ + if (sys_st.st_mtime == 0) + sys_st.st_mtime = 1; /* avoid confusion with missing file */ - if (cst == NULL) { - cst = bmake_malloc(sizeof *cst); - HashTable_Set(tbl, pathname, cst); - } + if (cst == NULL) { + cst = bmake_malloc(sizeof *cst); + HashTable_Set(tbl, pathname, cst); + } - cst->cst_mtime = sys_st.st_mtime; - cst->cst_mode = sys_st.st_mode; + cst->cst_mtime = sys_st.st_mtime; + cst->cst_mode = sys_st.st_mode; - *out_cst = *cst; - DIR_DEBUG2(" Caching %s for %s\n", - Targ_FmtTime(sys_st.st_mtime), pathname); + *out_cst = *cst; + DEBUG2(DIR, " Caching %s for %s\n", + Targ_FmtTime(sys_st.st_mtime), pathname); - return 0; + return 0; } int cached_stat(const char *pathname, struct cached_stat *cst) { - return cached_stats(pathname, cst, CST_NONE); + return cached_stats(pathname, cst, CST_NONE); } int cached_lstat(const char *pathname, struct cached_stat *cst) { - return cached_stats(pathname, cst, CST_LSTAT); + return cached_stats(pathname, cst, CST_LSTAT); } /* Initialize the directories module. */ void Dir_Init(void) { - dirSearchPath = Lst_New(); - OpenDirs_Init(&openDirs); - HashTable_Init(&mtimes); - HashTable_Init(&lmtimes); -} - -void -Dir_InitDir(const char *cdname) -{ - Dir_InitCur(cdname); - - dotLast = bmake_malloc(sizeof *dotLast); - dotLast->refCount = 1; - dotLast->hits = 0; - dotLast->name = bmake_strdup(".DOTLAST"); - HashTable_Init(&dotLast->files); + OpenDirs_Init(&openDirs); + HashTable_Init(&mtimes); + HashTable_Init(&lmtimes); + CachedDir_Assign(&dotLast, CachedDir_New(".DOTLAST")); } /* @@ -388,58 +486,40 @@ Dir_InitDir(const char *cdname) void Dir_InitCur(const char *cdname) { - CachedDir *dir; - - if (cdname == NULL) - return; + CachedDir *dir; - /* - * Our build directory is not the same as our source directory. - * Keep this one around too. - */ - dir = Dir_AddDir(NULL, cdname); - if (dir == NULL) - return; + if (cdname == NULL) + return; - /* XXX: Reference counting is wrong here. - * If this function is called repeatedly with the same directory name, - * its reference count increases each time even though the number of - * actual references stays the same. */ - - dir->refCount++; - if (cur != NULL && cur != dir) { /* - * We've been here before, clean up. + * Our build directory is not the same as our source directory. + * Keep this one around too. */ - cur->refCount--; - Dir_Destroy(cur); - } - cur = dir; + dir = Dir_AddDir(NULL, cdname); + if (dir == NULL) + return; + + CachedDir_Assign(&cur, dir); } -/* (Re)initialize "dot" (current/object directory) path hash. - * Some directories may be opened. */ +/* + * (Re)initialize "dot" (current/object directory) path hash. + * Some directories may be cached. + */ void Dir_InitDot(void) { - if (dot != NULL) { - /* Remove old entry from openDirs, but do not destroy. */ - OpenDirs_Remove(&openDirs, dot->name); - } + CachedDir *dir; - dot = Dir_AddDir(NULL, "."); + dir = Dir_AddDir(NULL, "."); + if (dir == NULL) { + Error("Cannot open `.' (%s)", strerror(errno)); + exit(2); /* Not 1 so -q can distinguish error */ + } - if (dot == NULL) { - Error("Cannot open `.' (%s)", strerror(errno)); - exit(1); - } + CachedDir_Assign(&dot, dir); - /* - * We always need to have dot around, so we increment its reference count - * to make sure it's not destroyed. - */ - dot->refCount++; - Dir_SetPATH(); /* initialize */ + Dir_SetPATH(); /* initialize */ } /* Clean up the directories module. */ @@ -447,18 +527,13 @@ void Dir_End(void) { #ifdef CLEANUP - if (cur) { - cur->refCount--; - Dir_Destroy(cur); - } - dot->refCount--; - dotLast->refCount--; - Dir_Destroy(dotLast); - Dir_Destroy(dot); - Dir_ClearPath(dirSearchPath); - Lst_Free(dirSearchPath); - OpenDirs_Done(&openDirs); - HashTable_Done(&mtimes); + CachedDir_Assign(&cur, NULL); + CachedDir_Assign(&dot, NULL); + CachedDir_Assign(&dotLast, NULL); + SearchPath_Clear(&dirSearchPath); + OpenDirs_Done(&openDirs); + HashTable_Done(&mtimes); + HashTable_Done(&lmtimes); #endif } @@ -470,44 +545,45 @@ Dir_End(void) void Dir_SetPATH(void) { - CachedDirListNode *ln; - Boolean hasLastDot = FALSE; /* true if we should search dot last */ + CachedDirListNode *ln; + Boolean seenDotLast = FALSE; /* true if we should search '.' last */ - Var_Delete(".PATH", VAR_GLOBAL); + Var_Delete(".PATH", VAR_GLOBAL); - if ((ln = dirSearchPath->first) != NULL) { - CachedDir *dir = ln->datum; - if (dir == dotLast) { - hasLastDot = TRUE; - Var_Append(".PATH", dotLast->name, VAR_GLOBAL); + if ((ln = dirSearchPath.first) != NULL) { + CachedDir *dir = ln->datum; + if (dir == dotLast) { + seenDotLast = TRUE; + Var_Append(".PATH", dotLast->name, VAR_GLOBAL); + } } - } - if (!hasLastDot) { - if (dot) - Var_Append(".PATH", dot->name, VAR_GLOBAL); - if (cur) - Var_Append(".PATH", cur->name, VAR_GLOBAL); - } + if (!seenDotLast) { + if (dot != NULL) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur != NULL) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } - for (ln = dirSearchPath->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (dir == dotLast) - continue; - if (dir == dot && hasLastDot) - continue; - Var_Append(".PATH", dir->name, VAR_GLOBAL); - } + for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (dir == dotLast) + continue; + if (dir == dot && seenDotLast) + continue; + Var_Append(".PATH", dir->name, VAR_GLOBAL); + } - if (hasLastDot) { - if (dot) - Var_Append(".PATH", dot->name, VAR_GLOBAL); - if (cur) - Var_Append(".PATH", cur->name, VAR_GLOBAL); - } + if (seenDotLast) { + if (dot != NULL) + Var_Append(".PATH", dot->name, VAR_GLOBAL); + if (cur != NULL) + Var_Append(".PATH", cur->name, VAR_GLOBAL); + } } -/* See if the given name has any wildcard characters in it and all braces and +/* + * See if the given name has any wildcard characters in it and all braces and * brackets are properly balanced. * * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think @@ -519,38 +595,39 @@ Dir_SetPATH(void) Boolean Dir_HasWildcards(const char *name) { - const char *p; - Boolean wild = FALSE; - int braces = 0, brackets = 0; + const char *p; + Boolean wild = FALSE; + int braces = 0, brackets = 0; - for (p = name; *p != '\0'; p++) { - switch (*p) { - case '{': - braces++; - wild = TRUE; - break; - case '}': - braces--; - break; - case '[': - brackets++; - wild = TRUE; - break; - case ']': - brackets--; - break; - case '?': - case '*': - wild = TRUE; - break; - default: - break; + for (p = name; *p != '\0'; p++) { + switch (*p) { + case '{': + braces++; + wild = TRUE; + break; + case '}': + braces--; + break; + case '[': + brackets++; + wild = TRUE; + break; + case ']': + brackets--; + break; + case '?': + case '*': + wild = TRUE; + break; + default: + break; + } } - } - return wild && brackets == 0 && braces == 0; + return wild && brackets == 0 && braces == 0; } -/* See if any files match the pattern and add their names to the 'expansions' +/* + * See if any files match the pattern and add their names to the 'expansions' * list if they do. * * This is incomplete -- wildcards are only expanded in the final path @@ -566,105 +643,114 @@ Dir_HasWildcards(const char *name) static void DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) { - const char *dirName = dir->name; - Boolean isDot = dirName[0] == '.' && dirName[1] == '\0'; - HashIter hi; + const char *dirName = dir->name; + Boolean isDot = dirName[0] == '.' && dirName[1] == '\0'; + HashIter hi; - /* XXX: Iterating over all hash entries is inefficient. If the pattern - * is a plain string without any wildcards, a direct lookup is faster. */ + /* + * XXX: Iterating over all hash entries is inefficient. If the + * pattern is a plain string without any wildcards, a direct lookup + * is faster. + */ - HashIter_Init(&hi, &dir->files); - while (HashIter_Next(&hi) != NULL) { - const char *base = hi.entry->key; + HashIter_InitSet(&hi, &dir->files); + while (HashIter_Next(&hi) != NULL) { + const char *base = hi.entry->key; - if (!Str_Match(base, pattern)) - continue; + if (!Str_Match(base, pattern)) + continue; - /* - * Follow the UNIX convention that dot files are only found if the - * pattern begins with a dot. The pattern '.*' does not match '.' or - * '..' since these are not included in the directory cache. - * - * This means that the pattern '[a-z.]*' does not find '.file', which - * is consistent with bash, NetBSD sh and csh. - */ - if (base[0] == '.' && pattern[0] != '.') - continue; + /* + * Follow the UNIX convention that dot files are only found + * if the pattern begins with a dot. The pattern '.*' does + * not match '.' or '..' since these are not included in the + * directory cache. + * + * This means that the pattern '[a-z.]*' does not find + * '.file', which is consistent with bash, NetBSD sh and csh. + */ + if (base[0] == '.' && pattern[0] != '.') + continue; - { - char *fullName = isDot - ? bmake_strdup(base) - : str_concat3(dirName, "/", base); - Lst_Append(expansions, fullName); + { + char *fullName = isDot + ? bmake_strdup(base) + : str_concat3(dirName, "/", base); + Lst_Append(expansions, fullName); + } } - } } -/* Find the next closing brace in the string, taking nested braces into - * account. */ +/* + * Find the next closing brace in the string, taking nested braces into + * account. + */ static const char * closing_brace(const char *p) { - int nest = 0; - while (*p != '\0') { - if (*p == '}' && nest == 0) - break; - if (*p == '{') - nest++; - if (*p == '}') - nest--; - p++; - } - return p; + int nest = 0; + while (*p != '\0') { + if (*p == '}' && nest == 0) + break; + if (*p == '{') + nest++; + if (*p == '}') + nest--; + p++; + } + return p; } -/* Find the next closing brace or comma in the string, taking nested braces - * into account. */ +/* + * Find the next closing brace or comma in the string, taking nested braces + * into account. + */ static const char * separator_comma(const char *p) { - int nest = 0; - while (*p != '\0') { - if ((*p == '}' || *p == ',') && nest == 0) - break; - if (*p == '{') - nest++; - if (*p == '}') - nest--; - p++; - } - return p; + int nest = 0; + while (*p != '\0') { + if ((*p == '}' || *p == ',') && nest == 0) + break; + if (*p == '{') + nest++; + if (*p == '}') + nest--; + p++; + } + return p; } static Boolean contains_wildcard(const char *p) { - for (; *p != '\0'; p++) { - switch (*p) { - case '*': - case '?': - case '{': - case '[': - return TRUE; + for (; *p != '\0'; p++) { + switch (*p) { + case '*': + case '?': + case '{': + case '[': + return TRUE; + } } - } - return FALSE; + return FALSE; } static char * concat3(const char *a, size_t a_len, const char *b, size_t b_len, const char *c, size_t c_len) { - size_t s_len = a_len + b_len + c_len; - char *s = bmake_malloc(s_len + 1); - memcpy(s, a, a_len); - memcpy(s + a_len, b, b_len); - memcpy(s + a_len + b_len, c, c_len); - s[s_len] = '\0'; - return s; + size_t s_len = a_len + b_len + c_len; + char *s = bmake_malloc(s_len + 1); + memcpy(s, a, a_len); + memcpy(s + a_len, b, b_len); + memcpy(s + a_len + b_len, c, c_len); + s[s_len] = '\0'; + return s; } -/* Expand curly braces like the C shell. Brace expansion by itself is purely +/* + * Expand curly braces like the C shell. Brace expansion by itself is purely * textual, the expansions are not looked up in the file system. But if an * expanded word contains wildcard characters, it is expanded further, * matching only the actually existing files. @@ -683,42 +769,43 @@ static void DirExpandCurly(const char *word, const char *brace, SearchPath *path, StringList *expansions) { - const char *prefix, *middle, *piece, *middle_end, *suffix; - size_t prefix_len, suffix_len; + const char *prefix, *middle, *piece, *middle_end, *suffix; + size_t prefix_len, suffix_len; - /* Split the word into prefix '{' middle '}' suffix. */ + /* Split the word into prefix '{' middle '}' suffix. */ - middle = brace + 1; - middle_end = closing_brace(middle); - if (*middle_end == '\0') { - Error("Unterminated {} clause \"%s\"", middle); - return; - } + middle = brace + 1; + middle_end = closing_brace(middle); + if (*middle_end == '\0') { + Error("Unterminated {} clause \"%s\"", middle); + return; + } - prefix = word; - prefix_len = (size_t)(brace - prefix); - suffix = middle_end + 1; - suffix_len = strlen(suffix); + prefix = word; + prefix_len = (size_t)(brace - prefix); + suffix = middle_end + 1; + suffix_len = strlen(suffix); - /* Split the middle into pieces, separated by commas. */ + /* Split the middle into pieces, separated by commas. */ - piece = middle; - while (piece < middle_end + 1) { - const char *piece_end = separator_comma(piece); - size_t piece_len = (size_t)(piece_end - piece); + piece = middle; + while (piece < middle_end + 1) { + const char *piece_end = separator_comma(piece); + size_t piece_len = (size_t)(piece_end - piece); - char *file = concat3(prefix, prefix_len, piece, piece_len, - suffix, suffix_len); + char *file = concat3(prefix, prefix_len, piece, piece_len, + suffix, suffix_len); - if (contains_wildcard(file)) { - Dir_Expand(file, path, expansions); - free(file); - } else { - Lst_Append(expansions, file); - } + if (contains_wildcard(file)) { + Dir_Expand(file, path, expansions); + free(file); + } else { + Lst_Append(expansions, file); + } - piece = piece_end + 1; /* skip over the comma or closing brace */ - } + /* skip over the comma or closing brace */ + piece = piece_end + 1; + } } @@ -726,27 +813,28 @@ DirExpandCurly(const char *word, const char *brace, SearchPath *path, static void DirExpandPath(const char *word, SearchPath *path, StringList *expansions) { - SearchPathNode *ln; - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - DirMatchFiles(word, dir, expansions); - } + SearchPathNode *ln; + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + DirMatchFiles(word, dir, expansions); + } } static void PrintExpansions(StringList *expansions) { - const char *sep = ""; - StringListNode *ln; - for (ln = expansions->first; ln != NULL; ln = ln->next) { - const char *word = ln->datum; - debug_printf("%s%s", sep, word); - sep = " "; - } - debug_printf("\n"); + const char *sep = ""; + StringListNode *ln; + for (ln = expansions->first; ln != NULL; ln = ln->next) { + const char *word = ln->datum; + debug_printf("%s%s", sep, word); + sep = " "; + } + debug_printf("\n"); } -/* Expand the given word into a list of words by globbing it, looking in the +/* + * Expand the given word into a list of words by globbing it, looking in the * directories on the given search path. * * Input: @@ -757,185 +845,214 @@ PrintExpansions(StringList *expansions) void Dir_Expand(const char *word, SearchPath *path, StringList *expansions) { - const char *cp; + const char *cp; + + assert(path != NULL); + assert(expansions != NULL); + + DEBUG1(DIR, "Expanding \"%s\"... ", word); - assert(path != NULL); - assert(expansions != NULL); + cp = strchr(word, '{'); + if (cp != NULL) { + DirExpandCurly(word, cp, path, expansions); + goto done; + } - DIR_DEBUG1("Expanding \"%s\"... ", word); + /* At this point, the word does not contain '{'. */ - cp = strchr(word, '{'); - if (cp) { - DirExpandCurly(word, cp, path, expansions); - } else { cp = strchr(word, '/'); - if (cp) { - /* - * The thing has a directory component -- find the first wildcard - * in the string. - */ - for (cp = word; *cp; cp++) { - if (*cp == '?' || *cp == '[' || *cp == '*') { - break; - } - } + if (cp == NULL) { + /* The word has no directory component. */ + /* First the files in dot. */ + DirMatchFiles(word, dot, expansions); - if (*cp != '\0') { + /* Then the files in every other directory on the path. */ + DirExpandPath(word, path, expansions); + goto done; + } + + /* At this point, the word has a directory component. */ + + /* Find the first wildcard in the word. */ + for (cp = word; *cp != '\0'; cp++) + if (*cp == '?' || *cp == '[' || *cp == '*') + break; + + if (*cp == '\0') { /* - * Back up to the start of the component + * No directory component and no wildcard at all -- this + * should never happen as in such a simple case there is no + * need to expand anything. */ - while (cp > word && *cp != '/') { - cp--; - } - if (cp != word) { - char *prefix = bmake_strsedup(word, cp + 1); - /* - * If the glob isn't in the first component, try and find - * all the components up to the one with a wildcard. - */ - char *dirpath = Dir_FindFile(prefix, path); - free(prefix); - /* - * dirpath is null if can't find the leading component - * XXX: Dir_FindFile won't find internal components. - * i.e. if the path contains ../Etc/Object and we're - * looking for Etc, it won't be found. Ah well. - * Probably not important. - */ - if (dirpath != NULL) { - char *dp = &dirpath[strlen(dirpath) - 1]; - if (*dp == '/') - *dp = '\0'; - path = Lst_New(); - (void)Dir_AddDir(path, dirpath); - DirExpandPath(cp + 1, path, expansions); - Lst_Free(path); - } - } else { - /* - * Start the search from the local directory - */ - DirExpandPath(word, path, expansions); - } - } else { + DirExpandPath(word, path, expansions); + goto done; + } + + /* Back up to the start of the component containing the wildcard. */ + /* XXX: This handles '///' and '/' differently. */ + while (cp > word && *cp != '/') + cp--; + + if (cp == word) { + /* The first component contains the wildcard. */ + /* Start the search from the local directory */ + DirExpandPath(word, path, expansions); + goto done; + } + + { + char *prefix = bmake_strsedup(word, cp + 1); /* - * Return the file -- this should never happen. + * The wildcard isn't in the first component. + * Find all the components up to the one with the wildcard. */ - DirExpandPath(word, path, expansions); - } - } else { - /* - * First the files in dot - */ - DirMatchFiles(word, dot, expansions); + /* + * XXX: Check the "the directory is added to the path" part. + * It is probably surprising that the directory before a + * wildcard gets added to the path. + */ + /* + * XXX: Only the first match of the prefix in the path is + * taken, any others are ignored. The expectation may be + * that the pattern is expanded in the whole path. + */ + char *dirpath = Dir_FindFile(prefix, path); + free(prefix); + + /* + * dirpath is null if can't find the leading component + * XXX: Dir_FindFile won't find internal components. + * i.e. if the path contains ../Etc/Object and we're + * looking for Etc, it won't be found. Ah well. + * Probably not important. + * XXX: Check whether the above comment is still true. + */ + if (dirpath != NULL) { + SearchPath *partPath; + + char *end = &dirpath[strlen(dirpath) - 1]; + /* XXX: What about multiple trailing slashes? */ + if (*end == '/') + *end = '\0'; - /* - * Then the files in every other directory on the path. - */ - DirExpandPath(word, path, expansions); + partPath = SearchPath_New(); + (void)Dir_AddDir(partPath, dirpath); + DirExpandPath(cp + 1, partPath, expansions); + SearchPath_Free(partPath); + } } - } - if (DEBUG(DIR)) - PrintExpansions(expansions); + +done: + if (DEBUG(DIR)) + PrintExpansions(expansions); } -/* Find if the file with the given name exists in the given path. - * Return the freshly allocated path to the file, or NULL. */ +/* + * Find if the file with the given name exists in the given path. + * Return the freshly allocated path to the file, or NULL. + */ static char * DirLookup(CachedDir *dir, const char *base) { - char *file; /* the current filename to check */ + char *file; /* the current filename to check */ - DIR_DEBUG1(" %s ...\n", dir->name); + DEBUG1(DIR, " %s ...\n", dir->name); - if (HashTable_FindEntry(&dir->files, base) == NULL) - return NULL; + if (!HashSet_Contains(&dir->files, base)) + return NULL; - file = str_concat3(dir->name, "/", base); - DIR_DEBUG1(" returning %s\n", file); - dir->hits++; - hits++; - return file; + file = str_concat3(dir->name, "/", base); + DEBUG1(DIR, " returning %s\n", file); + dir->hits++; + hits++; + return file; } -/* Find if the file with the given name exists in the given directory. - * Return the freshly allocated path to the file, or NULL. */ +/* + * Find if the file with the given name exists in the given directory. + * Return the freshly allocated path to the file, or NULL. + */ static char * DirLookupSubdir(CachedDir *dir, const char *name) { - struct cached_stat cst; - char *file = dir == dot ? bmake_strdup(name) - : str_concat3(dir->name, "/", name); + struct cached_stat cst; + char *file = dir == dot ? bmake_strdup(name) + : str_concat3(dir->name, "/", name); - DIR_DEBUG1("checking %s ...\n", file); + DEBUG1(DIR, "checking %s ...\n", file); - if (cached_stat(file, &cst) == 0) { - nearmisses++; - return file; - } - free(file); - return NULL; + if (cached_stat(file, &cst) == 0) { + nearmisses++; + return file; + } + free(file); + return NULL; } -/* Find if the file with the given name exists in the given path. +/* + * Find if the file with the given name exists in the given path. * Return the freshly allocated path to the file, the empty string, or NULL. * Returning the empty string means that the search should be terminated. */ static char * DirLookupAbs(CachedDir *dir, const char *name, const char *cp) { - const char *dnp; /* pointer into dir->name */ - const char *np; /* pointer into name */ + const char *dnp; /* pointer into dir->name */ + const char *np; /* pointer into name */ - DIR_DEBUG1(" %s ...\n", dir->name); + DEBUG1(DIR, " %s ...\n", dir->name); - /* - * If the file has a leading path component and that component - * exactly matches the entire name of the current search - * directory, we can attempt another cache lookup. And if we don't - * have a hit, we can safely assume the file does not exist at all. - */ - for (dnp = dir->name, np = name; *dnp != '\0' && *dnp == *np; dnp++, np++) - continue; - if (*dnp != '\0' || np != cp - 1) - return NULL; + /* + * If the file has a leading path component and that component + * exactly matches the entire name of the current search + * directory, we can attempt another cache lookup. And if we don't + * have a hit, we can safely assume the file does not exist at all. + */ + for (dnp = dir->name, np = name; + *dnp != '\0' && *dnp == *np; dnp++, np++) + continue; + if (*dnp != '\0' || np != cp - 1) + return NULL; - if (HashTable_FindEntry(&dir->files, cp) == NULL) { - DIR_DEBUG0(" must be here but isn't -- returning\n"); - return bmake_strdup(""); /* to terminate the search */ - } + if (!HashSet_Contains(&dir->files, cp)) { + DEBUG0(DIR, " must be here but isn't -- returning\n"); + return bmake_strdup(""); /* to terminate the search */ + } - dir->hits++; - hits++; - DIR_DEBUG1(" returning %s\n", name); - return bmake_strdup(name); + dir->hits++; + hits++; + DEBUG1(DIR, " returning %s\n", name); + return bmake_strdup(name); } -/* Find the file given on "." or curdir. - * Return the freshly allocated path to the file, or NULL. */ +/* + * Find the file given on "." or curdir. + * Return the freshly allocated path to the file, or NULL. + */ static char * DirFindDot(const char *name, const char *base) { - if (HashTable_FindEntry(&dot->files, base) != NULL) { - DIR_DEBUG0(" in '.'\n"); - hits++; - dot->hits++; - return bmake_strdup(name); - } + if (HashSet_Contains(&dot->files, base)) { + DEBUG0(DIR, " in '.'\n"); + hits++; + dot->hits++; + return bmake_strdup(name); + } - if (cur != NULL && HashTable_FindEntry(&cur->files, base) != NULL) { - DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name); - hits++; - cur->hits++; - return str_concat3(cur->name, "/", base); - } + if (cur != NULL && HashSet_Contains(&cur->files, base)) { + DEBUG1(DIR, " in ${.CURDIR} = %s\n", cur->name); + hits++; + cur->hits++; + return str_concat3(cur->name, "/", base); + } - return NULL; + return NULL; } -/* Find the file with the given name along the given search path. +/* + * Find the file with the given name along the given search path. * * If the file is found in a directory that is not on the path * already (either 'name' is absolute or it is a relative path @@ -954,250 +1071,244 @@ DirFindDot(const char *name, const char *base) char * Dir_FindFile(const char *name, SearchPath *path) { - SearchPathNode *ln; - char *file; /* the current filename to check */ - const char *base; /* Terminal name of file */ - Boolean hasLastDot = FALSE; /* true if we should search dot last */ - Boolean hasSlash; /* true if 'name' contains a / */ - struct cached_stat cst; /* Buffer for stat, if necessary */ - const char *trailing_dot = "."; + char *file; /* the current filename to check */ + Boolean seenDotLast = FALSE; /* true if we should search dot last */ + struct cached_stat cst; /* Buffer for stat, if necessary */ + const char *trailing_dot = "."; + const char *base = str_basename(name); - /* - * Find the final component of the name and note whether it has a - * slash in it (the name, I mean) - */ - base = strrchr(name, '/'); - if (base) { - hasSlash = TRUE; - base++; - } else { - hasSlash = FALSE; - base = name; - } + DEBUG1(DIR, "Searching for %s ...", name); - DIR_DEBUG1("Searching for %s ...", name); - - if (path == NULL) { - DIR_DEBUG0("couldn't open path, file not found\n"); - misses++; - return NULL; - } + if (path == NULL) { + DEBUG0(DIR, "couldn't open path, file not found\n"); + misses++; + return NULL; + } - if ((ln = path->first) != NULL) { - CachedDir *dir = ln->datum; - if (dir == dotLast) { - hasLastDot = TRUE; - DIR_DEBUG0("[dot last]..."); + if (path->first != NULL) { + CachedDir *dir = path->first->datum; + if (dir == dotLast) { + seenDotLast = TRUE; + DEBUG0(DIR, "[dot last]..."); + } } - } - DIR_DEBUG0("\n"); + DEBUG0(DIR, "\n"); - /* - * If there's no leading directory components or if the leading - * directory component is exactly `./', consult the cached contents - * of each of the directories on the search path. - */ - if (!hasSlash || (base - name == 2 && *name == '.')) { /* - * We look through all the directories on the path seeking one which - * contains the final component of the given name. If such a beast - * is found, we concatenate the directory name and the final - * component and return the resulting string. If we don't find any - * such thing, we go on to phase two... - * - * No matter what, we always look for the file in the current - * directory before anywhere else (unless we found the magic - * DOTLAST path, in which case we search it last) and we *do not* - * add the ./ to it if it exists. - * This is so there are no conflicts between what the user - * specifies (fish.c) and what pmake finds (./fish.c). + * If there's no leading directory components or if the leading + * directory component is exactly `./', consult the cached contents + * of each of the directories on the search path. */ - if (!hasLastDot && (file = DirFindDot(name, base)) != NULL) - return file; + if (base == name || (base - name == 2 && *name == '.')) { + SearchPathNode *ln; - for (; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (dir == dotLast) - continue; - if ((file = DirLookup(dir, base)) != NULL) - return file; - } + /* + * We look through all the directories on the path seeking one + * which contains the final component of the given name. If + * such a beast is found, we concatenate the directory name + * and the final component and return the resulting string. + * If we don't find any such thing, we go on to phase two. + * + * No matter what, we always look for the file in the current + * directory before anywhere else (unless we found the magic + * DOTLAST path, in which case we search it last) and we *do + * not* add the ./ to it if it exists. + * This is so there are no conflicts between what the user + * specifies (fish.c) and what pmake finds (./fish.c). + */ + if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) + return file; - if (hasLastDot && (file = DirFindDot(name, base)) != NULL) - return file; - } + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (dir == dotLast) + continue; + if ((file = DirLookup(dir, base)) != NULL) + return file; + } - /* - * We didn't find the file on any directory in the search path. - * If the name doesn't contain a slash, that means it doesn't exist. - * If it *does* contain a slash, however, there is still hope: it - * could be in a subdirectory of one of the members of the search - * path. (eg. /usr/include and sys/types.h. The above search would - * fail to turn up types.h in /usr/include, but it *is* in - * /usr/include/sys/types.h). - * [ This no longer applies: If we find such a beast, we assume there - * will be more (what else can we assume?) and add all but the last - * component of the resulting name onto the search path (at the - * end).] - * This phase is only performed if the file is *not* absolute. - */ - if (!hasSlash) { - DIR_DEBUG0(" failed.\n"); - misses++; - return NULL; - } + if (seenDotLast && (file = DirFindDot(name, base)) != NULL) + return file; + } - if (*base == '\0') { - /* we were given a trailing "/" */ - base = trailing_dot; - } + /* + * We didn't find the file on any directory in the search path. + * If the name doesn't contain a slash, that means it doesn't exist. + * If it *does* contain a slash, however, there is still hope: it + * could be in a subdirectory of one of the members of the search + * path. (eg. /usr/include and sys/types.h. The above search would + * fail to turn up types.h in /usr/include, but it *is* in + * /usr/include/sys/types.h). + * [ This no longer applies: If we find such a beast, we assume there + * will be more (what else can we assume?) and add all but the last + * component of the resulting name onto the search path (at the + * end).] + * This phase is only performed if the file is *not* absolute. + */ + if (base == name) { + DEBUG0(DIR, " failed.\n"); + misses++; + return NULL; + } - if (name[0] != '/') { - Boolean checkedDot = FALSE; + if (*base == '\0') { + /* we were given a trailing "/" */ + base = trailing_dot; + } - DIR_DEBUG0(" Trying subdirectories...\n"); + if (name[0] != '/') { + SearchPathNode *ln; + Boolean checkedDot = FALSE; - if (!hasLastDot) { - if (dot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur && (file = DirLookupSubdir(cur, name)) != NULL) - return file; - } + DEBUG0(DIR, " Trying subdirectories...\n"); - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (dir == dotLast) - continue; - if (dir == dot) { - if (checkedDot) - continue; - checkedDot = TRUE; - } - if ((file = DirLookupSubdir(dir, name)) != NULL) - return file; - } + if (!seenDotLast) { + if (dot != NULL) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur != NULL && + (file = DirLookupSubdir(cur, name)) != NULL) + return file; + } - if (hasLastDot) { - if (dot && !checkedDot) { - checkedDot = TRUE; - if ((file = DirLookupSubdir(dot, name)) != NULL) - return file; - } - if (cur && (file = DirLookupSubdir(cur, name)) != NULL) - return file; - } + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (dir == dotLast) + continue; + if (dir == dot) { + if (checkedDot) + continue; + checkedDot = TRUE; + } + if ((file = DirLookupSubdir(dir, name)) != NULL) + return file; + } - if (checkedDot) { - /* - * Already checked by the given name, since . was in the path, - * so no point in proceeding... - */ - DIR_DEBUG0(" Checked . already, returning NULL\n"); - return NULL; - } + if (seenDotLast) { + if (dot != NULL && !checkedDot) { + checkedDot = TRUE; + if ((file = DirLookupSubdir(dot, name)) != NULL) + return file; + } + if (cur != NULL && + (file = DirLookupSubdir(cur, name)) != NULL) + return file; + } - } else { /* name[0] == '/' */ + if (checkedDot) { + /* + * Already checked by the given name, since . was in + * the path, so no point in proceeding. + */ + DEBUG0(DIR, " Checked . already, returning NULL\n"); + return NULL; + } - /* - * For absolute names, compare directory path prefix against the - * the directory path of each member on the search path for an exact - * match. If we have an exact match on any member of the search path, - * use the cached contents of that member to lookup the final file - * component. If that lookup fails we can safely assume that the - * file does not exist at all. This is signified by DirLookupAbs() - * returning an empty string. - */ - DIR_DEBUG0(" Trying exact path matches...\n"); + } else { /* name[0] == '/' */ + SearchPathNode *ln; - if (!hasLastDot && cur && - ((file = DirLookupAbs(cur, name, base)) != NULL)) { - if (file[0] == '\0') { - free(file); - return NULL; - } - return file; - } + /* + * For absolute names, compare directory path prefix against + * the the directory path of each member on the search path + * for an exact match. If we have an exact match on any member + * of the search path, use the cached contents of that member + * to lookup the final file component. If that lookup fails we + * can safely assume that the file does not exist at all. + * This is signified by DirLookupAbs() returning an empty + * string. + */ + DEBUG0(DIR, " Trying exact path matches...\n"); - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (dir == dotLast) - continue; - if ((file = DirLookupAbs(dir, name, base)) != NULL) { - if (file[0] == '\0') { - free(file); - return NULL; + if (!seenDotLast && cur != NULL && + ((file = DirLookupAbs(cur, name, base)) != NULL)) { + if (file[0] == '\0') { + free(file); + return NULL; + } + return file; } - return file; - } - } - if (hasLastDot && cur && - ((file = DirLookupAbs(cur, name, base)) != NULL)) { - if (file[0] == '\0') { - free(file); - return NULL; - } - return file; + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (dir == dotLast) + continue; + if ((file = DirLookupAbs(dir, name, base)) != NULL) { + if (file[0] == '\0') { + free(file); + return NULL; + } + return file; + } + } + + if (seenDotLast && cur != NULL && + ((file = DirLookupAbs(cur, name, base)) != NULL)) { + if (file[0] == '\0') { + free(file); + return NULL; + } + return file; + } } - } - /* - * Didn't find it that way, either. Sigh. Phase 3. Add its directory - * onto the search path in any case, just in case, then look for the - * thing in the hash table. If we find it, grand. We return a new - * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. - * Note that if the directory holding the file doesn't exist, this will - * do an extra search of the final directory on the path. Unless something - * weird happens, this search won't succeed and life will be groovy. - * - * Sigh. We cannot add the directory onto the search path because - * of this amusing case: - * $(INSTALLDIR)/$(FILE): $(FILE) - * - * $(FILE) exists in $(INSTALLDIR) but not in the current one. - * When searching for $(FILE), we will find it in $(INSTALLDIR) - * b/c we added it here. This is not good... - */ + /* + * Didn't find it that way, either. Sigh. Phase 3. Add its directory + * onto the search path in any case, just in case, then look for the + * thing in the hash table. If we find it, grand. We return a new + * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. + * Note that if the directory holding the file doesn't exist, this + * will do an extra search of the final directory on the path. Unless + * something weird happens, this search won't succeed and life will + * be groovy. + * + * Sigh. We cannot add the directory onto the search path because + * of this amusing case: + * $(INSTALLDIR)/$(FILE): $(FILE) + * + * $(FILE) exists in $(INSTALLDIR) but not in the current one. + * When searching for $(FILE), we will find it in $(INSTALLDIR) + * b/c we added it here. This is not good... + */ #if 0 - if (base == trailing_dot) { - base = strrchr(name, '/'); - base++; - } - base[-1] = '\0'; - (void)Dir_AddDir(path, name); - base[-1] = '/'; + { + CachedDir *dir; + char *prefix; - bigmisses++; - ln = Lst_Last(path); - if (ln == NULL) { - return NULL; - } else { - dir = LstNode_Datum(ln); - } + if (base == trailing_dot) { + base = strrchr(name, '/'); + base++; + } + prefix = bmake_strsedup(name, base - 1); + (void)Dir_AddDir(path, prefix); + free(prefix); - if (Hash_FindEntry(&dir->files, base) != NULL) { - return bmake_strdup(name); - } else { - return NULL; - } + bigmisses++; + if (path->last == NULL) + return NULL; + + dir = path->last->datum; + if (HashSet_Contains(&dir->files, base)) + return bmake_strdup(name); + return NULL; + } #else - DIR_DEBUG1(" Looking for \"%s\" ...\n", name); + DEBUG1(DIR, " Looking for \"%s\" ...\n", name); - bigmisses++; - if (cached_stat(name, &cst) == 0) { - return bmake_strdup(name); - } + bigmisses++; + if (cached_stat(name, &cst) == 0) { + return bmake_strdup(name); + } - DIR_DEBUG0(" failed. Returning NULL\n"); - return NULL; + DEBUG0(DIR, " failed. Returning NULL\n"); + return NULL; #endif } -/* Search for a path starting at a given directory and then working our way +/* + * Search for a path starting at a given directory and then working our way * up towards the root. * * Input: @@ -1210,346 +1321,376 @@ Dir_FindFile(const char *name, SearchPath *path) char * Dir_FindHereOrAbove(const char *here, const char *search_path) { - struct cached_stat cst; - char *dirbase, *dirbase_end; - char *try, *try_end; + struct cached_stat cst; + char *dirbase, *dirbase_end; + char *try, *try_end; + + /* copy out our starting point */ + dirbase = bmake_strdup(here); + dirbase_end = dirbase + strlen(dirbase); - /* copy out our starting point */ - dirbase = bmake_strdup(here); - dirbase_end = dirbase + strlen(dirbase); + /* loop until we determine a result */ + for (;;) { - /* loop until we determine a result */ - for (;;) { + /* try and stat(2) it ... */ + try = str_concat3(dirbase, "/", search_path); + if (cached_stat(try, &cst) != -1) { + /* + * success! if we found a file, chop off + * the filename so we return a directory. + */ + if ((cst.cst_mode & S_IFMT) != S_IFDIR) { + try_end = try + strlen(try); + while (try_end > try && *try_end != '/') + try_end--; + if (try_end > try) + *try_end = '\0'; /* chop! */ + } - /* try and stat(2) it ... */ - try = str_concat3(dirbase, "/", search_path); - if (cached_stat(try, &cst) != -1) { - /* - * success! if we found a file, chop off - * the filename so we return a directory. - */ - if ((cst.cst_mode & S_IFMT) != S_IFDIR) { - try_end = try + strlen(try); - while (try_end > try && *try_end != '/') - try_end--; - if (try_end > try) - *try_end = '\0'; /* chop! */ - } + free(dirbase); + return try; + } + free(try); - free(dirbase); - return try; + /* + * nope, we didn't find it. if we used up dirbase we've + * reached the root and failed. + */ + if (dirbase_end == dirbase) + break; /* failed! */ + + /* + * truncate dirbase from the end to move up a dir + */ + while (dirbase_end > dirbase && *dirbase_end != '/') + dirbase_end--; + *dirbase_end = '\0'; /* chop! */ } - free(try); + + free(dirbase); + return NULL; +} + +/* + * This is an implied source, and it may have moved, + * see if we can find it via the current .PATH + */ +static char * +ResolveMovedDepends(GNode *gn) +{ + char *fullName; + + const char *base = str_basename(gn->name); + if (base == gn->name) + return NULL; + + fullName = Dir_FindFile(base, Suff_FindPath(gn)); + if (fullName == NULL) + return NULL; /* - * nope, we didn't find it. if we used up dirbase we've - * reached the root and failed. + * Put the found file in gn->path so that we give that to the compiler. */ - if (dirbase_end == dirbase) - break; /* failed! */ - /* - * truncate dirbase from the end to move up a dir + * XXX: Better just reset gn->path to NULL; updating it is already done + * by Dir_UpdateMTime. */ - while (dirbase_end > dirbase && *dirbase_end != '/') - dirbase_end--; - *dirbase_end = '\0'; /* chop! */ - } + gn->path = bmake_strdup(fullName); + if (!Job_RunTarget(".STALE", gn->fname)) + fprintf(stdout, /* XXX: Why stdout? */ + "%s: %s, %d: ignoring stale %s for %s, found %s\n", + progname, gn->fname, gn->lineno, + makeDependfile, gn->name, fullName); - free(dirbase); - return NULL; + return fullName; } -/* Search gn along dirSearchPath and store its modification time in gn->mtime. +static char * +ResolveFullName(GNode *gn) +{ + char *fullName; + + fullName = gn->path; + if (fullName == NULL && !(gn->type & OP_NOPATH)) { + + fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); + + if (fullName == NULL && gn->flags & FROM_DEPEND && + !Lst_IsEmpty(&gn->implicitParents)) + fullName = ResolveMovedDepends(gn); + + DEBUG2(DIR, "Found '%s' as '%s'\n", + gn->name, fullName != NULL ? fullName : "(not found)"); + } + + if (fullName == NULL) + fullName = bmake_strdup(gn->name); + + /* XXX: Is every piece of memory freed as it should? */ + + return fullName; +} + +/* + * Search gn along dirSearchPath and store its modification time in gn->mtime. * If no file is found, store 0 instead. * - * The found file is stored in gn->path, unless the node already had a path. */ + * The found file is stored in gn->path, unless the node already had a path. + */ void Dir_UpdateMTime(GNode *gn, Boolean recheck) { - char *fullName; - struct cached_stat cst; - - if (gn->type & OP_ARCHV) { - Arch_UpdateMTime(gn); - return; - } + char *fullName; + struct cached_stat cst; - if (gn->type & OP_PHONY) { - gn->mtime = 0; - return; - } + if (gn->type & OP_ARCHV) { + Arch_UpdateMTime(gn); + return; + } - if (gn->path == NULL) { - if (gn->type & OP_NOPATH) - fullName = NULL; - else { - fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); - if (fullName == NULL && gn->flags & FROM_DEPEND && - !Lst_IsEmpty(gn->implicitParents)) { - char *cp; + if (gn->type & OP_PHONY) { + gn->mtime = 0; + return; + } - cp = strrchr(gn->name, '/'); - if (cp) { - /* - * This is an implied source, and it may have moved, - * see if we can find it via the current .PATH - */ - cp++; + fullName = ResolveFullName(gn); - fullName = Dir_FindFile(cp, Suff_FindPath(gn)); - if (fullName) { - /* - * Put the found file in gn->path - * so that we give that to the compiler. - */ - gn->path = bmake_strdup(fullName); - if (!Job_RunTarget(".STALE", gn->fname)) - fprintf(stdout, - "%s: %s, %d: ignoring stale %s for %s, " - "found %s\n", progname, gn->fname, - gn->lineno, - makeDependfile, gn->name, fullName); - } + if (cached_stats(fullName, &cst, recheck ? CST_UPDATE : CST_NONE) < 0) { + if (gn->type & OP_MEMBER) { + if (fullName != gn->path) + free(fullName); + Arch_UpdateMemberMTime(gn); + return; } - } - DIR_DEBUG2("Found '%s' as '%s'\n", - gn->name, fullName ? fullName : "(not found)"); + + cst.cst_mtime = 0; } - } else { - fullName = gn->path; - } - if (fullName == NULL) - fullName = bmake_strdup(gn->name); + if (fullName != NULL && gn->path == NULL) + gn->path = fullName; + /* XXX: else free(fullName)? */ + + gn->mtime = cst.cst_mtime; +} - if (cached_stats(fullName, &cst, recheck ? CST_UPDATE : CST_NONE) < 0) { - if (gn->type & OP_MEMBER) { - if (fullName != gn->path) - free(fullName); - Arch_UpdateMemberMTime(gn); - return; +/* + * Read the directory and add it to the cache in openDirs. + * If a path is given, add the directory to that path as well. + */ +static CachedDir * +CacheNewDir(const char *name, SearchPath *path) +{ + CachedDir *dir = NULL; + DIR *d; + struct dirent *dp; + + if ((d = opendir(name)) == NULL) { + DEBUG1(DIR, "Caching %s ... not found\n", name); + return dir; } - cst.cst_mtime = 0; - } + DEBUG1(DIR, "Caching %s ...\n", name); + + dir = CachedDir_New(name); - if (fullName != NULL && gn->path == NULL) - gn->path = fullName; + while ((dp = readdir(d)) != NULL) { - gn->mtime = cst.cst_mtime; +#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ + /* + * The sun directory library doesn't check for a 0 inode + * (0-inode slots just take up space), so we have to do + * it ourselves. + */ + if (dp->d_fileno == 0) + continue; +#endif /* sun && d_ino */ + + (void)HashSet_Add(&dir->files, dp->d_name); + } + (void)closedir(d); + + OpenDirs_Add(&openDirs, dir); + if (path != NULL) + Lst_Append(path, CachedDir_Ref(dir)); + + DEBUG1(DIR, "Caching %s done\n", name); + return dir; } -/* Read the list of filenames in the directory and store the result - * in openDirectories. +/* + * Read the list of filenames in the directory and store the result + * in openDirs. * * If a path is given, append the directory to that path. * * Input: * path The path to which the directory should be - * added, or NULL to only add the directory to - * openDirectories + * added, or NULL to only add the directory to openDirs * name The name of the directory to add. * The name is not normalized in any way. + * Output: + * result If no path is given and the directory exists, the + * returned CachedDir has a reference count of 0. It + * must either be assigned to a variable using + * CachedDir_Assign or be appended to a SearchPath using + * Lst_Append and CachedDir_Ref. */ CachedDir * Dir_AddDir(SearchPath *path, const char *name) { - CachedDir *dir = NULL; /* the added directory */ - DIR *d; - struct dirent *dp; - if (path != NULL && strcmp(name, ".DOTLAST") == 0) { - SearchPathNode *ln; + if (path != NULL && strcmp(name, ".DOTLAST") == 0) { + SearchPathNode *ln; - /* XXX: Linear search gets slow with thousands of entries. */ - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *pathDir = ln->datum; - if (strcmp(pathDir->name, name) == 0) - return pathDir; - } - - dotLast->refCount++; - Lst_Prepend(path, dotLast); - } + /* XXX: Linear search gets slow with thousands of entries. */ + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *pathDir = ln->datum; + if (strcmp(pathDir->name, name) == 0) + return pathDir; + } - if (path != NULL) - dir = OpenDirs_Find(&openDirs, name); - if (dir != NULL) { - if (Lst_FindDatum(path, dir) == NULL) { - dir->refCount++; - Lst_Append(path, dir); + Lst_Prepend(path, CachedDir_Ref(dotLast)); } - return dir; - } - - DIR_DEBUG1("Caching %s ...", name); - if ((d = opendir(name)) != NULL) { - dir = bmake_malloc(sizeof *dir); - dir->name = bmake_strdup(name); - dir->hits = 0; - dir->refCount = 1; - HashTable_Init(&dir->files); - - while ((dp = readdir(d)) != NULL) { -#if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ - /* - * The sun directory library doesn't check for a 0 inode - * (0-inode slots just take up space), so we have to do - * it ourselves. - */ - if (dp->d_fileno == 0) { - continue; - } -#endif /* sun && d_ino */ - (void)HashTable_CreateEntry(&dir->files, dp->d_name, NULL); + if (path != NULL) { + /* XXX: Why is OpenDirs only checked if path != NULL? */ + CachedDir *dir = OpenDirs_Find(&openDirs, name); + if (dir != NULL) { + if (Lst_FindDatum(path, dir) == NULL) + Lst_Append(path, CachedDir_Ref(dir)); + return dir; + } } - (void)closedir(d); - OpenDirs_Add(&openDirs, dir); - if (path != NULL) - Lst_Append(path, dir); - } - DIR_DEBUG0("done\n"); - return dir; + + return CacheNewDir(name, path); } -/* Return a copy of dirSearchPath, incrementing the reference counts for - * the contained directories. */ +/* + * Return a copy of dirSearchPath, incrementing the reference counts for + * the contained directories. + */ SearchPath * Dir_CopyDirSearchPath(void) { - SearchPath *path = Lst_New(); - SearchPathNode *ln; - for (ln = dirSearchPath->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - dir->refCount++; - Lst_Append(path, dir); - } - return path; + SearchPath *path = SearchPath_New(); + SearchPathNode *ln; + for (ln = dirSearchPath.first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + Lst_Append(path, CachedDir_Ref(dir)); + } + return path; } -/*- - *----------------------------------------------------------------------- - * Dir_MakeFlags -- - * Make a string by taking all the directories in the given search - * path and preceding them by the given flag. Used by the suffix - * module to create variables for compilers based on suffix search - * paths. +/* + * Make a string by taking all the directories in the given search path and + * preceding them by the given flag. Used by the suffix module to create + * variables for compilers based on suffix search paths. * * Input: * flag flag which should precede each directory * path list of directories * * Results: - * The string mentioned above. Note that there is no space between - * the given flag and each directory. The empty string is returned if - * Things don't go well. - * - * Side Effects: - * None - *----------------------------------------------------------------------- + * The string mentioned above. Note that there is no space between the + * given flag and each directory. The empty string is returned if things + * don't go well. */ char * -Dir_MakeFlags(const char *flag, SearchPath *path) +SearchPath_ToFlags(const char *flag, SearchPath *path) { - Buffer buf; - SearchPathNode *ln; + Buffer buf; + SearchPathNode *ln; - Buf_Init(&buf); + Buf_Init(&buf); - if (path != NULL) { - for (ln = path->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - Buf_AddStr(&buf, " "); - Buf_AddStr(&buf, flag); - Buf_AddStr(&buf, dir->name); + if (path != NULL) { + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + Buf_AddStr(&buf, " "); + Buf_AddStr(&buf, flag); + Buf_AddStr(&buf, dir->name); + } } - } - return Buf_Destroy(&buf, FALSE); + return Buf_Destroy(&buf, FALSE); } -/* Nuke a directory descriptor, if possible. Callback procedure for the - * suffixes module when destroying a search path. - * - * Input: - * dirp The directory descriptor to nuke - */ +/* Free the search path and all directories mentioned in it. */ void -Dir_Destroy(void *dirp) +SearchPath_Free(SearchPath *path) { - CachedDir *dir = dirp; - dir->refCount--; - - if (dir->refCount == 0) { - OpenDirs_Remove(&openDirs, dir->name); + SearchPathNode *ln; - HashTable_Done(&dir->files); - free(dir->name); - free(dir); - } + for (ln = path->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + CachedDir_Unref(dir); + } + Lst_Free(path); } -/* Clear out all elements from the given search path. - * The path is set to the empty list but is not destroyed. */ +/* + * Clear out all elements from the given search path. + * The path is set to the empty list but is not destroyed. + */ void -Dir_ClearPath(SearchPath *path) +SearchPath_Clear(SearchPath *path) { - while (!Lst_IsEmpty(path)) { - CachedDir *dir = Lst_Dequeue(path); - Dir_Destroy(dir); - } + while (!Lst_IsEmpty(path)) { + CachedDir *dir = Lst_Dequeue(path); + CachedDir_Unref(dir); + } } -/* Concatenate two paths, adding the second to the end of the first, - * skipping duplicates. */ +/* + * Concatenate two paths, adding the second to the end of the first, + * skipping duplicates. + */ void -Dir_Concat(SearchPath *dst, SearchPath *src) +SearchPath_AddAll(SearchPath *dst, SearchPath *src) { - SearchPathNode *ln; + SearchPathNode *ln; - for (ln = src->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - if (Lst_FindDatum(dst, dir) == NULL) { - dir->refCount++; - Lst_Append(dst, dir); + for (ln = src->first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + if (Lst_FindDatum(dst, dir) == NULL) + Lst_Append(dst, CachedDir_Ref(dir)); } - } } static int percentage(int num, int den) { - return den != 0 ? num * 100 / den : 0; + return den != 0 ? num * 100 / den : 0; } /********** DEBUG INFO **********/ void Dir_PrintDirectories(void) { - CachedDirListNode *ln; + CachedDirListNode *ln; - debug_printf("#*** Directory Cache:\n"); - debug_printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", - hits, misses, nearmisses, bigmisses, - percentage(hits, hits + bigmisses + nearmisses)); - debug_printf("# %-20s referenced\thits\n", "directory"); + debug_printf("#*** Directory Cache:\n"); + debug_printf( + "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", + hits, misses, nearmisses, bigmisses, + percentage(hits, hits + bigmisses + nearmisses)); + debug_printf("# refs hits directory\n"); - for (ln = openDirs.list->first; ln != NULL; ln = ln->next) { - CachedDir *dir = ln->datum; - debug_printf("# %-20s %10d\t%4d\n", dir->name, dir->refCount, - dir->hits); - } + for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { + CachedDir *dir = ln->datum; + debug_printf("# %4d %4d %s\n", + dir->refCount, dir->hits, dir->name); + } } void -Dir_PrintPath(SearchPath *path) +SearchPath_Print(SearchPath *path) { - SearchPathNode *node; - for (node = path->first; node != NULL; node = node->next) { - const CachedDir *dir = node->datum; - debug_printf("%s ", dir->name); - } + SearchPathNode *ln; + + for (ln = path->first; ln != NULL; ln = ln->next) { + const CachedDir *dir = ln->datum; + debug_printf("%s ", dir->name); + } } |
