[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.31

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