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

1.31      espie       1: /*     $OpenPackages$ */
1.54      espie       2: /*     $OpenBSD: dir.c,v 1.53 2007/09/17 09:28:36 espie Exp $ */
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.
1.40      millert    48:  * 3. Neither the name of the University nor the names of its contributors
1.1       deraadt    49:  *    may be used to endorse or promote products derived from this software
                     50:  *    without specific prior written permission.
                     51:  *
                     52:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     53:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     54:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     55:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     56:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     57:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     58:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     59:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     60:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     61:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     62:  * SUCH DAMAGE.
                     63:  */
                     64:
1.34      espie      65: #include <sys/param.h>
1.32      espie      66: #include <sys/stat.h>
                     67: #include <dirent.h>
1.34      espie      68: #include <limits.h>
1.25      espie      69: #include <stddef.h>
1.1       deraadt    70: #include <stdio.h>
1.44      espie      71: #include <stdint.h>
1.34      espie      72: #include <stdlib.h>
1.32      espie      73: #include <string.h>
                     74: #include "config.h"
                     75: #include "defines.h"
1.25      espie      76: #include "ohash.h"
1.1       deraadt    77: #include "dir.h"
1.32      espie      78: #include "lst.h"
                     79: #include "memory.h"
                     80: #include "buf.h"
                     81: #include "gnode.h"
                     82: #include "arch.h"
1.55    ! espie      83: #include "targ.h"
1.32      espie      84: #include "error.h"
                     85: #include "str.h"
                     86: #include "timestamp.h"
                     87:
                     88:
1.55    ! espie      89: /*     A search path consists of a Lst of PathEntry structures. A Path
1.50      espie      90:  *     structure has in it the name of the directory and a hash table of all
                     91:  *     the files in the directory. This is used to cut down on the number of
                     92:  *     system calls necessary to find implicit dependents and their like.
                     93:  *     Since these searches are made before any actions are taken, we need not
1.1       deraadt    94:  *     worry about the directory changing due to creation commands. If this
                     95:  *     hampers the style of some makefiles, they must be changed.
                     96:  *
                     97:  *     A list of all previously-read directories is kept in the
1.51      espie      98:  *     knownDirectories cache.
1.1       deraadt    99:  *
                    100:  *     The need for the caching of whole directories is brought about by
                    101:  *     the multi-level transformation code in suff.c, which tends to search
                    102:  *     for far more files than regular make does. In the initial
                    103:  *     implementation, the amount of time spent performing "stat" calls was
                    104:  *     truly astronomical. The problem with hashing at the start is,
                    105:  *     of course, that pmake doesn't then detect changes to these directories
                    106:  *     during the course of the make. Three possibilities suggest themselves:
                    107:  *
                    108:  *         1) just use stat to test for a file's existence. As mentioned
                    109:  *            above, this is very inefficient due to the number of checks
                    110:  *            engendered by the multi-level transformation code.
                    111:  *         2) use readdir() and company to search the directories, keeping
                    112:  *            them open between checks. I have tried this and while it
                    113:  *            didn't slow down the process too much, it could severely
                    114:  *            affect the amount of parallelism available as each directory
                    115:  *            open would take another file descriptor out of play for
                    116:  *            handling I/O for another job. Given that it is only recently
                    117:  *            that UNIX OS's have taken to allowing more than 20 or 32
                    118:  *            file descriptors for a process, this doesn't seem acceptable
                    119:  *            to me.
1.50      espie     120:  *         3) record the mtime of the directory in the PathEntry structure and
1.1       deraadt   121:  *            verify the directory hasn't changed since the contents were
                    122:  *            hashed. This will catch the creation or deletion of files,
                    123:  *            but not the updating of files. However, since it is the
                    124:  *            creation and deletion that is the problem, this could be
                    125:  *            a good thing to do. Unfortunately, if the directory (say ".")
                    126:  *            were fairly large and changed fairly frequently, the constant
                    127:  *            rehashing could seriously degrade performance. It might be
                    128:  *            good in such cases to keep track of the number of rehashes
                    129:  *            and if the number goes over a (small) limit, resort to using
                    130:  *            stat in its place.
                    131:  *
                    132:  *     An additional thing to consider is that pmake is used primarily
                    133:  *     to create C programs and until recently pcc-based compilers refused
                    134:  *     to allow you to specify where the resulting object file should be
                    135:  *     placed. This forced all objects to be created in the current
                    136:  *     directory. This isn't meant as a full excuse, just an explanation of
                    137:  *     some of the reasons for the caching used here.
                    138:  *
                    139:  *     One more note: the location of a target's file is only performed
                    140:  *     on the downward traversal of the graph and then only for terminal
                    141:  *     nodes in the graph. This could be construed as wrong in some cases,
                    142:  *     but prevents inadvertent modification of files when the "installed"
                    143:  *     directory for a file is provided in the search path.
                    144:  *
                    145:  *     Another data structure maintained by this module is an mtime
                    146:  *     cache used when the searching of cached directories fails to find
                    147:  *     a file. In the past, Dir_FindFile would simply perform an access()
                    148:  *     call in such a case to determine if the file could be found using
                    149:  *     just the name given. When this hit, however, all that was gained
                    150:  *     was the knowledge that the file existed. Given that an access() is
                    151:  *     essentially a stat() without the copyout() call, and that the same
                    152:  *     filesystem overhead would have to be incurred in Dir_MTime, it made
                    153:  *     sense to replace the access() with a stat() and record the mtime
1.31      espie     154:  *     in a cache for when Dir_MTime was actually called.  */
1.1       deraadt   155:
                    156:
1.55    ! espie     157: /* several data structures exist to handle caching of directory stuff.
        !           158:  *
        !           159:  * There is a global hash of directory names (knownDirectories), and each
        !           160:  * read directory is kept there as one PathEntry instance. Such a structure
        !           161:  * only contains the file names.
        !           162:  *
        !           163:  * There is a global hash of timestamps (modification times), so care must
        !           164:  * be taken of giving the right file names to that structure.
        !           165:  *
        !           166:  * XXX A set of similar structure should exist at the Target level to properly
        !           167:  * take care of VPATH issues.
        !           168:  */
        !           169:
        !           170:
        !           171: /* each directory is cached into a PathEntry structure. */
        !           172: struct PathEntry {
        !           173:        int refCount;           /* ref-counted, can participate to
        !           174:                                 * several paths */
        !           175:        struct ohash files;     /* hash of name of files in the directory */
        !           176:        char name[1];           /* directory name */
        !           177: };
1.27      espie     178:
1.55    ! espie     179: /* PathEntry kept on knownDirectories */
        !           180: static struct ohash_info dir_info = {
        !           181:        offsetof(struct PathEntry, name), NULL, hash_alloc, hash_free,
        !           182:        element_alloc
1.27      espie     183: };
                    184:
1.51      espie     185: static struct ohash   knownDirectories;        /* cache all open directories */
1.31      espie     186:
1.55    ! espie     187:
        !           188: /* file names kept in a path entry */
        !           189: static struct ohash_info file_info = {
        !           190:        0, NULL, hash_alloc, hash_free, element_alloc
        !           191: };
        !           192:
        !           193:
1.32      espie     194: /* Global structure used to cache mtimes.  XXX We don't cache an mtime
                    195:  * before a caller actually looks up for the given time, because of the
                    196:  * possibility a caller might update the file and invalidate the cache
                    197:  * entry, and we don't look up in this cache except as a last resort.
                    198:  */
1.55    ! espie     199: struct file_stamp {
        !           200:        TIMESTAMP mtime;                /* time stamp... */
        !           201:        char name[1];                   /* ...for that file.  */
        !           202: };
        !           203:
1.53      espie     204: static struct ohash mtimes;
1.1       deraadt   205:
1.31      espie     206:
1.53      espie     207: static struct ohash_info stamp_info = {
                    208:        offsetof(struct file_stamp, name), NULL, hash_alloc, hash_free,
1.55    ! espie     209:        element_alloc
        !           210: };
        !           211:
        !           212:
        !           213:
        !           214: static LIST   theDefaultPath;          /* main search path */
        !           215: Lst          defaultPath= &theDefaultPath;
        !           216: struct PathEntry *dot;                         /* contents of current directory */
1.27      espie     217:
1.26      espie     218:
1.1       deraadt   219:
1.32      espie     220: /* add_file(path, name): add a file name to a path hash structure. */
1.50      espie     221: static void add_file(struct PathEntry *, const char *);
1.32      espie     222: /* n = find_file_hashi(p, name, end, hv): retrieve name in a path hash
                    223:  *     structure. */
1.53      espie     224: static char *find_file_hashi(struct PathEntry *, const char *, const char *,
1.50      espie     225:     uint32_t);
1.32      espie     226:
                    227: /* stamp = find_stampi(name, end): look for (name, end) in the global
                    228:  *     cache. */
1.31      espie     229: static struct file_stamp *find_stampi(const char *, const char *);
1.32      espie     230: /* record_stamp(name, timestamp): record timestamp for name in the global
                    231:  *     cache. */
                    232: static void record_stamp(const char *, TIMESTAMP);
                    233:
1.55    ! espie     234: static bool read_directory(struct PathEntry *);
1.32      espie     235: /* p = DirReaddiri(name, end): read an actual directory, caching results
                    236:  *     as we go.  */
1.55    ! espie     237: static struct PathEntry *create_PathEntry(const char *, const char *);
1.32      espie     238: /* Debugging: show a dir name in a path. */
1.31      espie     239: static void DirPrintDir(void *);
1.1       deraadt   240:
1.55    ! espie     241: /***
        !           242:  *** timestamp handling
        !           243:  ***/
        !           244:
1.26      espie     245: static void
1.41      espie     246: record_stamp(const char *file, TIMESTAMP t)
1.27      espie     247: {
1.46      espie     248:        unsigned int slot;
                    249:        const char *end = NULL;
                    250:        struct file_stamp *n;
                    251:
                    252:        slot = ohash_qlookupi(&mtimes, file, &end);
                    253:        n = ohash_find(&mtimes, slot);
                    254:        if (n)
                    255:                n->mtime = t;
                    256:        else {
                    257:                n = ohash_create_entry(&stamp_info, file, &end);
                    258:                n->mtime = t;
                    259:                ohash_insert(&mtimes, slot, n);
                    260:        }
1.27      espie     261: }
1.31      espie     262:
1.27      espie     263: static struct file_stamp *
1.41      espie     264: find_stampi(const char *file, const char *efile)
1.27      espie     265: {
1.46      espie     266:        return ohash_find(&mtimes, ohash_qlookupi(&mtimes, file, &efile));
1.27      espie     267: }
                    268:
1.55    ! espie     269: /***
        !           270:  *** PathEntry handling
        !           271:  ***/
        !           272:
1.27      espie     273: static void
1.50      espie     274: add_file(struct PathEntry *p, const char *file)
1.26      espie     275: {
1.46      espie     276:        unsigned int    slot;
                    277:        const char      *end = NULL;
                    278:        char            *n;
                    279:        struct ohash    *h = &p->files;
                    280:
                    281:        slot = ohash_qlookupi(h, file, &end);
                    282:        n = ohash_find(h, slot);
                    283:        if (n == NULL) {
                    284:                n = ohash_create_entry(&file_info, file, &end);
                    285:                ohash_insert(h, slot, n);
                    286:        }
1.26      espie     287: }
1.31      espie     288:
1.26      espie     289: static char *
1.53      espie     290: find_file_hashi(struct PathEntry *p, const char *file, const char *efile,
1.50      espie     291:     uint32_t hv)
1.26      espie     292: {
1.46      espie     293:        struct ohash    *h = &p->files;
1.26      espie     294:
1.46      espie     295:        return ohash_find(h, ohash_lookup_interval(h, file, efile, hv));
1.26      espie     296: }
                    297:
1.55    ! espie     298: static bool
        !           299: read_directory(struct PathEntry *p)
        !           300: {
        !           301:        DIR *d;
        !           302:        struct dirent *dp;
        !           303:
        !           304:        if (DEBUG(DIR)) {
        !           305:                printf("Caching %s...", p->name);
        !           306:                fflush(stdout);
        !           307:        }
        !           308:
        !           309:        if ((d = opendir(p->name)) == NULL)
        !           310:                return false;
        !           311:
        !           312:        ohash_init(&p->files, 4, &file_info);
        !           313:
        !           314:        while ((dp = readdir(d)) != NULL) {
        !           315:                if (dp->d_name[0] == '.' &&
        !           316:                    (dp->d_name[1] == '\0' ||
        !           317:                    (dp->d_name[1] == '.' && dp->d_name[2] == '\0')))
        !           318:                        continue;
        !           319:                add_file(p, dp->d_name);
        !           320:        }
        !           321:        (void)closedir(d);
        !           322:        if (DEBUG(DIR))
        !           323:                printf("done\n");
        !           324:        return true;
        !           325: }
        !           326:
        !           327: /* Read a directory, either from the disk, or from the cache.  */
        !           328: static struct PathEntry *
        !           329: create_PathEntry(const char *name, const char *ename)
        !           330: {
        !           331:        struct PathEntry *p;
        !           332:        unsigned int slot;
        !           333:
        !           334:        slot = ohash_qlookupi(&knownDirectories, name, &ename);
        !           335:        p = ohash_find(&knownDirectories, slot);
        !           336:
        !           337:        if (p == NULL) {
        !           338:                p = ohash_create_entry(&dir_info, name, &ename);
        !           339:                p->refCount = 0;
        !           340:                if (!read_directory(p)) {
        !           341:                        free(p);
        !           342:                        return NULL;
        !           343:                }
        !           344:                ohash_insert(&knownDirectories, slot, p);
        !           345:        }
        !           346:        p->refCount++;
        !           347:        return p;
        !           348: }
1.32      espie     349:
                    350: /* Side Effects: cache the current directory */
1.1       deraadt   351: void
1.41      espie     352: Dir_Init(void)
1.1       deraadt   353: {
1.46      espie     354:        char *dotname = ".";
1.32      espie     355:
1.51      espie     356:        Static_Lst_Init(defaultPath);
                    357:        ohash_init(&knownDirectories, 4, &dir_info);
1.46      espie     358:        ohash_init(&mtimes, 4, &stamp_info);
1.6       millert   359:
1.32      espie     360:
1.55    ! espie     361:        dot = create_PathEntry(dotname, dotname+1);
1.1       deraadt   362:
1.46      espie     363:        if (!dot)
                    364:                Fatal("Can't access current directory");
1.1       deraadt   365: }
                    366:
1.32      espie     367: #ifdef CLEANUP
1.1       deraadt   368: void
1.41      espie     369: Dir_End(void)
1.1       deraadt   370: {
1.50      espie     371:        struct PathEntry *p;
1.46      espie     372:        unsigned int i;
1.25      espie     373:
1.46      espie     374:        dot->refCount--;
                    375:        Dir_Destroy(dot);
1.51      espie     376:        Lst_Destroy(defaultPath, Dir_Destroy);
                    377:        for (p = ohash_first(&knownDirectories, &i); p != NULL;
                    378:            p = ohash_next(&knownDirectories, &i))
1.46      espie     379:                Dir_Destroy(p);
1.51      espie     380:        ohash_delete(&knownDirectories);
1.46      espie     381:        free_hash(&mtimes);
1.32      espie     382: }
1.9       espie     383: #endif
1.1       deraadt   384:
1.55    ! espie     385:
1.1       deraadt   386: /*-
                    387:  *-----------------------------------------------------------------------
1.47      espie     388:  * Dir_MatchFilesi --
1.50      espie     389:  *     Given a pattern and a PathEntry structure, see if any files
1.1       deraadt   390:  *     match the pattern and add their names to the 'expansions' list if
                    391:  *     any do. This is incomplete -- it doesn't take care of patterns like
                    392:  *     src / *src / *.c properly (just *.c on any of the directories), but it
                    393:  *     will do for now.
                    394:  *-----------------------------------------------------------------------
                    395:  */
1.47      espie     396: void
1.53      espie     397: Dir_MatchFilesi(const char *word, const char *eword, struct PathEntry *p,
1.50      espie     398:     Lst expansions)
1.31      espie     399: {
1.46      espie     400:        unsigned int search;    /* Index into the directory's table */
                    401:        const char *entry;      /* Current entry in the table */
                    402:
                    403:        for (entry = ohash_first(&p->files, &search); entry != NULL;
                    404:             entry = ohash_next(&p->files, &search)) {
                    405:                /* See if the file matches the given pattern. We follow the UNIX
                    406:                 * convention that dot files will only be found if the pattern
                    407:                 * begins with a dot (the hashing scheme doesn't hash . or ..,
                    408:                 * so they won't match `.*'.  */
                    409:                if (*word != '.' && *entry == '.')
                    410:                        continue;
                    411:                if (Str_Matchi(entry, strchr(entry, '\0'), word, eword))
1.53      espie     412:                        Lst_AtEnd(expansions,
1.55    ! espie     413:                            p == dot  ? estrdup(entry) :
1.46      espie     414:                            Str_concat(p->name, entry, '/'));
                    415:        }
1.1       deraadt   416: }
1.32      espie     417:
1.1       deraadt   418: /*-
                    419:  * Side Effects:
                    420:  *     If the file is found in a directory which is not on the path
                    421:  *     already (either 'name' is absolute or it is a relative path
                    422:  *     [ dir1/.../dirn/file ] which exists below one of the directories
                    423:  *     already on the search path), its directory is added to the end
                    424:  *     of the path on the assumption that there will be more files in
1.53      espie     425:  *     that directory later on.
1.1       deraadt   426:  */
                    427: char *
1.53      espie     428: Dir_FindFileComplexi(const char *name, const char *ename, Lst path,
1.45      espie     429:     bool checkCurdirFirst)
1.31      espie     430: {
1.50      espie     431:        struct PathEntry *p;    /* current path member */
1.46      espie     432:        char *p1;       /* pointer into p->name */
                    433:        const char *p2; /* pointer into name */
                    434:        LstNode ln;     /* a list element */
                    435:        char *file;     /* the current filename to check */
                    436:        char *temp;     /* index into file */
1.54      espie     437:        const char *basename;
1.46      espie     438:        bool hasSlash;
                    439:        struct stat stb;/* Buffer for stat, if necessary */
1.53      espie     440:        struct file_stamp *entry;
1.46      espie     441:                        /* Entry for mtimes table */
                    442:        uint32_t hv;    /* hash value for last component in file name */
                    443:        char *q;        /* Str_dupi(name, ename) */
                    444:
                    445:        /* Find the final component of the name and note whether name has a
                    446:         * slash in it */
1.54      espie     447:        basename = Str_rchri(name, ename, '/');
                    448:        if (basename) {
1.46      espie     449:                hasSlash = true;
1.54      espie     450:                basename++;
1.46      espie     451:        } else {
                    452:                hasSlash = false;
1.54      espie     453:                basename = name;
1.46      espie     454:        }
                    455:
1.54      espie     456:        hv = ohash_interval(basename, &ename);
1.6       millert   457:
1.26      espie     458:        if (DEBUG(DIR))
1.46      espie     459:                printf("Searching for %s...", name);
1.53      espie     460:        /* Unless checkCurDirFirst is false, we always look for
                    461:         * the file in the current directory before anywhere else
1.46      espie     462:         * and we always return exactly what the caller specified. */
1.53      espie     463:        if (checkCurdirFirst &&
1.54      espie     464:            (!hasSlash || (basename - name == 2 && *name == '.')) &&
                    465:            find_file_hashi(dot, basename, ename, hv) != NULL) {
1.46      espie     466:                if (DEBUG(DIR))
                    467:                        printf("in '.'\n");
                    468:                return Str_dupi(name, ename);
1.1       deraadt   469:        }
1.6       millert   470:
1.53      espie     471:        /* Then, we look through all the directories on path, seeking one
1.46      espie     472:         * containing the final component of name and whose final
1.53      espie     473:         * component(s) match name's initial component(s).
                    474:         * If found, we concatenate the directory name and the
1.46      espie     475:         * final component and return the resulting string.  */
                    476:        for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
1.50      espie     477:                p = (struct PathEntry *)Lst_Datum(ln);
1.46      espie     478:                if (DEBUG(DIR))
                    479:                        printf("%s...", p->name);
1.54      espie     480:                if (find_file_hashi(p, basename, ename, hv) != NULL) {
1.46      espie     481:                        if (DEBUG(DIR))
                    482:                                printf("here...");
                    483:                        if (hasSlash) {
1.53      espie     484:                                /* If the name had a slash, its initial
                    485:                                 * components and p's final components must
                    486:                                 * match. This is false if a mismatch is
                    487:                                 * encountered before all of the initial
                    488:                                 * components have been checked (p2 > name at
                    489:                                 * the end of the loop), or we matched only
                    490:                                 * part of one of the components of p along
1.46      espie     491:                                 * with all the rest of them (*p1 != '/').  */
                    492:                                p1 = p->name + strlen(p->name) - 1;
1.54      espie     493:                                p2 = basename - 2;
1.53      espie     494:                                while (p2 >= name && p1 >= p->name &&
1.46      espie     495:                                    *p1 == *p2) {
                    496:                                        p1--;
                    497:                                        p2--;
                    498:                                }
1.53      espie     499:                                if (p2 >= name ||
1.46      espie     500:                                    (p1 >= p->name && *p1 != '/')) {
                    501:                                        if (DEBUG(DIR))
                    502:                                                printf("component mismatch -- continuing...");
                    503:                                        continue;
                    504:                                }
                    505:                        }
1.55    ! espie     506:                        file = Str_concati(p->name, strchr(p->name, '\0'), basename,
        !           507:                            ename, '/');
1.46      espie     508:                        if (DEBUG(DIR))
                    509:                                printf("returning %s\n", file);
                    510:                        return file;
                    511:                } else if (hasSlash) {
1.53      espie     512:                        /* If the file has a leading path component and that
                    513:                         * component exactly matches the entire name of the
                    514:                         * current search directory, we assume the file
1.46      espie     515:                         * doesn't exist and return NULL.  */
1.53      espie     516:                        for (p1 = p->name, p2 = name; *p1 && *p1 == *p2;
1.46      espie     517:                            p1++, p2++)
                    518:                                continue;
1.54      espie     519:                        if (*p1 == '\0' && p2 == basename - 1) {
1.46      espie     520:                                if (DEBUG(DIR))
                    521:                                        printf("has to be here but isn't -- returning NULL\n");
                    522:                                return NULL;
                    523:                        }
                    524:                }
                    525:        }
1.27      espie     526:
1.46      espie     527:        /* We didn't find the file on any existing member of the path.
                    528:         * If the name doesn't contain a slash, end of story.
1.53      espie     529:         * If it does contain a slash, however, it could be in a subdirectory
                    530:         * of one of the members of the search path. (eg., for path=/usr/include
                    531:         * and name=sys/types.h, the above search fails to turn up types.h
1.46      espie     532:         * in /usr/include, even though /usr/include/sys/types.h exists).
                    533:         *
                    534:         * We only perform this look-up for non-absolute file names.
                    535:         *
                    536:         * Whenever we score a hit, we assume there will be more matches from
1.53      espie     537:         * that directory, and append all but the last component of the
1.46      espie     538:         * resulting name onto the search path. */
                    539:        if (!hasSlash) {
1.26      espie     540:                if (DEBUG(DIR))
1.46      espie     541:                        printf("failed.\n");
                    542:                return NULL;
                    543:        }
1.6       millert   544:
1.46      espie     545:        if (*name != '/') {
                    546:                bool checkedDot = false;
1.6       millert   547:
1.26      espie     548:                if (DEBUG(DIR))
1.46      espie     549:                        printf("failed. Trying subdirectories...");
                    550:                for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
1.50      espie     551:                        p = (struct PathEntry *)Lst_Datum(ln);
1.46      espie     552:                        if (p != dot)
1.53      espie     553:                                file = Str_concati(p->name,
1.46      espie     554:                                    strchr(p->name, '\0'), name, ename, '/');
                    555:                        else {
1.53      espie     556:                                /* Checking in dot -- DON'T put a leading
1.46      espie     557:                                * ./ on the thing.  */
                    558:                                file = Str_dupi(name, ename);
                    559:                                checkedDot = true;
                    560:                        }
                    561:                        if (DEBUG(DIR))
                    562:                                printf("checking %s...", file);
                    563:
                    564:                        if (stat(file, &stb) == 0) {
                    565:                                TIMESTAMP mtime;
                    566:
                    567:                                ts_set_from_stat(stb, mtime);
                    568:                                if (DEBUG(DIR))
                    569:                                        printf("got it.\n");
                    570:
1.53      espie     571:                                /* We've found another directory to search.
                    572:                                 * We know there is a slash in 'file'. We
                    573:                                 * call Dir_AddDiri to add the new directory
                    574:                                 * onto the existing search path. Once that's
                    575:                                 * done, we return the file name, knowing that
                    576:                                 * should a file in this directory ever be
                    577:                                 * referenced again in such a manner, we will
                    578:                                 * find it without having to do numerous
1.46      espie     579:                                 * access calls.  */
                    580:                                temp = strrchr(file, '/');
                    581:                                Dir_AddDiri(path, file, temp);
                    582:
1.53      espie     583:                                /* Save the modification time so if it's
1.46      espie     584:                                * needed, we don't have to fetch it again.  */
                    585:                                if (DEBUG(DIR))
1.53      espie     586:                                        printf("Caching %s for %s\n",
1.49      espie     587:                                            time_to_string(mtime), file);
1.46      espie     588:                                record_stamp(file, mtime);
                    589:                                return file;
                    590:                        } else
                    591:                                free(file);
                    592:                }
1.6       millert   593:
1.46      espie     594:                if (DEBUG(DIR))
                    595:                        printf("failed. ");
1.31      espie     596:
1.46      espie     597:                if (checkedDot) {
1.53      espie     598:                        /* Already checked by the given name, since . was in
1.46      espie     599:                         * the path, so no point in proceeding...  */
                    600:                        if (DEBUG(DIR))
                    601:                                printf("Checked . already, returning NULL\n");
                    602:                        return NULL;
                    603:                }
1.1       deraadt   604:        }
1.6       millert   605:
1.46      espie     606:        /* Didn't find it that way, either. Last resort: look for the file
                    607:         * in the global mtime cache, then on the disk.
                    608:         * If this doesn't succeed, we finally return a NULL pointer.
                    609:         *
                    610:         * We cannot add this directory onto the search path because
                    611:         * of this amusing case:
                    612:         * $(INSTALLDIR)/$(FILE): $(FILE)
                    613:         *
                    614:         * $(FILE) exists in $(INSTALLDIR) but not in the current one.
                    615:         * When searching for $(FILE), we will find it in $(INSTALLDIR)
                    616:         * b/c we added it here. This is not good...  */
                    617:        q = Str_dupi(name, ename);
                    618:        if (DEBUG(DIR))
                    619:                printf("Looking for \"%s\"...", q);
1.6       millert   620:
1.46      espie     621:        entry = find_stampi(name, ename);
                    622:        if (entry != NULL) {
                    623:                if (DEBUG(DIR))
                    624:                        printf("got it (in mtime cache)\n");
                    625:                return q;
                    626:        } else if (stat(q, &stb) == 0) {
                    627:                TIMESTAMP mtime;
1.27      espie     628:
1.46      espie     629:                ts_set_from_stat(stb, mtime);
                    630:                if (DEBUG(DIR))
1.49      espie     631:                        printf("Caching %s for %s\n", time_to_string(mtime), q);
1.46      espie     632:                record_stamp(q, mtime);
                    633:                return q;
                    634:        } else {
                    635:            if (DEBUG(DIR))
                    636:                    printf("failed. Returning NULL\n");
                    637:            free(q);
                    638:            return NULL;
                    639:        }
1.1       deraadt   640: }
                    641:
                    642: void
1.41      espie     643: Dir_AddDiri(Lst path, const char *name, const char *ename)
1.1       deraadt   644: {
1.50      espie     645:        struct PathEntry        *p;
1.6       millert   646:
1.55    ! espie     647:        p = create_PathEntry(name, ename);
1.46      espie     648:        if (p == NULL)
                    649:                return;
1.55    ! espie     650:        if (p->refCount == 1)
1.46      espie     651:                Lst_AtEnd(path, p);
                    652:        else if (!Lst_AddNew(path, p))
                    653:                return;
1.1       deraadt   654: }
                    655:
1.19      espie     656: void *
1.41      espie     657: Dir_CopyDir(void *p)
1.1       deraadt   658: {
1.50      espie     659:        ((struct PathEntry *)p)->refCount++;
1.46      espie     660:        return p;
1.1       deraadt   661: }
                    662:
                    663: /*-
                    664:  *-----------------------------------------------------------------------
                    665:  * Dir_MakeFlags --
                    666:  *     Make a string by taking all the directories in the given search
                    667:  *     path and preceding them by the given flag. Used by the suffix
                    668:  *     module to create variables for compilers based on suffix search
                    669:  *     paths.
                    670:  *
                    671:  * Results:
                    672:  *     The string mentioned above. Note that there is no space between
                    673:  *     the given flag and each directory. The empty string is returned if
                    674:  *     Things don't go well.
                    675:  *-----------------------------------------------------------------------
                    676:  */
                    677: char *
1.41      espie     678: Dir_MakeFlags(const char *flag, Lst path)
1.1       deraadt   679: {
1.46      espie     680:        LstNode   ln;
                    681:        BUFFER    buf;
1.6       millert   682:
1.46      espie     683:        Buf_Init(&buf, 0);
1.6       millert   684:
1.46      espie     685:        for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
                    686:                Buf_AddString(&buf, flag);
1.50      espie     687:                Buf_AddString(&buf, ((struct PathEntry *)Lst_Datum(ln))->name);
1.46      espie     688:                Buf_AddSpace(&buf);
                    689:        }
1.6       millert   690:
1.46      espie     691:        return Buf_Retrieve(&buf);
1.1       deraadt   692: }
                    693:
                    694: void
1.41      espie     695: Dir_Destroy(void *pp)
1.1       deraadt   696: {
1.50      espie     697:        struct PathEntry *p = (struct PathEntry *)pp;
1.1       deraadt   698:
1.46      espie     699:        if (--p->refCount == 0) {
1.53      espie     700:                ohash_remove(&knownDirectories,
1.51      espie     701:                    ohash_qlookup(&knownDirectories, p->name));
1.46      espie     702:                free_hash(&p->files);
                    703:                free(p);
                    704:        }
1.1       deraadt   705: }
                    706:
                    707: /*-
                    708:  *-----------------------------------------------------------------------
                    709:  * Dir_Concat --
                    710:  *     Concatenate two paths, adding the second to the end of the first.
                    711:  *     Makes sure to avoid duplicates.
                    712:  *
                    713:  * Side Effects:
                    714:  *     Reference counts for added dirs are upped.
                    715:  *-----------------------------------------------------------------------
                    716:  */
                    717: void
1.41      espie     718: Dir_Concat(Lst path1, Lst path2)
1.1       deraadt   719: {
1.46      espie     720:        LstNode ln;
1.55    ! espie     721:        struct PathEntry *p;
1.1       deraadt   722:
1.46      espie     723:        for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) {
1.50      espie     724:                p = (struct PathEntry *)Lst_Datum(ln);
1.46      espie     725:                if (Lst_AddNew(path1, p))
                    726:                        p->refCount++;
                    727:        }
1.1       deraadt   728: }
                    729:
1.31      espie     730: static void
1.41      espie     731: DirPrintDir(void *p)
1.6       millert   732: {
1.50      espie     733:        printf("%s ", ((struct PathEntry *)p)->name);
1.1       deraadt   734: }
                    735:
                    736: void
1.41      espie     737: Dir_PrintPath(Lst path)
1.1       deraadt   738: {
1.46      espie     739:        Lst_Every(path, DirPrintDir);
1.29      espie     740: }
                    741:
1.32      espie     742: TIMESTAMP
1.41      espie     743: Dir_MTime(GNode *gn)
1.32      espie     744: {
1.46      espie     745:        char *fullName;
                    746:        struct stat stb;
                    747:        struct file_stamp *entry;
                    748:        unsigned int slot;
                    749:        TIMESTAMP         mtime;
                    750:
                    751:        if (gn->type & OP_ARCHV)
                    752:                return Arch_MTime(gn);
                    753:
                    754:        if (gn->path == NULL) {
1.51      espie     755:                fullName = Dir_FindFile(gn->name, defaultPath);
1.46      espie     756:                if (fullName == NULL)
                    757:                        fullName = estrdup(gn->name);
1.32      espie     758:        } else
1.46      espie     759:                fullName = gn->path;
                    760:
                    761:        slot = ohash_qlookup(&mtimes, fullName);
                    762:        entry = ohash_find(&mtimes, slot);
                    763:        if (entry != NULL) {
                    764:                /* Only do this once -- the second time folks are checking to
1.53      espie     765:                 * see if the file was actually updated, so we need to
1.46      espie     766:                 * actually go to the file system.      */
                    767:                if (DEBUG(DIR))
                    768:                        printf("Using cached time %s for %s\n",
1.49      espie     769:                            time_to_string(entry->mtime), fullName);
1.46      espie     770:                mtime = entry->mtime;
                    771:                free(entry);
                    772:                ohash_remove(&mtimes, slot);
                    773:        } else if (stat(fullName, &stb) == 0)
                    774:                ts_set_from_stat(stb, mtime);
                    775:        else {
                    776:                if (gn->type & OP_MEMBER) {
                    777:                        if (fullName != gn->path)
                    778:                                free(fullName);
                    779:                        return Arch_MemMTime(gn);
                    780:                } else
                    781:                        ts_set_out_of_date(mtime);
                    782:        }
                    783:        if (fullName && gn->path == NULL)
                    784:                gn->path = fullName;
1.29      espie     785:
1.46      espie     786:        gn->mtime = mtime;
                    787:        return gn->mtime;
1.1       deraadt   788: }
1.32      espie     789: