[BACK]Return to dir.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / make

Annotation of src/usr.bin/make/dir.c, Revision 1.32

1.31      espie       1: /*     $OpenPackages$ */
                      2: /*     $OpenBSD$ */
1.7       millert     3: /*     $NetBSD: dir.c,v 1.14 1997/03/29 16:51:26 christos Exp $        */
1.1       deraadt     4:
                      5: /*
1.31      espie       6:  * Copyright (c) 1999 Marc Espie.
                      7:  *
                      8:  * Extensive code changes for the OpenBSD project.
                      9:  *
                     10:  * Redistribution and use in source and binary forms, with or without
                     11:  * modification, are permitted provided that the following conditions
                     12:  * are met:
                     13:  * 1. Redistributions of source code must retain the above copyright
                     14:  *    notice, this list of conditions and the following disclaimer.
                     15:  * 2. Redistributions in binary form must reproduce the above copyright
                     16:  *    notice, this list of conditions and the following disclaimer in the
                     17:  *    documentation and/or other materials provided with the distribution.
                     18:  *
                     19:  * THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS
                     20:  * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
                     21:  * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
                     22:  * A PARTICULAR PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE OPENBSD
                     23:  * PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
                     24:  * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
                     25:  * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
                     26:  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
                     27:  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
                     28:  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
                     29:  * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
                     30:  */
                     31: /*
1.1       deraadt    32:  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
                     33:  * Copyright (c) 1988, 1989 by Adam de Boor
                     34:  * Copyright (c) 1989 by Berkeley Softworks
                     35:  * All rights reserved.
                     36:  *
                     37:  * This code is derived from software contributed to Berkeley by
                     38:  * Adam de Boor.
                     39:  *
                     40:  * Redistribution and use in source and binary forms, with or without
                     41:  * modification, are permitted provided that the following conditions
                     42:  * are met:
                     43:  * 1. Redistributions of source code must retain the above copyright
                     44:  *    notice, this list of conditions and the following disclaimer.
                     45:  * 2. Redistributions in binary form must reproduce the above copyright
                     46:  *    notice, this list of conditions and the following disclaimer in the
                     47:  *    documentation and/or other materials provided with the distribution.
                     48:  * 3. All advertising materials mentioning features or use of this software
                     49:  *    must display the following acknowledgement:
                     50:  *     This product includes software developed by the University of
                     51:  *     California, Berkeley and its contributors.
                     52:  * 4. Neither the name of the University nor the names of its contributors
                     53:  *    may be used to endorse or promote products derived from this software
                     54:  *    without specific prior written permission.
                     55:  *
                     56:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     57:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     58:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     59:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     60:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     61:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     62:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     63:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     64:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     65:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     66:  * SUCH DAMAGE.
                     67:  */
                     68:
1.32    ! espie      69: #include <sys/types.h>
        !            70: #include <sys/stat.h>
        !            71: #include <dirent.h>
1.25      espie      72: #include <stddef.h>
1.1       deraadt    73: #include <stdio.h>
1.32    ! espie      74: #include <string.h>
        !            75: #include "config.h"
        !            76: #include "defines.h"
1.25      espie      77: #include "ohash.h"
1.1       deraadt    78: #include "dir.h"
1.32    ! espie      79: #include "lst.h"
        !            80: #include "memory.h"
        !            81: #include "buf.h"
        !            82: #include "gnode.h"
        !            83: #include "arch.h"
        !            84: #include "targ.h"
        !            85: #include "error.h"
        !            86: #include "str.h"
        !            87: #include "timestamp.h"
        !            88:
        !            89:
        !            90: typedef struct Path_ {
        !            91:     int          refCount;     /* Number of paths with this directory */
        !            92: #ifdef DEBUG_DIRECTORY_CACHE
        !            93:     int          hits;         /* the number of times a file in this
        !            94:                                 * directory has been found */
1.24      espie      95: #endif
1.32    ! espie      96:     struct ohash   files;      /* Hash table of files in directory */
        !            97:     char         name[1];      /* Name of directory */
        !            98: } Path;
1.1       deraadt    99:
1.31      espie     100: /*     A search path consists of a Lst of Path structures. A Path structure
1.1       deraadt   101:  *     has in it the name of the directory and a hash table of all the files
                    102:  *     in the directory. This is used to cut down on the number of system
                    103:  *     calls necessary to find implicit dependents and their like. Since
                    104:  *     these searches are made before any actions are taken, we need not
                    105:  *     worry about the directory changing due to creation commands. If this
                    106:  *     hampers the style of some makefiles, they must be changed.
                    107:  *
                    108:  *     A list of all previously-read directories is kept in the
1.25      espie     109:  *     openDirectories cache.
1.1       deraadt   110:  *
                    111:  *     The need for the caching of whole directories is brought about by
                    112:  *     the multi-level transformation code in suff.c, which tends to search
                    113:  *     for far more files than regular make does. In the initial
                    114:  *     implementation, the amount of time spent performing "stat" calls was
                    115:  *     truly astronomical. The problem with hashing at the start is,
                    116:  *     of course, that pmake doesn't then detect changes to these directories
                    117:  *     during the course of the make. Three possibilities suggest themselves:
                    118:  *
                    119:  *         1) just use stat to test for a file's existence. As mentioned
                    120:  *            above, this is very inefficient due to the number of checks
                    121:  *            engendered by the multi-level transformation code.
                    122:  *         2) use readdir() and company to search the directories, keeping
                    123:  *            them open between checks. I have tried this and while it
                    124:  *            didn't slow down the process too much, it could severely
                    125:  *            affect the amount of parallelism available as each directory
                    126:  *            open would take another file descriptor out of play for
                    127:  *            handling I/O for another job. Given that it is only recently
                    128:  *            that UNIX OS's have taken to allowing more than 20 or 32
                    129:  *            file descriptors for a process, this doesn't seem acceptable
                    130:  *            to me.
                    131:  *         3) record the mtime of the directory in the Path structure and
                    132:  *            verify the directory hasn't changed since the contents were
                    133:  *            hashed. This will catch the creation or deletion of files,
                    134:  *            but not the updating of files. However, since it is the
                    135:  *            creation and deletion that is the problem, this could be
                    136:  *            a good thing to do. Unfortunately, if the directory (say ".")
                    137:  *            were fairly large and changed fairly frequently, the constant
                    138:  *            rehashing could seriously degrade performance. It might be
                    139:  *            good in such cases to keep track of the number of rehashes
                    140:  *            and if the number goes over a (small) limit, resort to using
                    141:  *            stat in its place.
                    142:  *
                    143:  *     An additional thing to consider is that pmake is used primarily
                    144:  *     to create C programs and until recently pcc-based compilers refused
                    145:  *     to allow you to specify where the resulting object file should be
                    146:  *     placed. This forced all objects to be created in the current
                    147:  *     directory. This isn't meant as a full excuse, just an explanation of
                    148:  *     some of the reasons for the caching used here.
                    149:  *
                    150:  *     One more note: the location of a target's file is only performed
                    151:  *     on the downward traversal of the graph and then only for terminal
                    152:  *     nodes in the graph. This could be construed as wrong in some cases,
                    153:  *     but prevents inadvertent modification of files when the "installed"
                    154:  *     directory for a file is provided in the search path.
                    155:  *
                    156:  *     Another data structure maintained by this module is an mtime
                    157:  *     cache used when the searching of cached directories fails to find
                    158:  *     a file. In the past, Dir_FindFile would simply perform an access()
                    159:  *     call in such a case to determine if the file could be found using
                    160:  *     just the name given. When this hit, however, all that was gained
                    161:  *     was the knowledge that the file existed. Given that an access() is
                    162:  *     essentially a stat() without the copyout() call, and that the same
                    163:  *     filesystem overhead would have to be incurred in Dir_MTime, it made
                    164:  *     sense to replace the access() with a stat() and record the mtime
1.31      espie     165:  *     in a cache for when Dir_MTime was actually called.  */
1.1       deraadt   166:
1.32    ! espie     167: static LIST   thedirSearchPath;                /* main search path */
        !           168: Lst          dirSearchPath= &thedirSearchPath;
1.1       deraadt   169:
1.32    ! espie     170: #ifdef DEBUG_DIRECTORY_CACHE
1.31      espie     171: /* Variables for gathering statistics on the efficiency of the hashing
                    172:  * mechanism.  */
                    173: static int    hits,                    /* Found in directory cache */
                    174:              misses,                   /* Sad, but not evil misses */
                    175:              nearmisses,               /* Found under search path */
                    176:              bigmisses;                /* Sought by itself */
1.32    ! espie     177: #endif
1.1       deraadt   178:
1.31      espie     179: static Path      *dot;                 /* contents of current directory */
1.27      espie     180:
                    181: struct file_stamp {
                    182:        TIMESTAMP mtime;                /* time stamp... */
                    183:        char name[1];                   /* ...for that file.  */
                    184: };
                    185:
1.31      espie     186: static struct ohash   openDirectories; /* cache all open directories */
                    187:
1.32    ! espie     188: /* Global structure used to cache mtimes.  XXX We don't cache an mtime
        !           189:  * before a caller actually looks up for the given time, because of the
        !           190:  * possibility a caller might update the file and invalidate the cache
        !           191:  * entry, and we don't look up in this cache except as a last resort.
        !           192:  */
        !           193: static struct ohash mtimes;
1.1       deraadt   194:
1.31      espie     195:
1.27      espie     196: /* There are three distinct hash structures:
                    197:  * - to collate files's last modification times (global mtimes)
                    198:  * - to collate file names (in each Path structure)
                    199:  * - to collate known directories (global openDirectories).  */
1.30      espie     200: static struct ohash_info stamp_info = { offsetof(struct file_stamp, name),
1.27      espie     201:     NULL, hash_alloc, hash_free, element_alloc };
                    202:
1.31      espie     203: static struct ohash_info file_info = { 0,
1.26      espie     204:     NULL, hash_alloc, hash_free, element_alloc };
                    205:
1.31      espie     206: static struct ohash_info dir_info = { offsetof(Path, name),
1.25      espie     207:     NULL, hash_alloc, hash_free, element_alloc };
1.1       deraadt   208:
1.32    ! espie     209: /* add_file(path, name): add a file name to a path hash structure. */
1.31      espie     210: static void add_file(Path *, const char *);
1.32    ! espie     211: /* n = find_file_hashi(p, name, end, hv): retrieve name in a path hash
        !           212:  *     structure. */
        !           213: static char *find_file_hashi(Path *, const char *, const char *, u_int32_t);
        !           214:
        !           215: /* stamp = find_stampi(name, end): look for (name, end) in the global
        !           216:  *     cache. */
1.31      espie     217: static struct file_stamp *find_stampi(const char *, const char *);
1.32    ! espie     218: /* record_stamp(name, timestamp): record timestamp for name in the global
        !           219:  *     cache. */
        !           220: static void record_stamp(const char *, TIMESTAMP);
        !           221:
        !           222: /* free_hash(o): free a ohash structure, where each element can be free'd. */
1.31      espie     223: static void free_hash(struct ohash *);
                    224:
1.32    ! espie     225: /* p = DirReaddiri(name, end): read an actual directory, caching results
        !           226:  *     as we go.  */
        !           227: static Path *DirReaddiri(const char *, const char *);
        !           228: /* Handles wildcard expansion on a given directory. */
        !           229: static void DirMatchFilesi(const char *, const char *, Path *, Lst);
        !           230: /* Handles simple wildcard expansion on a path. */
        !           231: static void PathMatchFilesi(const char *, const char *, Lst, Lst);
        !           232: /* Handles wildcards expansion except for curly braces. */
        !           233: static void DirExpandWildi(const char *, const char *, Lst, Lst);
        !           234: #define DirExpandWild(s, l1, l2) DirExpandWildi(s, strchr(s, '\0'), l1, l2)
        !           235: /* Handles wildcard expansion including curly braces. */
        !           236: static void DirExpandCurlyi(const char *, const char *, Lst, Lst);
1.31      espie     237:
1.32    ! espie     238: /* Debugging: show each word in an expansion list. */
1.31      espie     239: static void DirPrintWord(void *);
1.32    ! espie     240: /* Debugging: show a dir name in a path. */
1.31      espie     241: static void DirPrintDir(void *);
1.1       deraadt   242:
1.26      espie     243: static void
1.27      espie     244: record_stamp(file, t)
1.31      espie     245:     const char         *file;
                    246:     TIMESTAMP          t;
1.27      espie     247: {
1.31      espie     248:     unsigned           slot;
                    249:     const char         *end = NULL;
                    250:     struct file_stamp  *n;
1.27      espie     251:
1.30      espie     252:     slot = ohash_qlookupi(&mtimes, file, &end);
                    253:     n = ohash_find(&mtimes, slot);
1.27      espie     254:     if (n)
                    255:        n->mtime = t;
                    256:     else {
1.30      espie     257:        n = ohash_create_entry(&stamp_info, file, &end);
1.27      espie     258:        n->mtime = t;
1.30      espie     259:        ohash_insert(&mtimes, slot, n);
1.27      espie     260:     }
                    261: }
1.31      espie     262:
1.27      espie     263: static struct file_stamp *
1.31      espie     264: find_stampi(file, end)
                    265:     const char *file;
                    266:     const char *end;
1.27      espie     267: {
1.31      espie     268:     return ohash_find(&mtimes, ohash_qlookupi(&mtimes, file, &end));
1.27      espie     269: }
                    270:
                    271: static void
1.26      espie     272: add_file(p, file)
1.31      espie     273:     Path               *p;
                    274:     const char         *file;
1.26      espie     275: {
1.31      espie     276:     unsigned           slot;
                    277:     const char         *end = NULL;
                    278:     char               *n;
                    279:     struct ohash       *h = &p->files;
1.26      espie     280:
1.30      espie     281:     slot = ohash_qlookupi(h, file, &end);
                    282:     n = ohash_find(h, slot);
1.26      espie     283:     if (n == NULL) {
1.30      espie     284:        n = ohash_create_entry(&file_info, file, &end);
                    285:        ohash_insert(h, slot, n);
1.26      espie     286:     }
                    287: }
1.31      espie     288:
1.26      espie     289: static char *
1.32    ! espie     290: find_file_hashi(p, file, e, hv)
1.26      espie     291:     Path               *p;
1.31      espie     292:     const char         *file;
                    293:     const char         *e;
1.26      espie     294:     u_int32_t          hv;
                    295: {
1.30      espie     296:     struct ohash       *h = &p->files;
1.26      espie     297:
1.30      espie     298:     return ohash_find(h, ohash_lookup_interval(h, file, e, hv));
1.26      espie     299: }
                    300:
                    301: static void
                    302: free_hash(h)
1.30      espie     303:     struct ohash       *h;
1.26      espie     304: {
1.31      espie     305:     void               *e;
                    306:     unsigned           i;
1.26      espie     307:
1.30      espie     308:     for (e = ohash_first(h, &i); e != NULL; e = ohash_next(h, &i))
1.26      espie     309:        free(e);
1.30      espie     310:     ohash_delete(h);
1.26      espie     311: }
                    312:
1.32    ! espie     313:
        !           314: /* Side Effects: cache the current directory */
1.1       deraadt   315: void
1.20      espie     316: Dir_Init()
1.1       deraadt   317: {
1.32    ! espie     318:     char *dotname = ".";
        !           319:
        !           320:     Lst_Init(dirSearchPath);
1.30      espie     321:     ohash_init(&openDirectories, 4, &dir_info);
                    322:     ohash_init(&mtimes, 4, &stamp_info);
1.6       millert   323:
1.32    ! espie     324:
        !           325:     dot = DirReaddiri(dotname, dotname+1);
1.1       deraadt   326:
1.31      espie     327:     if (!dot)
                    328:        Error("Can't access current directory");
                    329:
1.25      espie     330:     /* We always need to have dot around, so we increment its reference count
1.31      espie     331:      * to make sure it won't be destroyed.  */
1.25      espie     332:     dot->refCount++;
1.1       deraadt   333: }
                    334:
1.32    ! espie     335: #ifdef CLEANUP
1.1       deraadt   336: void
                    337: Dir_End()
                    338: {
1.25      espie     339:     struct Path *p;
                    340:     unsigned int i;
                    341:
                    342:     dot->refCount--;
1.17      espie     343:     Dir_Destroy(dot);
1.32    ! espie     344:     Lst_Destroy(dirSearchPath, Dir_Destroy);
1.30      espie     345:     for (p = ohash_first(&openDirectories, &i); p != NULL;
1.31      espie     346:        p = ohash_next(&openDirectories, &i))
1.25      espie     347:            Dir_Destroy(p);
1.30      espie     348:     ohash_delete(&openDirectories);
1.28      espie     349:     free_hash(&mtimes);
1.32    ! espie     350: }
1.9       espie     351: #endif
1.1       deraadt   352:
1.32    ! espie     353:
        !           354: /* XXX: This code is not 100% correct ([^]] fails) */
        !           355: bool
        !           356: Dir_HasWildcardsi(name, end)
        !           357:     const char         *name;
        !           358:     const char                 *end;
1.1       deraadt   359: {
1.31      espie     360:     const char         *cp;
1.32    ! espie     361:     bool               wild = false;
1.28      espie     362:     unsigned long      brace = 0, bracket = 0;
1.6       millert   363:
1.32    ! espie     364:     for (cp = name; cp != end; cp++) {
1.28      espie     365:        switch (*cp) {
1.1       deraadt   366:        case '{':
1.7       millert   367:            brace++;
1.32    ! espie     368:            wild = true;
1.7       millert   369:            break;
                    370:        case '}':
1.28      espie     371:            if (brace == 0)
1.32    ! espie     372:                return false;
1.7       millert   373:            brace--;
                    374:            break;
1.1       deraadt   375:        case '[':
1.7       millert   376:            bracket++;
1.32    ! espie     377:            wild = true;
1.7       millert   378:            break;
                    379:        case ']':
1.28      espie     380:            if (bracket == 0)
1.32    ! espie     381:                return false;
1.7       millert   382:            bracket--;
                    383:            break;
1.1       deraadt   384:        case '?':
                    385:        case '*':
1.32    ! espie     386:            wild = true;
1.7       millert   387:            break;
                    388:        default:
                    389:            break;
1.1       deraadt   390:        }
                    391:     }
1.28      espie     392:     return wild && bracket == 0 && brace == 0;
1.1       deraadt   393: }
                    394:
                    395: /*-
                    396:  *-----------------------------------------------------------------------
1.32    ! espie     397:  * DirMatchFilesi --
1.31      espie     398:  *     Given a pattern and a Path structure, see if any files
1.1       deraadt   399:  *     match the pattern and add their names to the 'expansions' list if
                    400:  *     any do. This is incomplete -- it doesn't take care of patterns like
                    401:  *     src / *src / *.c properly (just *.c on any of the directories), but it
                    402:  *     will do for now.
                    403:  *-----------------------------------------------------------------------
                    404:  */
1.26      espie     405: static void
1.32    ! espie     406: DirMatchFilesi(pattern, end, p, expansions)
1.31      espie     407:     const char         *pattern;       /* Pattern to look for */
1.32    ! espie     408:     const char         *end;           /* End of pattern */
1.31      espie     409:     Path               *p;             /* Directory to search */
                    410:     Lst                expansions;     /* Place to store the results */
                    411: {
                    412:     unsigned int       search;         /* Index into the directory's table */
                    413:     const char         *entry;         /* Current entry in the table */
1.32    ! espie     414:     bool               isDot;          /* Is the directory "." ? */
1.6       millert   415:
1.31      espie     416:     isDot = p->name[0] == '.' && p->name[1] == '\0';
1.6       millert   417:
1.30      espie     418:     for (entry = ohash_first(&p->files, &search); entry != NULL;
                    419:         entry = ohash_next(&p->files, &search)) {
1.31      espie     420:        /* See if the file matches the given pattern. We follow the UNIX
1.1       deraadt   421:         * convention that dot files will only be found if the pattern
1.31      espie     422:         * begins with a dot (the hashing scheme doesn't hash . or ..,
                    423:         * so they won't match `.*'.  */
                    424:        if (*pattern != '.' && *entry == '.')
                    425:            continue;
1.32    ! espie     426:        if (Str_Matchi(entry, strchr(entry, '\0'), pattern, end))
1.13      espie     427:            Lst_AtEnd(expansions,
1.32    ! espie     428:                isDot ? estrdup(entry) : Str_concat(p->name, entry, '/'));
1.1       deraadt   429:     }
                    430: }
                    431:
                    432: /*-
                    433:  *-----------------------------------------------------------------------
1.32    ! espie     434:  * PathMatchFilesi --
1.31      espie     435:  *     Traverse directories in the path, calling DirMatchFiles for each.
                    436:  *     NOTE: This doesn't handle patterns in directories.
1.1       deraadt   437:  *-----------------------------------------------------------------------
                    438:  */
                    439: static void
1.32    ! espie     440: PathMatchFilesi(word, end, path, expansions)
1.31      espie     441:     const char *word;          /* Word to expand */
1.32    ! espie     442:     const char  *end;          /* End of word */
1.31      espie     443:     Lst        path;           /* Path on which to look */
                    444:     Lst        expansions;     /* Place to store the result */
                    445: {
                    446:     LstNode    ln;             /* Current node */
1.1       deraadt   447:
1.31      espie     448:     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln))
1.32    ! espie     449:        DirMatchFilesi(word, end, (Path *)Lst_Datum(ln), expansions);
1.31      espie     450: }
1.1       deraadt   451:
1.31      espie     452: static void
                    453: DirPrintWord(word)
                    454:     void       *word;
                    455: {
                    456:     printf("%s ", (char *)word);
1.1       deraadt   457: }
                    458:
                    459: /*-
                    460:  *-----------------------------------------------------------------------
1.32    ! espie     461:  * DirExpandWildi:
1.31      espie     462:  *     Expand all wild cards in a fully qualified name, except for
                    463:  *     curly braces.
1.32    ! espie     464:  * Side-effect:
        !           465:  *     Will hash any directory in which a file is found, and add it to
        !           466:  *     the path, on the assumption that future lookups will find files
        !           467:  *     there as well.
1.1       deraadt   468:  *-----------------------------------------------------------------------
                    469:  */
                    470: static void
1.32    ! espie     471: DirExpandWildi(word, end, path, expansions)
1.31      espie     472:     const char *word;          /* the word to expand */
1.32    ! espie     473:     const char  *end;          /* end of word */
1.31      espie     474:     Lst        path;           /* the list of directories in which to find
                    475:                                 * the resulting files */
                    476:     Lst        expansions;     /* the list on which to place the results */
                    477: {
                    478:     const char *cp;
                    479:     const char *slash;         /* keep track of first slash before wildcard */
                    480:
1.32    ! espie     481:     slash = memchr(word, '/', end - word);
1.31      espie     482:     if (slash == NULL) {
                    483:        /* First the files in dot.  */
1.32    ! espie     484:        DirMatchFilesi(word, end, dot, expansions);
1.1       deraadt   485:
1.31      espie     486:        /* Then the files in every other directory on the path.  */
1.32    ! espie     487:        PathMatchFilesi(word, end, path, expansions);
1.31      espie     488:        return;
                    489:     }
                    490:     /* The thing has a directory component -- find the first wildcard
                    491:      * in the string.  */
                    492:     slash = word;
1.32    ! espie     493:     for (cp = word; cp != end; cp++) {
1.31      espie     494:        if (*cp == '/')
                    495:            slash = cp;
                    496:        if (*cp == '?' || *cp == '[' || *cp == '*') {
                    497:
                    498:            if (slash != word) {
                    499:                char    *dirpath;
                    500:
                    501:                /* If the glob isn't in the first component, try and find
                    502:                 * all the components up to the one with a wildcard.  */
                    503:                dirpath = Dir_FindFilei(word, slash+1, path);
                    504:                /* dirpath is null if we can't find the leading component
                    505:                 * XXX: Dir_FindFile won't find internal components.
                    506:                 * i.e. if the path contains ../Etc/Object and we're
                    507:                 * looking for Etc, it won't be found. */
                    508:                if (dirpath != NULL) {
1.32    ! espie     509:                    char *dp;
1.31      espie     510:                    LIST temp;
                    511:
1.32    ! espie     512:                    dp = strchr(dirpath, '\0');
        !           513:                    while (dp > dirpath && dp[-1] == '/')
        !           514:                        dp--;
        !           515:
1.31      espie     516:                    Lst_Init(&temp);
1.32    ! espie     517:                    Dir_AddDiri(&temp, dirpath, dp);
        !           518:                    PathMatchFilesi(slash+1, end, &temp, expansions);
1.31      espie     519:                    Lst_Destroy(&temp, NOFREE);
                    520:                }
                    521:            } else
                    522:                /* Start the search from the local directory.  */
1.32    ! espie     523:                PathMatchFilesi(word, end, path, expansions);
1.31      espie     524:            return;
                    525:        }
1.1       deraadt   526:     }
1.31      espie     527:     /* Return the file -- this should never happen.  */
1.32    ! espie     528:     PathMatchFilesi(word, end, path, expansions);
1.1       deraadt   529: }
                    530:
                    531: /*-
                    532:  *-----------------------------------------------------------------------
1.31      espie     533:  * DirExpandCurly --
                    534:  *     Expand curly braces like the C shell, and other wildcards as per
                    535:  *     Str_Match.
1.32    ! espie     536:  *     XXX: if curly expansion yields a result with
1.31      espie     537:  *     no wildcards, the result is placed on the list WITHOUT CHECKING
                    538:  *     FOR ITS EXISTENCE.
1.1       deraadt   539:  *-----------------------------------------------------------------------
                    540:  */
1.18      espie     541: static void
1.32    ! espie     542: DirExpandCurlyi(word, endw, path, expansions)
1.31      espie     543:     const char *word;          /* Entire word to expand */
1.32    ! espie     544:     const char  *endw;         /* End of word */
1.31      espie     545:     Lst        path;           /* Search path to use */
                    546:     Lst        expansions;     /* Place to store the expansions */
1.1       deraadt   547: {
1.31      espie     548:     const char *cp2;           /* Pointer for checking for wildcards in
                    549:                                 * expansion before calling Dir_Expand */
                    550:     LIST       curled;         /* Queue of words to expand */
                    551:     char       *toexpand;      /* Current word to expand */
1.32    ! espie     552:     bool       dowild;         /* Wildcard left after curlies ? */
1.31      espie     553:
                    554:     /* Determine once and for all if there is something else going on */
1.32    ! espie     555:     dowild = false;
        !           556:     for (cp2 = word; cp2 != endw; cp2++)
1.31      espie     557:        if (*cp2 == '*' || *cp2 == '?' || *cp2 == '[') {
1.32    ! espie     558:                dowild = true;
1.31      espie     559:                break;
                    560:        }
                    561:
                    562:     /* Prime queue with copy of initial word */
                    563:     Lst_Init(&curled);
1.32    ! espie     564:     Lst_EnQueue(&curled, Str_dupi(word, endw));
1.31      espie     565:     while ((toexpand = (char *)Lst_DeQueue(&curled)) != NULL) {
                    566:        const char      *brace;
                    567:        const char      *start; /* Start of current chunk of brace clause */
                    568:        const char      *end;   /* Character after the closing brace */
                    569:        int             bracelevel;
                    570:                                /* Keep track of nested braces. If we hit
                    571:                                 * the right brace with bracelevel == 0,
                    572:                                 * this is the end of the clause. */
                    573:        const char      *cp;    /* Current position in brace clause */
                    574:        size_t          otherLen;
                    575:                                /* The length of the non-curlied part of
                    576:                                 * the current expansion */
                    577:
                    578:        /* End case: no curly left to expand */
                    579:        brace = strchr(toexpand, '{');
                    580:        if (brace == NULL) {
                    581:            if (dowild) {
                    582:                DirExpandWild(toexpand, path, expansions);
                    583:                free(toexpand);
                    584:            } else
                    585:                Lst_AtEnd(expansions, toexpand);
                    586:        }
                    587:
                    588:        start = brace+1;
                    589:
                    590:        /* Find the end of the brace clause first, being wary of nested brace
                    591:         * clauses.  */
                    592:        for (end = start, bracelevel = 0;; end++) {
                    593:            if (*end == '{')
                    594:                bracelevel++;
                    595:            else if (*end == '\0') {
                    596:                Error("Unterminated {} clause \"%s\"", start);
                    597:                return;
                    598:            } else if (*end == '}' && bracelevel-- == 0)
                    599:                break;
                    600:        }
                    601:        end++;
                    602:        otherLen = brace - toexpand + strlen(end);
                    603:
                    604:        for (cp = start; cp < end; cp++) {
                    605:            char        *file;  /* To hold current expansion */
                    606:
                    607:                /* Find the end of the current expansion */
                    608:            bracelevel = 0;
                    609:            while (*cp != ',') {
                    610:                if (*cp == '{')
                    611:                    bracelevel++;
                    612:                else if (*cp == '}' && bracelevel-- <= 0)
                    613:                    break;
                    614:                cp++;
                    615:            }
                    616:            /* Build the current combination and enqueue it.  */
                    617:            file = emalloc(otherLen + cp - start + 1);
                    618:            if (brace != toexpand)
                    619:                memcpy(file, toexpand, brace-word);
                    620:            if (cp != start)
                    621:                memcpy(file+(brace-toexpand), start, cp-start);
                    622:            strcpy(file+(brace-toexpand)+(cp-start), end);
                    623:            Lst_EnQueue(&curled, file);
                    624:        }
                    625:     }
1.1       deraadt   626: }
                    627:
1.32    ! espie     628: /* Side effects:
        !           629:  *     Dir_Expandi will hash directories that were not yet visited */
1.1       deraadt   630: void
1.32    ! espie     631: Dir_Expandi(word, end, path, expansions)
1.31      espie     632:     const char *word;          /* the word to expand */
1.32    ! espie     633:     const char  *end;          /* end of word */
1.31      espie     634:     Lst        path;           /* the list of directories in which to find
                    635:                                 * the resulting files */
                    636:     Lst        expansions;     /* the list on which to place the results */
1.1       deraadt   637: {
1.31      espie     638:     const char *cp;
1.1       deraadt   639:
1.32    ! espie     640:     if (DEBUG(DIR)) {
        !           641:        char *s = Str_dupi(word, end);
        !           642:        printf("expanding \"%s\"...", s);
        !           643:        free(s);
        !           644:     }
1.6       millert   645:
1.32    ! espie     646:     cp = memchr(word, '{', end - word);
1.31      espie     647:     if (cp)
1.32    ! espie     648:        DirExpandCurlyi(word, end, path, expansions);
1.31      espie     649:     else
1.32    ! espie     650:        DirExpandWildi(word, end, path, expansions);
1.6       millert   651:
1.1       deraadt   652:     if (DEBUG(DIR)) {
1.18      espie     653:        Lst_Every(expansions, DirPrintWord);
1.1       deraadt   654:        fputc('\n', stdout);
                    655:     }
                    656: }
                    657:
1.32    ! espie     658:
1.1       deraadt   659: /*-
                    660:  * Side Effects:
                    661:  *     If the file is found in a directory which is not on the path
                    662:  *     already (either 'name' is absolute or it is a relative path
                    663:  *     [ dir1/.../dirn/file ] which exists below one of the directories
                    664:  *     already on the search path), its directory is added to the end
                    665:  *     of the path on the assumption that there will be more files in
1.32    ! espie     666:  *     that directory later on.
1.1       deraadt   667:  */
                    668: char *
1.31      espie     669: Dir_FindFilei(name, end, path)
1.32    ! espie     670:     const char         *name;
        !           671:     const char         *end;
        !           672:     Lst                path;
1.31      espie     673: {
1.32    ! espie     674:     Path               *p;     /* current path member */
1.31      espie     675:     char               *p1;    /* pointer into p->name */
                    676:     const char         *p2;    /* pointer into name */
                    677:     LstNode            ln;     /* a list element */
                    678:     char               *file;  /* the current filename to check */
                    679:     char               *temp;  /* index into file */
                    680:     const char         *cp;    /* index of first slash, if any */
1.32    ! espie     681:     bool               hasSlash;
1.31      espie     682:     struct stat        stb;    /* Buffer for stat, if necessary */
                    683:     struct file_stamp  *entry; /* Entry for mtimes table */
1.32    ! espie     684:     u_int32_t          hv;     /* hash value for last component in file name */
        !           685:     char               *q;     /* Str_dupi(name, end) */
1.31      espie     686:
1.32    ! espie     687:     /* Find the final component of the name and note whether name has a
        !           688:      * slash in it */
        !           689:     cp = Str_rchri(name, end, '/');
1.1       deraadt   690:     if (cp) {
1.32    ! espie     691:        hasSlash = true;
1.31      espie     692:        cp++;
1.1       deraadt   693:     } else {
1.32    ! espie     694:        hasSlash = false;
1.1       deraadt   695:        cp = name;
                    696:     }
1.6       millert   697:
1.31      espie     698:     hv = ohash_interval(cp, &end);
1.26      espie     699:
1.31      espie     700:     if (DEBUG(DIR))
1.1       deraadt   701:        printf("Searching for %s...", name);
1.31      espie     702:     /* No matter what, we always look for the file in the current directory
1.32    ! espie     703:      * before anywhere else and we always return exactly what the caller
        !           704:      * specified. */
1.1       deraadt   705:     if ((!hasSlash || (cp - name == 2 && *name == '.')) &&
1.32    ! espie     706:        find_file_hashi(dot, cp, end, hv) != NULL) {
1.26      espie     707:            if (DEBUG(DIR))
1.1       deraadt   708:                printf("in '.'\n");
1.32    ! espie     709: #ifdef DEBUG_DIRECTORY_CACHE
1.31      espie     710:            hits++;
                    711:            dot->hits++;
1.32    ! espie     712: #endif
        !           713:            return Str_dupi(name, end);
1.1       deraadt   714:     }
1.6       millert   715:
1.32    ! espie     716:     /* Then, we look through all the directories on path, seeking one
        !           717:      * containing the final component of name and whose final
        !           718:      * component(s) match name's initial component(s).
        !           719:      * If found, we concatenate the directory name and the
        !           720:      * final component and return the resulting string.  */
1.26      espie     721:     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
1.22      espie     722:        p = (Path *)Lst_Datum(ln);
1.26      espie     723:        if (DEBUG(DIR))
1.1       deraadt   724:            printf("%s...", p->name);
1.32    ! espie     725:        if (find_file_hashi(p, cp, end, hv) != NULL) {
1.26      espie     726:            if (DEBUG(DIR))
1.1       deraadt   727:                printf("here...");
                    728:            if (hasSlash) {
1.31      espie     729:                /* If the name had a slash, its initial components and p's
1.1       deraadt   730:                 * final components must match. This is false if a mismatch
                    731:                 * is encountered before all of the initial components
                    732:                 * have been checked (p2 > name at the end of the loop), or
                    733:                 * we matched only part of one of the components of p
1.31      espie     734:                 * along with all the rest of them (*p1 != '/').  */
                    735:                p1 = p->name + strlen(p->name) - 1;
1.1       deraadt   736:                p2 = cp - 2;
                    737:                while (p2 >= name && p1 >= p->name && *p1 == *p2) {
1.31      espie     738:                    p1--;
                    739:                    p2--;
1.1       deraadt   740:                }
                    741:                if (p2 >= name || (p1 >= p->name && *p1 != '/')) {
1.26      espie     742:                    if (DEBUG(DIR))
1.1       deraadt   743:                        printf("component mismatch -- continuing...");
                    744:                    continue;
                    745:                }
                    746:            }
1.32    ! espie     747:            file = Str_concati(p->name, strchr(p->name, '\0'), cp, end, '/');
1.26      espie     748:            if (DEBUG(DIR))
1.1       deraadt   749:                printf("returning %s\n", file);
1.32    ! espie     750: #ifdef DEBUG_DIRECTORY_CACHE
1.31      espie     751:            p->hits++;
                    752:            hits++;
1.32    ! espie     753: #endif
1.26      espie     754:            return file;
1.1       deraadt   755:        } else if (hasSlash) {
1.26      espie     756:            /* If the file has a leading path component and that component
1.1       deraadt   757:             * exactly matches the entire name of the current search
1.26      espie     758:             * directory, we assume the file doesn't exist and return NULL.  */
1.31      espie     759:            for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++)
1.1       deraadt   760:                continue;
                    761:            if (*p1 == '\0' && p2 == cp - 1) {
1.26      espie     762:                if (DEBUG(DIR))
1.31      espie     763:                    printf("has to be here but isn't -- returning NULL\n");
1.26      espie     764:                return NULL;
1.1       deraadt   765:            }
                    766:        }
                    767:     }
1.6       millert   768:
1.32    ! espie     769:     /* We didn't find the file on any existing member of the path.
        !           770:      * If the name doesn't contain a slash, end of story.
        !           771:      * If it does contain a slash, however, it could be in a subdirectory
        !           772:      * of one of the members of the search path. (eg., for path=/usr/include
        !           773:      * and name=sys/types.h, the above search fails to turn up types.h
        !           774:      * in /usr/include, even though /usr/include/sys/types.h exists).
        !           775:      *
        !           776:      * We only perform this look-up for non-absolute file names.
        !           777:      *
        !           778:      * Whenever we score a hit, we assume there will be more matches from
        !           779:      * that directory, and append all but the last component of the
        !           780:      * resulting name onto the search path. */
1.1       deraadt   781:     if (!hasSlash) {
1.26      espie     782:        if (DEBUG(DIR))
1.1       deraadt   783:            printf("failed.\n");
1.32    ! espie     784: #ifdef DEBUG_DIRECTORY_CACHE
1.31      espie     785:        misses++;
1.32    ! espie     786: #endif
1.26      espie     787:        return NULL;
1.1       deraadt   788:     }
1.6       millert   789:
1.1       deraadt   790:     if (*name != '/') {
1.32    ! espie     791:        bool checkedDot = false;
1.6       millert   792:
1.26      espie     793:        if (DEBUG(DIR))
1.1       deraadt   794:            printf("failed. Trying subdirectories...");
1.26      espie     795:        for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
1.22      espie     796:            p = (Path *)Lst_Datum(ln);
1.26      espie     797:            if (p != dot)
1.32    ! espie     798:                file = Str_concati(p->name, strchr(p->name, '\0'), name, end, '/');
1.26      espie     799:            else {
                    800:                /* Checking in dot -- DON'T put a leading ./ on the thing.  */
1.32    ! espie     801:                file = Str_dupi(name, end);
        !           802:                checkedDot = true;
1.1       deraadt   803:            }
1.26      espie     804:            if (DEBUG(DIR))
1.1       deraadt   805:                printf("checking %s...", file);
1.6       millert   806:
1.26      espie     807:            if (stat(file, &stb) == 0) {
1.31      espie     808:                TIMESTAMP mtime;
1.27      espie     809:
1.32    ! espie     810:                ts_set_from_stat(stb, mtime);
1.26      espie     811:                if (DEBUG(DIR))
1.1       deraadt   812:                    printf("got it.\n");
1.6       millert   813:
1.32    ! espie     814:                /* We've found another directory to search. We know there
        !           815:                 * is a slash in 'file'. We call Dir_AddDiri to add the
        !           816:                 * new directory onto the existing search path. Once
        !           817:                 * that's done, we return the file name, knowing that
        !           818:                 * should a file in this directory ever be referenced again
        !           819:                 * in such a manner, we will find it without having to do
        !           820:                 * numerous access calls.  */
1.31      espie     821:                temp = strrchr(file, '/');
1.32    ! espie     822:                Dir_AddDiri(path, file, temp);
1.6       millert   823:
1.26      espie     824:                /* Save the modification time so if it's needed, we don't have
                    825:                 * to fetch it again.  */
                    826:                if (DEBUG(DIR))
1.29      espie     827:                    printf("Caching %s for %s\n", Targ_FmtTime(mtime),
1.1       deraadt   828:                            file);
1.27      espie     829:                record_stamp(file, mtime);
1.32    ! espie     830: #ifdef DEBUG_DIRECTORY_CACHE
1.31      espie     831:                nearmisses++;
1.32    ! espie     832: #endif
1.26      espie     833:                return file;
                    834:            } else
                    835:                free(file);
1.1       deraadt   836:        }
1.6       millert   837:
1.26      espie     838:        if (DEBUG(DIR))
1.1       deraadt   839:            printf("failed. ");
1.31      espie     840:
1.1       deraadt   841:        if (checkedDot) {
1.26      espie     842:            /* Already checked by the given name, since . was in the path,
                    843:             * so no point in proceeding...  */
                    844:            if (DEBUG(DIR))
1.1       deraadt   845:                printf("Checked . already, returning NULL\n");
1.26      espie     846:            return NULL;
1.1       deraadt   847:        }
                    848:     }
1.6       millert   849:
1.32    ! espie     850:     /* Didn't find it that way, either. Last resort: look for the file
        !           851:      * in the global mtime cache, then on the disk.
        !           852:      * If this doesn't succeed, we finally return a NULL pointer.
1.1       deraadt   853:      *
1.32    ! espie     854:      * We cannot add this directory onto the search path because
1.1       deraadt   855:      * of this amusing case:
                    856:      * $(INSTALLDIR)/$(FILE): $(FILE)
                    857:      *
                    858:      * $(FILE) exists in $(INSTALLDIR) but not in the current one.
                    859:      * When searching for $(FILE), we will find it in $(INSTALLDIR)
1.26      espie     860:      * b/c we added it here. This is not good...  */
1.32    ! espie     861:     q = Str_dupi(name, end);
1.26      espie     862:     if (DEBUG(DIR))
1.31      espie     863:        printf("Looking for \"%s\"...", q);
1.6       millert   864:
1.32    ! espie     865: #ifdef DEBUG_DIRECTORY_CACHE
1.31      espie     866:     bigmisses++;
1.32    ! espie     867: #endif
1.31      espie     868:     entry = find_stampi(name, end);
1.27      espie     869:     if (entry != NULL) {
1.26      espie     870:        if (DEBUG(DIR))
1.1       deraadt   871:            printf("got it (in mtime cache)\n");
1.31      espie     872:        return q;
                    873:     } else if (stat(q, &stb) == 0) {
                    874:        TIMESTAMP mtime;
1.27      espie     875:
1.32    ! espie     876:        ts_set_from_stat(stb, mtime);
1.26      espie     877:        if (DEBUG(DIR))
1.29      espie     878:            printf("Caching %s for %s\n", Targ_FmtTime(mtime),
1.31      espie     879:                    q);
                    880:        record_stamp(q, mtime);
                    881:        return q;
1.1       deraadt   882:     } else {
1.26      espie     883:        if (DEBUG(DIR))
1.1       deraadt   884:            printf("failed. Returning NULL\n");
1.31      espie     885:        free(q);
1.26      espie     886:        return NULL;
1.1       deraadt   887:     }
                    888: }
                    889:
1.25      espie     890: /* Read a directory, either from the disk, or from the cache.  */
                    891: static Path *
1.32    ! espie     892: DirReaddiri(name, end)
1.31      espie     893:     const char         *name;
                    894:     const char         *end;
1.25      espie     895: {
1.31      espie     896:     Path               *p;     /* pointer to new Path structure */
                    897:     DIR                *d;     /* for reading directory */
                    898:     struct dirent      *dp;    /* entry in directory */
                    899:     unsigned int       slot;
1.25      espie     900:
1.30      espie     901:     slot = ohash_qlookupi(&openDirectories, name, &end);
                    902:     p = ohash_find(&openDirectories, slot);
1.25      espie     903:
                    904:     if (p != NULL)
                    905:        return p;
                    906:
1.30      espie     907:     p = ohash_create_entry(&dir_info, name, &end);
1.32    ! espie     908: #ifdef DEBUG_DIRECTORY_CACHE
1.25      espie     909:     p->hits = 0;
1.32    ! espie     910: #endif
1.25      espie     911:     p->refCount = 0;
1.30      espie     912:     ohash_init(&p->files, 4, &file_info);
1.25      espie     913:
                    914:     if (DEBUG(DIR)) {
                    915:        printf("Caching %s...", p->name);
                    916:        fflush(stdout);
                    917:     }
                    918:
1.31      espie     919:     if ((d = opendir(p->name)) == NULL)
                    920:        return NULL;
1.25      espie     921:     /* Skip the first two entries -- these will *always* be . and ..  */
                    922:     (void)readdir(d);
                    923:     (void)readdir(d);
                    924:
                    925:     while ((dp = readdir(d)) != NULL) {
                    926: #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
                    927:        /* The sun directory library doesn't check for a 0 inode
                    928:         * (0-inode slots just take up space), so we have to do
                    929:         * it ourselves.  */
                    930:        if (dp->d_fileno == 0)
                    931:            continue;
                    932: #endif /* sun && d_ino */
1.26      espie     933:        add_file(p, dp->d_name);
1.25      espie     934:     }
                    935:     (void)closedir(d);
                    936:     if (DEBUG(DIR))
                    937:        printf("done\n");
                    938:
1.30      espie     939:     ohash_insert(&openDirectories, slot, p);
1.25      espie     940:     return p;
                    941: }
                    942:
1.1       deraadt   943: /*-
                    944:  *-----------------------------------------------------------------------
1.32    ! espie     945:  * Dir_AddDiri --
1.1       deraadt   946:  *     Add the given name to the end of the given path. The order of
                    947:  *     the arguments is backwards so ParseDoDependency can do a
                    948:  *     Lst_ForEach of its list of paths...
                    949:  *
                    950:  * Side Effects:
1.6       millert   951:  *     A structure is added to the list and the directory is
1.1       deraadt   952:  *     read and hashed.
                    953:  *-----------------------------------------------------------------------
                    954:  */
1.31      espie     955:
1.1       deraadt   956: void
1.32    ! espie     957: Dir_AddDiri(path, name, end)
1.31      espie     958:     Lst        path;   /* the path to which the directory should be added */
1.25      espie     959:     const char *name;  /* the name of the directory to add */
                    960:     const char *end;
1.1       deraadt   961: {
1.25      espie     962:     Path       *p;     /* pointer to new Path structure */
1.6       millert   963:
1.32    ! espie     964:     p = DirReaddiri(name, end);
1.25      espie     965:     if (p == NULL)
1.31      espie     966:        return;
1.25      espie     967:     if (p->refCount == 0)
1.31      espie     968:        Lst_AtEnd(path, p);
1.32    ! espie     969:     else if (!Lst_AddNew(path, p))
1.31      espie     970:        return;
1.25      espie     971:     p->refCount++;
1.1       deraadt   972: }
                    973:
                    974: /*-
                    975:  *-----------------------------------------------------------------------
                    976:  * Dir_CopyDir --
                    977:  *     Callback function for duplicating a search path via Lst_Duplicate.
                    978:  *     Ups the reference count for the directory.
                    979:  *
                    980:  * Results:
                    981:  *     Returns the Path it was given.
                    982:  *
                    983:  * Side Effects:
                    984:  *     The refCount of the path is incremented.
                    985:  *-----------------------------------------------------------------------
                    986:  */
1.19      espie     987: void *
1.1       deraadt   988: Dir_CopyDir(p)
1.19      espie     989:     void *p;
1.1       deraadt   990: {
1.31      espie     991:     ((Path *)p)->refCount++;
1.17      espie     992:     return p;
1.1       deraadt   993: }
                    994:
                    995: /*-
                    996:  *-----------------------------------------------------------------------
                    997:  * Dir_MakeFlags --
                    998:  *     Make a string by taking all the directories in the given search
                    999:  *     path and preceding them by the given flag. Used by the suffix
                   1000:  *     module to create variables for compilers based on suffix search
                   1001:  *     paths.
                   1002:  *
                   1003:  * Results:
                   1004:  *     The string mentioned above. Note that there is no space between
                   1005:  *     the given flag and each directory. The empty string is returned if
                   1006:  *     Things don't go well.
                   1007:  *-----------------------------------------------------------------------
                   1008:  */
                   1009: char *
1.22      espie    1010: Dir_MakeFlags(flag, path)
1.31      espie    1011:     const char   *flag;  /* flag which should precede each directory */
                   1012:     Lst          path;   /* list of directories */
1.1       deraadt  1013: {
                   1014:     LstNode      ln;     /* the node of the current directory */
1.23      espie    1015:     BUFFER       buf;
1.6       millert  1016:
1.23      espie    1017:     Buf_Init(&buf, 0);
1.6       millert  1018:
1.23      espie    1019:     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
                   1020:            Buf_AddString(&buf, flag);
                   1021:            Buf_AddString(&buf, ((Path *)Lst_Datum(ln))->name);
                   1022:            Buf_AddSpace(&buf);
1.1       deraadt  1023:     }
1.6       millert  1024:
1.23      espie    1025:     return Buf_Retrieve(&buf);
1.1       deraadt  1026: }
                   1027:
                   1028: /*-
                   1029:  *-----------------------------------------------------------------------
                   1030:  * Dir_Destroy --
                   1031:  *     Nuke a directory descriptor, if possible. Callback procedure
                   1032:  *     for the suffixes module when destroying a search path.
                   1033:  *
                   1034:  * Side Effects:
                   1035:  *     If no other path references this directory (refCount == 0),
                   1036:  *     the Path and all its data are freed.
                   1037:  *-----------------------------------------------------------------------
                   1038:  */
                   1039: void
1.31      espie    1040: Dir_Destroy(pp)
                   1041:     void       *pp;            /* The directory descriptor to nuke */
1.1       deraadt  1042: {
1.31      espie    1043:     Path       *p = (Path *)pp;
1.1       deraadt  1044:
1.25      espie    1045:     if (--p->refCount == 0) {
1.31      espie    1046:        ohash_remove(&openDirectories, ohash_qlookup(&openDirectories, p->name));
1.26      espie    1047:        free_hash(&p->files);
1.19      espie    1048:        free(p);
1.1       deraadt  1049:     }
                   1050: }
                   1051:
                   1052: /*-
                   1053:  *-----------------------------------------------------------------------
                   1054:  * Dir_Concat --
                   1055:  *     Concatenate two paths, adding the second to the end of the first.
                   1056:  *     Makes sure to avoid duplicates.
                   1057:  *
                   1058:  * Side Effects:
                   1059:  *     Reference counts for added dirs are upped.
                   1060:  *-----------------------------------------------------------------------
                   1061:  */
                   1062: void
                   1063: Dir_Concat(path1, path2)
1.31      espie    1064:     Lst        path1;          /* Dest */
                   1065:     Lst        path2;          /* Source */
1.1       deraadt  1066: {
1.31      espie    1067:     LstNode    ln;
                   1068:     Path       *p;
1.1       deraadt  1069:
1.22      espie    1070:     for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) {
1.1       deraadt  1071:        p = (Path *)Lst_Datum(ln);
1.32    ! espie    1072:        if (Lst_AddNew(path1, p))
1.31      espie    1073:            p->refCount++;
1.1       deraadt  1074:     }
                   1075: }
                   1076:
1.32    ! espie    1077: #ifdef DEBUG_DIRECTORY_CACHE
1.1       deraadt  1078: void
                   1079: Dir_PrintDirectories()
                   1080: {
1.31      espie    1081:     Path               *p;
                   1082:     unsigned int       i;
1.6       millert  1083:
1.31      espie    1084:     printf("#*** Directory Cache:\n");
                   1085:     printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
1.1       deraadt  1086:              hits, misses, nearmisses, bigmisses,
                   1087:              (hits+bigmisses+nearmisses ?
                   1088:               hits * 100 / (hits + bigmisses + nearmisses) : 0));
1.31      espie    1089:     printf("# %-20s referenced\thits\n", "directory");
1.30      espie    1090:     for (p = ohash_first(&openDirectories, &i); p != NULL;
1.31      espie    1091:        p = ohash_next(&openDirectories, &i))
1.25      espie    1092:            printf("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
1.1       deraadt  1093: }
1.32    ! espie    1094: #endif
1.1       deraadt  1095:
1.31      espie    1096: static void
1.19      espie    1097: DirPrintDir(p)
1.31      espie    1098:     void       *p;
1.6       millert  1099: {
1.18      espie    1100:     printf("%s ", ((Path *)p)->name);
1.1       deraadt  1101: }
                   1102:
                   1103: void
1.18      espie    1104: Dir_PrintPath(path)
1.31      espie    1105:     Lst path;
1.1       deraadt  1106: {
1.18      espie    1107:     Lst_Every(path, DirPrintDir);
1.29      espie    1108: }
                   1109:
1.32    ! espie    1110: TIMESTAMP
        !          1111: Dir_MTime(gn)
        !          1112:     GNode        *gn;        /* the file whose modification time is
        !          1113:                               * desired */
        !          1114: {
        !          1115:     char         *fullName;  /* the full pathname of name */
        !          1116:     struct stat   stb;       /* buffer for finding the mod time */
        !          1117:     struct file_stamp
        !          1118:                  *entry;
        !          1119:     unsigned int  slot;
        !          1120:     TIMESTAMP    mtime;
        !          1121:
        !          1122:     if (gn->type & OP_ARCHV)
        !          1123:        return Arch_MTime(gn);
        !          1124:
        !          1125:     if (gn->path == NULL) {
        !          1126:        fullName = Dir_FindFile(gn->name, dirSearchPath);
        !          1127:        if (fullName == NULL)
        !          1128:            fullName = estrdup(gn->name);
        !          1129:     } else
        !          1130:        fullName = gn->path;
        !          1131:
        !          1132:     slot = ohash_qlookup(&mtimes, fullName);
        !          1133:     entry = ohash_find(&mtimes, slot);
        !          1134:     if (entry != NULL) {
        !          1135:        /* Only do this once -- the second time folks are checking to
        !          1136:         * see if the file was actually updated, so we need to actually go
        !          1137:         * to the file system.  */
        !          1138:        if (DEBUG(DIR))
        !          1139:            printf("Using cached time %s for %s\n",
        !          1140:                    Targ_FmtTime(entry->mtime), fullName);
        !          1141:        mtime = entry->mtime;
        !          1142:        free(entry);
        !          1143:        ohash_remove(&mtimes, slot);
        !          1144:     } else if (stat(fullName, &stb) == 0)
        !          1145:        ts_set_from_stat(stb, mtime);
        !          1146:     else {
        !          1147:        if (gn->type & OP_MEMBER) {
        !          1148:            if (fullName != gn->path)
        !          1149:                free(fullName);
        !          1150:            return Arch_MemMTime(gn);
        !          1151:        } else
        !          1152:            ts_set_out_of_date(mtime);
        !          1153:     }
        !          1154:     if (fullName && gn->path == NULL)
        !          1155:        gn->path = fullName;
1.29      espie    1156:
1.32    ! espie    1157:     gn->mtime = mtime;
        !          1158:     return gn->mtime;
1.1       deraadt  1159: }
1.32    ! espie    1160: