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

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

1.47    ! christos    1: /*     $NetBSD: dir.c,v 1.46 2005/07/25 22:55:58 christos Exp $        */
1.8       christos    2:
1.1       cgd         3: /*
                      4:  * Copyright (c) 1988, 1989, 1990 The Regents of the University of California.
1.37      agc         5:  * All rights reserved.
                      6:  *
                      7:  * This code is derived from software contributed to Berkeley by
                      8:  * Adam de Boor.
                      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:  * 3. Neither the name of the University nor the names of its contributors
                     19:  *    may be used to endorse or promote products derived from this software
                     20:  *    without specific prior written permission.
                     21:  *
                     22:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     23:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     24:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     25:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     26:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     27:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     28:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     29:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     30:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     31:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     32:  * SUCH DAMAGE.
                     33:  */
                     34:
                     35: /*
1.1       cgd        36:  * Copyright (c) 1988, 1989 by Adam de Boor
                     37:  * Copyright (c) 1989 by Berkeley Softworks
                     38:  * All rights reserved.
                     39:  *
                     40:  * This code is derived from software contributed to Berkeley by
                     41:  * Adam de Boor.
                     42:  *
                     43:  * Redistribution and use in source and binary forms, with or without
                     44:  * modification, are permitted provided that the following conditions
                     45:  * are met:
                     46:  * 1. Redistributions of source code must retain the above copyright
                     47:  *    notice, this list of conditions and the following disclaimer.
                     48:  * 2. Redistributions in binary form must reproduce the above copyright
                     49:  *    notice, this list of conditions and the following disclaimer in the
                     50:  *    documentation and/or other materials provided with the distribution.
                     51:  * 3. All advertising materials mentioning features or use of this software
                     52:  *    must display the following acknowledgement:
                     53:  *     This product includes software developed by the University of
                     54:  *     California, Berkeley and its contributors.
                     55:  * 4. Neither the name of the University nor the names of its contributors
                     56:  *    may be used to endorse or promote products derived from this software
                     57:  *    without specific prior written permission.
                     58:  *
                     59:  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
                     60:  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
                     61:  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
                     62:  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
                     63:  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
                     64:  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
                     65:  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
                     66:  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
                     67:  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
                     68:  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
                     69:  * SUCH DAMAGE.
                     70:  */
                     71:
1.41      ross       72: #ifndef MAKE_NATIVE
1.47    ! christos   73: static char rcsid[] = "$NetBSD: dir.c,v 1.46 2005/07/25 22:55:58 christos Exp $";
1.20      lukem      74: #else
1.19      christos   75: #include <sys/cdefs.h>
1.1       cgd        76: #ifndef lint
1.8       christos   77: #if 0
1.12      christos   78: static char sccsid[] = "@(#)dir.c      8.2 (Berkeley) 1/2/94";
1.8       christos   79: #else
1.47    ! christos   80: __RCSID("$NetBSD: dir.c,v 1.46 2005/07/25 22:55:58 christos Exp $");
1.8       christos   81: #endif
1.1       cgd        82: #endif /* not lint */
1.20      lukem      83: #endif
1.1       cgd        84:
                     85: /*-
                     86:  * dir.c --
                     87:  *     Directory searching using wildcards and/or normal names...
                     88:  *     Used both for source wildcarding in the Makefile and for finding
                     89:  *     implicit sources.
                     90:  *
                     91:  * The interface for this module is:
                     92:  *     Dir_Init            Initialize the module.
                     93:  *
1.35      sjg        94:  *     Dir_InitCur         Set the cur Path.
                     95:  *
                     96:  *     Dir_InitDot         Set the dot Path.
                     97:  *
1.6       jtc        98:  *     Dir_End             Cleanup the module.
                     99:  *
1.35      sjg       100:  *     Dir_SetPATH         Set ${.PATH} to reflect state of dirSearchPath.
                    101:  *
1.1       cgd       102:  *     Dir_HasWildcards    Returns TRUE if the name given it needs to
                    103:  *                         be wildcard-expanded.
                    104:  *
                    105:  *     Dir_Expand          Given a pattern and a path, return a Lst of names
                    106:  *                         which match the pattern on the search path.
                    107:  *
                    108:  *     Dir_FindFile        Searches for a file on a given search path.
                    109:  *                         If it exists, the entire path is returned.
                    110:  *                         Otherwise NULL is returned.
                    111:  *
1.40      chuck     112:  *     Dir_FindHereOrAbove Search for a path in the current directory and
                    113:  *                         then all the directories above it in turn until
                    114:  *                         the path is found or we reach the root ("/").
                    115:  *
1.1       cgd       116:  *     Dir_MTime           Return the modification time of a node. The file
                    117:  *                         is searched for along the default search path.
                    118:  *                         The path and mtime fields of the node are filled
                    119:  *                         in.
                    120:  *
                    121:  *     Dir_AddDir          Add a directory to a search path.
                    122:  *
                    123:  *     Dir_MakeFlags       Given a search path and a command flag, create
                    124:  *                         a string with each of the directories in the path
                    125:  *                         preceded by the command flag and all of them
                    126:  *                         separated by a space.
                    127:  *
                    128:  *     Dir_Destroy         Destroy an element of a search path. Frees up all
                    129:  *                         things that can be freed for the element as long
                    130:  *                         as the element is no longer referenced by any other
                    131:  *                         search path.
                    132:  *     Dir_ClearPath       Resets a search path to the empty list.
                    133:  *
                    134:  * For debugging:
                    135:  *     Dir_PrintDirectories    Print stats about the directory cache.
                    136:  */
                    137:
                    138: #include <sys/types.h>
1.34      wiz       139: #include <sys/stat.h>
                    140:
1.5       cgd       141: #include <dirent.h>
1.34      wiz       142: #include <errno.h>
                    143: #include <stdio.h>
                    144:
1.1       cgd       145: #include "make.h"
                    146: #include "hash.h"
1.5       cgd       147: #include "dir.h"
1.1       cgd       148:
                    149: /*
                    150:  *     A search path consists of a Lst of Path structures. A Path structure
                    151:  *     has in it the name of the directory and a hash table of all the files
                    152:  *     in the directory. This is used to cut down on the number of system
                    153:  *     calls necessary to find implicit dependents and their like. Since
                    154:  *     these searches are made before any actions are taken, we need not
                    155:  *     worry about the directory changing due to creation commands. If this
                    156:  *     hampers the style of some makefiles, they must be changed.
                    157:  *
                    158:  *     A list of all previously-read directories is kept in the
                    159:  *     openDirectories Lst. This list is checked first before a directory
                    160:  *     is opened.
                    161:  *
                    162:  *     The need for the caching of whole directories is brought about by
                    163:  *     the multi-level transformation code in suff.c, which tends to search
                    164:  *     for far more files than regular make does. In the initial
                    165:  *     implementation, the amount of time spent performing "stat" calls was
                    166:  *     truly astronomical. The problem with hashing at the start is,
                    167:  *     of course, that pmake doesn't then detect changes to these directories
                    168:  *     during the course of the make. Three possibilities suggest themselves:
                    169:  *
                    170:  *         1) just use stat to test for a file's existence. As mentioned
                    171:  *            above, this is very inefficient due to the number of checks
                    172:  *            engendered by the multi-level transformation code.
                    173:  *         2) use readdir() and company to search the directories, keeping
                    174:  *            them open between checks. I have tried this and while it
                    175:  *            didn't slow down the process too much, it could severely
                    176:  *            affect the amount of parallelism available as each directory
                    177:  *            open would take another file descriptor out of play for
                    178:  *            handling I/O for another job. Given that it is only recently
                    179:  *            that UNIX OS's have taken to allowing more than 20 or 32
                    180:  *            file descriptors for a process, this doesn't seem acceptable
                    181:  *            to me.
                    182:  *         3) record the mtime of the directory in the Path structure and
                    183:  *            verify the directory hasn't changed since the contents were
                    184:  *            hashed. This will catch the creation or deletion of files,
                    185:  *            but not the updating of files. However, since it is the
                    186:  *            creation and deletion that is the problem, this could be
                    187:  *            a good thing to do. Unfortunately, if the directory (say ".")
                    188:  *            were fairly large and changed fairly frequently, the constant
                    189:  *            rehashing could seriously degrade performance. It might be
                    190:  *            good in such cases to keep track of the number of rehashes
                    191:  *            and if the number goes over a (small) limit, resort to using
                    192:  *            stat in its place.
                    193:  *
                    194:  *     An additional thing to consider is that pmake is used primarily
                    195:  *     to create C programs and until recently pcc-based compilers refused
                    196:  *     to allow you to specify where the resulting object file should be
                    197:  *     placed. This forced all objects to be created in the current
                    198:  *     directory. This isn't meant as a full excuse, just an explanation of
                    199:  *     some of the reasons for the caching used here.
                    200:  *
                    201:  *     One more note: the location of a target's file is only performed
                    202:  *     on the downward traversal of the graph and then only for terminal
                    203:  *     nodes in the graph. This could be construed as wrong in some cases,
                    204:  *     but prevents inadvertent modification of files when the "installed"
                    205:  *     directory for a file is provided in the search path.
                    206:  *
                    207:  *     Another data structure maintained by this module is an mtime
                    208:  *     cache used when the searching of cached directories fails to find
                    209:  *     a file. In the past, Dir_FindFile would simply perform an access()
                    210:  *     call in such a case to determine if the file could be found using
                    211:  *     just the name given. When this hit, however, all that was gained
                    212:  *     was the knowledge that the file existed. Given that an access() is
                    213:  *     essentially a stat() without the copyout() call, and that the same
                    214:  *     filesystem overhead would have to be incurred in Dir_MTime, it made
                    215:  *     sense to replace the access() with a stat() and record the mtime
                    216:  *     in a cache for when Dir_MTime was actually called.
                    217:  */
                    218:
                    219: Lst          dirSearchPath;    /* main search path */
                    220:
                    221: static Lst   openDirectories;  /* the list of all open directories */
                    222:
                    223: /*
                    224:  * Variables for gathering statistics on the efficiency of the hashing
                    225:  * mechanism.
                    226:  */
                    227: static int    hits,          /* Found in directory cache */
                    228:              misses,         /* Sad, but not evil misses */
                    229:              nearmisses,     /* Found under search path */
                    230:              bigmisses;      /* Sought by itself */
                    231:
                    232: static Path              *dot;     /* contents of current directory */
1.17      gwr       233: static Path              *cur;     /* contents of current directory, if not dot */
1.21      thorpej   234: static Path      *dotLast; /* a fake path entry indicating we need to
                    235:                             * look for . last */
1.1       cgd       236: static Hash_Table mtimes;   /* Results of doing a last-resort stat in
                    237:                             * Dir_FindFile -- if we have to go to the
                    238:                             * system to find the file, we might as well
                    239:                             * have its mtime on record. XXX: If this is done
                    240:                             * way early, there's a chance other rules will
                    241:                             * have already updated the file, in which case
                    242:                             * we'll update it again. Generally, there won't
                    243:                             * be two rules to update a single file, so this
                    244:                             * should be ok, but... */
                    245:
                    246:
1.34      wiz       247: static int DirFindName(ClientData, ClientData);
1.36      christos  248: static int DirMatchFiles(const char *, Path *, Lst);
                    249: static void DirExpandCurly(const char *, const char *, Lst, Lst);
                    250: static void DirExpandInt(const char *, Lst, Lst);
1.34      wiz       251: static int DirPrintWord(ClientData, ClientData);
                    252: static int DirPrintDir(ClientData, ClientData);
1.36      christos  253: static char *DirLookup(Path *, const char *, const char *, Boolean);
                    254: static char *DirLookupSubdir(Path *, const char *);
                    255: static char *DirFindDot(Boolean, const char *, const char *);
                    256: static char *DirLookupAbs(Path *, const char *, const char *);
1.5       cgd       257:
1.1       cgd       258: /*-
                    259:  *-----------------------------------------------------------------------
                    260:  * Dir_Init --
                    261:  *     initialize things for this module
                    262:  *
                    263:  * Results:
                    264:  *     none
                    265:  *
                    266:  * Side Effects:
                    267:  *     some directories may be opened.
                    268:  *-----------------------------------------------------------------------
                    269:  */
                    270: void
1.45      christos  271: Dir_Init(const char *cdname)
1.1       cgd       272: {
1.45      christos  273:     dirSearchPath = Lst_Init(FALSE);
                    274:     openDirectories = Lst_Init(FALSE);
1.1       cgd       275:     Hash_InitTable(&mtimes, 0);
1.12      christos  276:
1.35      sjg       277:     Dir_InitCur(cdname);
                    278:
1.45      christos  279:     dotLast = emalloc(sizeof(Path));
1.35      sjg       280:     dotLast->refCount = 1;
                    281:     dotLast->hits = 0;
                    282:     dotLast->name = estrdup(".DOTLAST");
1.45      christos  283:     Hash_InitTable(&dotLast->files, -1);
1.35      sjg       284: }
                    285:
                    286: /*
                    287:  * Called by Dir_Init() and whenever .CURDIR is assigned to.
                    288:  */
                    289: void
1.45      christos  290: Dir_InitCur(const char *cdname)
1.35      sjg       291: {
                    292:     Path *p;
                    293:
1.17      gwr       294:     if (cdname != NULL) {
                    295:        /*
                    296:         * Our build directory is not the same as our source directory.
                    297:         * Keep this one around too.
                    298:         */
1.35      sjg       299:        if ((p = Dir_AddDir(NULL, cdname))) {
                    300:            p->refCount += 1;
                    301:            if (cur && cur != p) {
                    302:                /*
                    303:                 * We've been here before, cleanup.
                    304:                 */
                    305:                cur->refCount -= 1;
                    306:                Dir_Destroy((ClientData) cur);
                    307:            }
                    308:            cur = p;
                    309:        }
1.17      gwr       310:     }
1.28      tv        311: }
                    312:
                    313: /*-
                    314:  *-----------------------------------------------------------------------
                    315:  * Dir_InitDot --
                    316:  *     (re)initialize "dot" (current/object directory) path hash
                    317:  *
                    318:  * Results:
                    319:  *     none
                    320:  *
                    321:  * Side Effects:
                    322:  *     some directories may be opened.
                    323:  *-----------------------------------------------------------------------
                    324:  */
                    325: void
1.34      wiz       326: Dir_InitDot(void)
1.28      tv        327: {
                    328:     if (dot != NULL) {
                    329:        LstNode ln;
                    330:
                    331:        /* Remove old entry from openDirectories, but do not destroy. */
1.45      christos  332:        ln = Lst_Member(openDirectories, (ClientData)dot);
1.46      christos  333:        (void)Lst_Remove(openDirectories, ln);
1.28      tv        334:     }
                    335:
1.45      christos  336:     dot = Dir_AddDir(NULL, ".");
1.28      tv        337:
                    338:     if (dot == NULL) {
                    339:        Error("Cannot open `.' (%s)", strerror(errno));
                    340:        exit(1);
                    341:     }
                    342:
                    343:     /*
                    344:      * We always need to have dot around, so we increment its reference count
                    345:      * to make sure it's not destroyed.
                    346:      */
                    347:     dot->refCount += 1;
1.35      sjg       348:     Dir_SetPATH();                     /* initialize */
1.1       cgd       349: }
                    350:
                    351: /*-
                    352:  *-----------------------------------------------------------------------
1.6       jtc       353:  * Dir_End --
                    354:  *     cleanup things for this module
                    355:  *
                    356:  * Results:
                    357:  *     none
                    358:  *
                    359:  * Side Effects:
                    360:  *     none
                    361:  *-----------------------------------------------------------------------
                    362:  */
                    363: void
1.34      wiz       364: Dir_End(void)
1.6       jtc       365: {
1.24      mycroft   366: #ifdef CLEANUP
1.17      gwr       367:     if (cur) {
                    368:        cur->refCount -= 1;
                    369:        Dir_Destroy((ClientData) cur);
                    370:     }
1.6       jtc       371:     dot->refCount -= 1;
1.21      thorpej   372:     dotLast->refCount -= 1;
                    373:     Dir_Destroy((ClientData) dotLast);
1.6       jtc       374:     Dir_Destroy((ClientData) dot);
                    375:     Dir_ClearPath(dirSearchPath);
                    376:     Lst_Destroy(dirSearchPath, NOFREE);
                    377:     Dir_ClearPath(openDirectories);
                    378:     Lst_Destroy(openDirectories, NOFREE);
                    379:     Hash_DeleteTable(&mtimes);
1.24      mycroft   380: #endif
1.6       jtc       381: }
                    382:
1.35      sjg       383: /*
                    384:  * We want ${.PATH} to indicate the order in which we will actually
                    385:  * search, so we rebuild it after any .PATH: target.
                    386:  * This is the simplest way to deal with the effect of .DOTLAST.
                    387:  */
                    388: void
1.45      christos  389: Dir_SetPATH(void)
1.35      sjg       390: {
                    391:     LstNode       ln;          /* a list element */
                    392:     Path *p;
                    393:     Boolean      hasLastDot = FALSE;   /* true we should search dot last */
                    394:
                    395:     Var_Delete(".PATH", VAR_GLOBAL);
                    396:
1.45      christos  397:     if (Lst_Open(dirSearchPath) == SUCCESS) {
                    398:        if ((ln = Lst_First(dirSearchPath)) != NILLNODE) {
1.46      christos  399:            p = (Path *)Lst_Datum(ln);
1.35      sjg       400:            if (p == dotLast) {
                    401:                hasLastDot = TRUE;
                    402:                Var_Append(".PATH", dotLast->name, VAR_GLOBAL);
                    403:            }
                    404:        }
                    405:
                    406:        if (!hasLastDot) {
                    407:            if (dot)
                    408:                Var_Append(".PATH", dot->name, VAR_GLOBAL);
                    409:            if (cur)
                    410:                Var_Append(".PATH", cur->name, VAR_GLOBAL);
                    411:        }
                    412:
1.45      christos  413:        while ((ln = Lst_Next(dirSearchPath)) != NILLNODE) {
1.46      christos  414:            p = (Path *)Lst_Datum(ln);
1.35      sjg       415:            if (p == dotLast)
                    416:                continue;
                    417:            if (p == dot && hasLastDot)
                    418:                continue;
                    419:            Var_Append(".PATH", p->name, VAR_GLOBAL);
                    420:        }
                    421:
                    422:        if (hasLastDot) {
                    423:            if (dot)
                    424:                Var_Append(".PATH", dot->name, VAR_GLOBAL);
                    425:            if (cur)
                    426:                Var_Append(".PATH", cur->name, VAR_GLOBAL);
                    427:        }
                    428:        Lst_Close(dirSearchPath);
                    429:     }
                    430: }
                    431:
1.6       jtc       432: /*-
                    433:  *-----------------------------------------------------------------------
1.1       cgd       434:  * DirFindName --
                    435:  *     See if the Path structure describes the same directory as the
                    436:  *     given one by comparing their names. Called from Dir_AddDir via
                    437:  *     Lst_Find when searching the list of open directories.
                    438:  *
1.34      wiz       439:  * Input:
                    440:  *     p               Current name
                    441:  *     dname           Desired name
                    442:  *
1.1       cgd       443:  * Results:
                    444:  *     0 if it is the same. Non-zero otherwise
                    445:  *
                    446:  * Side Effects:
                    447:  *     None
                    448:  *-----------------------------------------------------------------------
                    449:  */
                    450: static int
1.34      wiz       451: DirFindName(ClientData p, ClientData dname)
1.1       cgd       452: {
1.46      christos  453:     return (strcmp(((Path *)p)->name, (char *) dname));
1.1       cgd       454: }
                    455:
                    456: /*-
                    457:  *-----------------------------------------------------------------------
                    458:  * Dir_HasWildcards  --
                    459:  *     see if the given name has any wildcard characters in it
1.13      christos  460:  *     be careful not to expand unmatching brackets or braces.
                    461:  *     XXX: This code is not 100% correct. ([^]] fails etc.)
                    462:  *     I really don't think that make(1) should be expanding
                    463:  *     patterns, because then you have to set a mechanism for
1.14      christos  464:  *     escaping the expansion!
1.1       cgd       465:  *
1.34      wiz       466:  * Input:
                    467:  *     name            name to check
                    468:  *
1.1       cgd       469:  * Results:
                    470:  *     returns TRUE if the word should be expanded, FALSE otherwise
                    471:  *
                    472:  * Side Effects:
                    473:  *     none
                    474:  *-----------------------------------------------------------------------
                    475:  */
                    476: Boolean
1.34      wiz       477: Dir_HasWildcards(char *name)
1.1       cgd       478: {
1.34      wiz       479:     char *cp;
1.13      christos  480:     int wild = 0, brace = 0, bracket = 0;
1.12      christos  481:
1.1       cgd       482:     for (cp = name; *cp; cp++) {
                    483:        switch(*cp) {
                    484:        case '{':
1.13      christos  485:                brace++;
                    486:                wild = 1;
                    487:                break;
                    488:        case '}':
                    489:                brace--;
                    490:                break;
1.1       cgd       491:        case '[':
1.13      christos  492:                bracket++;
                    493:                wild = 1;
                    494:                break;
                    495:        case ']':
                    496:                bracket--;
                    497:                break;
1.1       cgd       498:        case '?':
                    499:        case '*':
1.13      christos  500:                wild = 1;
                    501:                break;
                    502:        default:
                    503:                break;
1.1       cgd       504:        }
                    505:     }
1.13      christos  506:     return wild && bracket == 0 && brace == 0;
1.1       cgd       507: }
                    508:
                    509: /*-
                    510:  *-----------------------------------------------------------------------
                    511:  * DirMatchFiles --
                    512:  *     Given a pattern and a Path structure, see if any files
                    513:  *     match the pattern and add their names to the 'expansions' list if
                    514:  *     any do. This is incomplete -- it doesn't take care of patterns like
1.5       cgd       515:  *     src / *src / *.c properly (just *.c on any of the directories), but it
1.1       cgd       516:  *     will do for now.
                    517:  *
1.34      wiz       518:  * Input:
                    519:  *     pattern         Pattern to look for
                    520:  *     p               Directory to search
                    521:  *     expansion       Place to store the results
                    522:  *
1.1       cgd       523:  * Results:
                    524:  *     Always returns 0
                    525:  *
                    526:  * Side Effects:
                    527:  *     File names are added to the expansions lst. The directory will be
                    528:  *     fully hashed when this is done.
                    529:  *-----------------------------------------------------------------------
                    530:  */
                    531: static int
1.36      christos  532: DirMatchFiles(const char *pattern, Path *p, Lst expansions)
1.1       cgd       533: {
1.12      christos  534:     Hash_Search          search;       /* Index into the directory's table */
1.1       cgd       535:     Hash_Entry   *entry;       /* Current entry in the table */
                    536:     Boolean      isDot;        /* TRUE if the directory being searched is . */
1.12      christos  537:
1.1       cgd       538:     isDot = (*p->name == '.' && p->name[1] == '\0');
1.12      christos  539:
1.1       cgd       540:     for (entry = Hash_EnumFirst(&p->files, &search);
                    541:         entry != (Hash_Entry *)NULL;
                    542:         entry = Hash_EnumNext(&search))
                    543:     {
                    544:        /*
                    545:         * See if the file matches the given pattern. Note we follow the UNIX
                    546:         * convention that dot files will only be found if the pattern
                    547:         * begins with a dot (note also that as a side effect of the hashing
                    548:         * scheme, .* won't match . or .. since they aren't hashed).
                    549:         */
                    550:        if (Str_Match(entry->name, pattern) &&
                    551:            ((entry->name[0] != '.') ||
                    552:             (pattern[0] == '.')))
                    553:        {
                    554:            (void)Lst_AtEnd(expansions,
1.11      christos  555:                            (isDot ? estrdup(entry->name) :
1.1       cgd       556:                             str_concat(p->name, entry->name,
                    557:                                        STR_ADDSLASH)));
                    558:        }
                    559:     }
                    560:     return (0);
                    561: }
                    562:
                    563: /*-
                    564:  *-----------------------------------------------------------------------
                    565:  * DirExpandCurly --
                    566:  *     Expand curly braces like the C shell. Does this recursively.
                    567:  *     Note the special case: if after the piece of the curly brace is
                    568:  *     done there are no wildcard characters in the result, the result is
                    569:  *     placed on the list WITHOUT CHECKING FOR ITS EXISTENCE.
                    570:  *
1.34      wiz       571:  * Input:
                    572:  *     word            Entire word to expand
                    573:  *     brace           First curly brace in it
                    574:  *     path            Search path to use
                    575:  *     expansions      Place to store the expansions
                    576:  *
1.1       cgd       577:  * Results:
                    578:  *     None.
                    579:  *
                    580:  * Side Effects:
                    581:  *     The given list is filled with the expansions...
                    582:  *
                    583:  *-----------------------------------------------------------------------
                    584:  */
                    585: static void
1.36      christos  586: DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions)
1.1       cgd       587: {
1.36      christos  588:     const char   *end;         /* Character after the closing brace */
                    589:     const char   *cp;          /* Current position in brace clause */
                    590:     const char   *start;       /* Start of current piece of brace clause */
1.1       cgd       591:     int                  bracelevel;   /* Number of braces we've seen. If we see a
                    592:                                 * right brace when this is 0, we've hit the
                    593:                                 * end of the clause. */
1.36      christos  594:     char        *file;         /* Current expansion */
1.1       cgd       595:     int                  otherLen;     /* The length of the other pieces of the
                    596:                                 * expansion (chars before and after the
                    597:                                 * clause in 'word') */
1.36      christos  598:     char        *cp2;          /* Pointer for checking for wildcards in
1.1       cgd       599:                                 * expansion before calling Dir_Expand */
                    600:
                    601:     start = brace+1;
                    602:
                    603:     /*
                    604:      * Find the end of the brace clause first, being wary of nested brace
                    605:      * clauses.
                    606:      */
                    607:     for (end = start, bracelevel = 0; *end != '\0'; end++) {
                    608:        if (*end == '{') {
                    609:            bracelevel++;
                    610:        } else if ((*end == '}') && (bracelevel-- == 0)) {
                    611:            break;
                    612:        }
                    613:     }
                    614:     if (*end == '\0') {
                    615:        Error("Unterminated {} clause \"%s\"", start);
                    616:        return;
                    617:     } else {
                    618:        end++;
                    619:     }
                    620:     otherLen = brace - word + strlen(end);
                    621:
                    622:     for (cp = start; cp < end; cp++) {
                    623:        /*
                    624:         * Find the end of this piece of the clause.
                    625:         */
                    626:        bracelevel = 0;
                    627:        while (*cp != ',') {
                    628:            if (*cp == '{') {
                    629:                bracelevel++;
                    630:            } else if ((*cp == '}') && (bracelevel-- <= 0)) {
                    631:                break;
                    632:            }
                    633:            cp++;
                    634:        }
                    635:        /*
                    636:         * Allocate room for the combination and install the three pieces.
                    637:         */
                    638:        file = emalloc(otherLen + cp - start + 1);
                    639:        if (brace != word) {
                    640:            strncpy(file, word, brace-word);
                    641:        }
                    642:        if (cp != start) {
                    643:            strncpy(&file[brace-word], start, cp-start);
                    644:        }
                    645:        strcpy(&file[(brace-word)+(cp-start)], end);
                    646:
                    647:        /*
                    648:         * See if the result has any wildcards in it. If we find one, call
                    649:         * Dir_Expand right away, telling it to place the result on our list
                    650:         * of expansions.
                    651:         */
                    652:        for (cp2 = file; *cp2 != '\0'; cp2++) {
                    653:            switch(*cp2) {
                    654:            case '*':
                    655:            case '?':
                    656:            case '{':
                    657:            case '[':
                    658:                Dir_Expand(file, path, expansions);
                    659:                goto next;
                    660:            }
                    661:        }
                    662:        if (*cp2 == '\0') {
                    663:            /*
                    664:             * Hit the end w/o finding any wildcards, so stick the expansion
                    665:             * on the end of the list.
                    666:             */
                    667:            (void)Lst_AtEnd(expansions, file);
                    668:        } else {
                    669:        next:
                    670:            free(file);
                    671:        }
                    672:        start = cp+1;
                    673:     }
                    674: }
                    675:
                    676:
                    677: /*-
                    678:  *-----------------------------------------------------------------------
                    679:  * DirExpandInt --
                    680:  *     Internal expand routine. Passes through the directories in the
                    681:  *     path one by one, calling DirMatchFiles for each. NOTE: This still
                    682:  *     doesn't handle patterns in directories...
                    683:  *
1.34      wiz       684:  * Input:
                    685:  *     word            Word to expand
                    686:  *     path            Path on which to look
                    687:  *     expansions      Place to store the result
                    688:  *
1.1       cgd       689:  * Results:
                    690:  *     None.
                    691:  *
                    692:  * Side Effects:
                    693:  *     Things are added to the expansions list.
                    694:  *
                    695:  *-----------------------------------------------------------------------
                    696:  */
                    697: static void
1.36      christos  698: DirExpandInt(const char *word, Lst path, Lst expansions)
1.1       cgd       699: {
                    700:     LstNode      ln;           /* Current node */
                    701:     Path         *p;           /* Directory in the node */
                    702:
                    703:     if (Lst_Open(path) == SUCCESS) {
                    704:        while ((ln = Lst_Next(path)) != NILLNODE) {
                    705:            p = (Path *)Lst_Datum(ln);
                    706:            DirMatchFiles(word, p, expansions);
                    707:        }
                    708:        Lst_Close(path);
                    709:     }
                    710: }
                    711:
                    712: /*-
                    713:  *-----------------------------------------------------------------------
                    714:  * DirPrintWord --
                    715:  *     Print a word in the list of expansions. Callback for Dir_Expand
                    716:  *     when DEBUG(DIR), via Lst_ForEach.
                    717:  *
                    718:  * Results:
                    719:  *     === 0
                    720:  *
                    721:  * Side Effects:
                    722:  *     The passed word is printed, followed by a space.
                    723:  *
                    724:  *-----------------------------------------------------------------------
                    725:  */
                    726: static int
1.34      wiz       727: DirPrintWord(ClientData word, ClientData dummy)
1.1       cgd       728: {
1.6       jtc       729:     printf("%s ", (char *) word);
1.1       cgd       730:
1.6       jtc       731:     return(dummy ? 0 : 0);
1.1       cgd       732: }
                    733:
                    734: /*-
                    735:  *-----------------------------------------------------------------------
                    736:  * Dir_Expand  --
                    737:  *     Expand the given word into a list of words by globbing it looking
                    738:  *     in the directories on the given search path.
                    739:  *
1.34      wiz       740:  * Input:
                    741:  *     word            the word to expand
                    742:  *     path            the list of directories in which to find the
                    743:  *                     resulting files
                    744:  *     expansions      the list on which to place the results
                    745:  *
1.1       cgd       746:  * Results:
                    747:  *     A list of words consisting of the files which exist along the search
                    748:  *     path matching the given pattern.
                    749:  *
                    750:  * Side Effects:
                    751:  *     Directories may be opened. Who knows?
                    752:  *-----------------------------------------------------------------------
                    753:  */
                    754: void
1.36      christos  755: Dir_Expand(const char *word, Lst path, Lst expansions)
1.1       cgd       756: {
1.36      christos  757:     const char           *cp;
1.1       cgd       758:
                    759:     if (DEBUG(DIR)) {
1.39      dsl       760:        printf("Expanding \"%s\"... ", word);
1.1       cgd       761:     }
1.12      christos  762:
1.5       cgd       763:     cp = strchr(word, '{');
1.1       cgd       764:     if (cp) {
                    765:        DirExpandCurly(word, cp, path, expansions);
                    766:     } else {
1.5       cgd       767:        cp = strchr(word, '/');
1.1       cgd       768:        if (cp) {
                    769:            /*
                    770:             * The thing has a directory component -- find the first wildcard
                    771:             * in the string.
                    772:             */
                    773:            for (cp = word; *cp; cp++) {
                    774:                if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') {
                    775:                    break;
                    776:                }
                    777:            }
                    778:            if (*cp == '{') {
                    779:                /*
                    780:                 * This one will be fun.
                    781:                 */
                    782:                DirExpandCurly(word, cp, path, expansions);
                    783:                return;
                    784:            } else if (*cp != '\0') {
                    785:                /*
                    786:                 * Back up to the start of the component
                    787:                 */
                    788:                char  *dirpath;
                    789:
                    790:                while (cp > word && *cp != '/') {
                    791:                    cp--;
                    792:                }
                    793:                if (cp != word) {
1.5       cgd       794:                    char sc;
1.1       cgd       795:                    /*
                    796:                     * If the glob isn't in the first component, try and find
                    797:                     * all the components up to the one with a wildcard.
                    798:                     */
1.5       cgd       799:                    sc = cp[1];
1.36      christos  800:                    ((char *)UNCONST(cp))[1] = '\0';
1.1       cgd       801:                    dirpath = Dir_FindFile(word, path);
1.36      christos  802:                    ((char *)UNCONST(cp))[1] = sc;
1.1       cgd       803:                    /*
                    804:                     * dirpath is null if can't find the leading component
                    805:                     * XXX: Dir_FindFile won't find internal components.
                    806:                     * i.e. if the path contains ../Etc/Object and we're
                    807:                     * looking for Etc, it won't be found. Ah well.
                    808:                     * Probably not important.
                    809:                     */
                    810:                    if (dirpath != (char *)NULL) {
1.5       cgd       811:                        char *dp = &dirpath[strlen(dirpath) - 1];
                    812:                        if (*dp == '/')
                    813:                            *dp = '\0';
1.1       cgd       814:                        path = Lst_Init(FALSE);
1.46      christos  815:                        (void)Dir_AddDir(path, dirpath);
1.1       cgd       816:                        DirExpandInt(cp+1, path, expansions);
                    817:                        Lst_Destroy(path, NOFREE);
                    818:                    }
                    819:                } else {
                    820:                    /*
                    821:                     * Start the search from the local directory
                    822:                     */
                    823:                    DirExpandInt(word, path, expansions);
                    824:                }
                    825:            } else {
                    826:                /*
                    827:                 * Return the file -- this should never happen.
                    828:                 */
                    829:                DirExpandInt(word, path, expansions);
                    830:            }
                    831:        } else {
                    832:            /*
                    833:             * First the files in dot
                    834:             */
                    835:            DirMatchFiles(word, dot, expansions);
1.12      christos  836:
1.1       cgd       837:            /*
                    838:             * Then the files in every other directory on the path.
                    839:             */
                    840:            DirExpandInt(word, path, expansions);
                    841:        }
                    842:     }
                    843:     if (DEBUG(DIR)) {
1.6       jtc       844:        Lst_ForEach(expansions, DirPrintWord, (ClientData) 0);
1.5       cgd       845:        fputc('\n', stdout);
1.1       cgd       846:     }
                    847: }
                    848:
                    849: /*-
                    850:  *-----------------------------------------------------------------------
1.18      christos  851:  * DirLookup  --
                    852:  *     Find if the file with the given name exists in the given path.
                    853:  *
                    854:  * Results:
1.32      pk        855:  *     The path to the file or NULL. This path is guaranteed to be in a
1.18      christos  856:  *     different part of memory than name and so may be safely free'd.
                    857:  *
                    858:  * Side Effects:
                    859:  *     None.
                    860:  *-----------------------------------------------------------------------
                    861:  */
                    862: static char *
1.43      jmc       863: DirLookup(Path *p, const char *name __unused, const char *cp,
                    864:           Boolean hasSlash __unused)
1.18      christos  865: {
                    866:     char *file;                /* the current filename to check */
                    867:
                    868:     if (DEBUG(DIR)) {
1.39      dsl       869:        printf("   %s ...\n", p->name);
1.18      christos  870:     }
1.32      pk        871:
1.45      christos  872:     if (Hash_FindEntry(&p->files, cp) == (Hash_Entry *)NULL)
1.32      pk        873:        return NULL;
                    874:
1.45      christos  875:     file = str_concat(p->name, cp, STR_ADDSLASH);
1.32      pk        876:     if (DEBUG(DIR)) {
1.39      dsl       877:        printf("   returning %s\n", file);
1.18      christos  878:     }
1.32      pk        879:     p->hits += 1;
                    880:     hits += 1;
                    881:     return file;
1.18      christos  882: }
                    883:
                    884:
                    885: /*-
                    886:  *-----------------------------------------------------------------------
                    887:  * DirLookupSubdir  --
                    888:  *     Find if the file with the given name exists in the given path.
                    889:  *
                    890:  * Results:
                    891:  *     The path to the file or NULL. This path is guaranteed to be in a
                    892:  *     different part of memory than name and so may be safely free'd.
                    893:  *
                    894:  * Side Effects:
                    895:  *     If the file is found, it is added in the modification times hash
                    896:  *     table.
                    897:  *-----------------------------------------------------------------------
                    898:  */
                    899: static char *
1.36      christos  900: DirLookupSubdir(Path *p, const char *name)
1.18      christos  901: {
                    902:     struct stat          stb;          /* Buffer for stat, if necessary */
                    903:     Hash_Entry  *entry;        /* Entry for mtimes table */
                    904:     char        *file;         /* the current filename to check */
                    905:
                    906:     if (p != dot) {
1.45      christos  907:        file = str_concat(p->name, name, STR_ADDSLASH);
1.18      christos  908:     } else {
                    909:        /*
                    910:         * Checking in dot -- DON'T put a leading ./ on the thing.
                    911:         */
                    912:        file = estrdup(name);
                    913:     }
                    914:
                    915:     if (DEBUG(DIR)) {
1.39      dsl       916:        printf("checking %s ...\n", file);
1.18      christos  917:     }
                    918:
1.46      christos  919:     if (stat(file, &stb) == 0) {
1.18      christos  920:        /*
                    921:         * Save the modification time so if it's needed, we don't have
                    922:         * to fetch it again.
                    923:         */
                    924:        if (DEBUG(DIR)) {
1.39      dsl       925:            printf("   Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
1.18      christos  926:                    file);
                    927:        }
                    928:        entry = Hash_CreateEntry(&mtimes, (char *) file,
                    929:                                 (Boolean *)NULL);
                    930:        Hash_SetValue(entry, (long)stb.st_mtime);
                    931:        nearmisses += 1;
                    932:        return (file);
                    933:     }
1.46      christos  934:     free(file);
1.18      christos  935:     return NULL;
                    936: }
                    937:
                    938: /*-
                    939:  *-----------------------------------------------------------------------
1.32      pk        940:  * DirLookupAbs  --
                    941:  *     Find if the file with the given name exists in the given path.
                    942:  *
                    943:  * Results:
                    944:  *     The path to the file, the empty string or NULL. If the file is
                    945:  *     the empty string, the search should be terminated.
                    946:  *     This path is guaranteed to be in a different part of memory
                    947:  *     than name and so may be safely free'd.
                    948:  *
                    949:  * Side Effects:
                    950:  *     None.
                    951:  *-----------------------------------------------------------------------
                    952:  */
                    953: static char *
1.36      christos  954: DirLookupAbs(Path *p, const char *name, const char *cp)
1.32      pk        955: {
                    956:        char *p1;               /* pointer into p->name */
1.36      christos  957:        const char *p2;         /* pointer into name */
1.32      pk        958:
                    959:        if (DEBUG(DIR)) {
1.39      dsl       960:                printf("   %s ...\n", p->name);
1.32      pk        961:        }
                    962:
                    963:        /*
                    964:         * If the file has a leading path component and that component
                    965:         * exactly matches the entire name of the current search
                    966:         * directory, we can attempt another cache lookup. And if we don't
                    967:         * have a hit, we can safely assume the file does not exist at all.
                    968:         */
                    969:        for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) {
                    970:                continue;
                    971:        }
                    972:        if (*p1 != '\0' || p2 != cp - 1) {
                    973:                return NULL;
                    974:        }
                    975:
1.45      christos  976:        if (Hash_FindEntry(&p->files, cp) == (Hash_Entry *)NULL) {
1.32      pk        977:                if (DEBUG(DIR)) {
1.39      dsl       978:                        printf("   must be here but isn't -- returning\n");
1.32      pk        979:                }
                    980:                /* Return empty string: terminates search */
1.36      christos  981:                return estrdup("");
1.32      pk        982:        }
                    983:
                    984:        p->hits += 1;
                    985:        hits += 1;
                    986:        if (DEBUG(DIR)) {
1.39      dsl       987:                printf("   returning %s\n", name);
1.32      pk        988:        }
1.45      christos  989:        return (estrdup(name));
1.32      pk        990: }
                    991:
                    992: /*-
                    993:  *-----------------------------------------------------------------------
1.21      thorpej   994:  * DirFindDot  --
                    995:  *     Find the file given on "." or curdir
                    996:  *
                    997:  * Results:
                    998:  *     The path to the file or NULL. This path is guaranteed to be in a
                    999:  *     different part of memory than name and so may be safely free'd.
                   1000:  *
                   1001:  * Side Effects:
                   1002:  *     Hit counts change
                   1003:  *-----------------------------------------------------------------------
                   1004:  */
                   1005: static char *
1.43      jmc      1006: DirFindDot(Boolean hasSlash __unused, const char *name, const char *cp)
1.21      thorpej  1007: {
                   1008:
1.45      christos 1009:        if (Hash_FindEntry(&dot->files, cp) != (Hash_Entry *)NULL) {
1.21      thorpej  1010:            if (DEBUG(DIR)) {
1.39      dsl      1011:                printf("   in '.'\n");
1.21      thorpej  1012:            }
                   1013:            hits += 1;
                   1014:            dot->hits += 1;
1.45      christos 1015:            return (estrdup(name));
1.21      thorpej  1016:        }
                   1017:        if (cur &&
1.45      christos 1018:            Hash_FindEntry(&cur->files, cp) != (Hash_Entry *)NULL) {
1.21      thorpej  1019:            if (DEBUG(DIR)) {
1.39      dsl      1020:                printf("   in ${.CURDIR} = %s\n", cur->name);
1.21      thorpej  1021:            }
                   1022:            hits += 1;
                   1023:            cur->hits += 1;
1.45      christos 1024:            return str_concat(cur->name, cp, STR_ADDSLASH);
1.21      thorpej  1025:        }
                   1026:
1.32      pk       1027:        return NULL;
1.21      thorpej  1028: }
                   1029:
                   1030: /*-
                   1031:  *-----------------------------------------------------------------------
1.1       cgd      1032:  * Dir_FindFile  --
                   1033:  *     Find the file with the given name along the given search path.
                   1034:  *
1.34      wiz      1035:  * Input:
                   1036:  *     name            the file to find
                   1037:  *     path            the Lst of directories to search
                   1038:  *
1.1       cgd      1039:  * Results:
                   1040:  *     The path to the file or NULL. This path is guaranteed to be in a
                   1041:  *     different part of memory than name and so may be safely free'd.
                   1042:  *
                   1043:  * Side Effects:
                   1044:  *     If the file is found in a directory which is not on the path
                   1045:  *     already (either 'name' is absolute or it is a relative path
                   1046:  *     [ dir1/.../dirn/file ] which exists below one of the directories
                   1047:  *     already on the search path), its directory is added to the end
                   1048:  *     of the path on the assumption that there will be more files in
                   1049:  *     that directory later on. Sometimes this is true. Sometimes not.
                   1050:  *-----------------------------------------------------------------------
                   1051:  */
                   1052: char *
1.36      christos 1053: Dir_FindFile(const char *name, Lst path)
1.1       cgd      1054: {
1.21      thorpej  1055:     LstNode       ln;                  /* a list element */
1.34      wiz      1056:     char         *file;                /* the current filename to check */
                   1057:     Path         *p;                   /* current path member */
1.36      christos 1058:     const char   *cp;                  /* index of first slash, if any */
1.31      reinoud  1059:     Boolean      hasLastDot = FALSE;   /* true we should search dot last */
1.21      thorpej  1060:     Boolean      hasSlash;             /* true if 'name' contains a / */
                   1061:     struct stat          stb;                  /* Buffer for stat, if necessary */
                   1062:     Hash_Entry   *entry;               /* Entry for mtimes table */
1.12      christos 1063:
1.1       cgd      1064:     /*
                   1065:      * Find the final component of the name and note whether it has a
                   1066:      * slash in it (the name, I mean)
                   1067:      */
1.46      christos 1068:     cp = strrchr(name, '/');
1.1       cgd      1069:     if (cp) {
                   1070:        hasSlash = TRUE;
                   1071:        cp += 1;
                   1072:     } else {
                   1073:        hasSlash = FALSE;
                   1074:        cp = name;
                   1075:     }
1.12      christos 1076:
1.1       cgd      1077:     if (DEBUG(DIR)) {
1.39      dsl      1078:        printf("Searching for %s ...", name);
1.1       cgd      1079:     }
1.12      christos 1080:
1.45      christos 1081:     if (Lst_Open(path) == FAILURE) {
1.1       cgd      1082:        if (DEBUG(DIR)) {
                   1083:            printf("couldn't open path, file not found\n");
                   1084:        }
                   1085:        misses += 1;
                   1086:        return ((char *) NULL);
                   1087:     }
1.12      christos 1088:
1.45      christos 1089:     if ((ln = Lst_First(path)) != NILLNODE) {
1.46      christos 1090:        p = (Path *)Lst_Datum(ln);
1.31      reinoud  1091:        if (p == dotLast) {
                   1092:            hasLastDot = TRUE;
1.32      pk       1093:             if (DEBUG(DIR))
                   1094:                printf("[dot last]...");
1.21      thorpej  1095:        }
1.18      christos 1096:     }
1.39      dsl      1097:     if (DEBUG(DIR)) {
                   1098:        printf("\n");
                   1099:     }
1.18      christos 1100:
1.1       cgd      1101:     /*
1.32      pk       1102:      * If there's no leading directory components or if the leading
                   1103:      * directory component is exactly `./', consult the cached contents
                   1104:      * of each of the directories on the search path.
1.21      thorpej  1105:      */
1.32      pk       1106:     if ((!hasSlash || (cp - name == 2 && *name == '.'))) {
                   1107:            /*
                   1108:             * We look through all the directories on the path seeking one which
                   1109:             * contains the final component of the given name.  If such a beast
                   1110:             * is found, we concatenate the directory name and the final
                   1111:             * component and return the resulting string. If we don't find any
                   1112:             * such thing, we go on to phase two...
                   1113:             *
                   1114:             * No matter what, we always look for the file in the current
                   1115:             * directory before anywhere else (unless we found the magic
                   1116:             * DOTLAST path, in which case we search it last) and we *do not*
                   1117:             * add the ./ to it if it exists.
                   1118:             * This is so there are no conflicts between what the user
                   1119:             * specifies (fish.c) and what pmake finds (./fish.c).
                   1120:             */
                   1121:            if (!hasLastDot &&
                   1122:                        (file = DirFindDot(hasSlash, name, cp)) != NULL) {
1.45      christos 1123:                    Lst_Close(path);
1.32      pk       1124:                    return file;
                   1125:            }
                   1126:
1.45      christos 1127:            while ((ln = Lst_Next(path)) != NILLNODE) {
1.46      christos 1128:                p = (Path *)Lst_Datum(ln);
1.32      pk       1129:                if (p == dotLast)
                   1130:                    continue;
                   1131:                if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) {
1.45      christos 1132:                    Lst_Close(path);
1.32      pk       1133:                        return file;
                   1134:                }
                   1135:            }
1.21      thorpej  1136:
1.32      pk       1137:            if (hasLastDot &&
                   1138:                        (file = DirFindDot(hasSlash, name, cp)) != NULL) {
1.45      christos 1139:                    Lst_Close(path);
1.32      pk       1140:                    return file;
                   1141:            }
1.1       cgd      1142:     }
1.45      christos 1143:     Lst_Close(path);
1.12      christos 1144:
1.1       cgd      1145:     /*
1.32      pk       1146:      * We didn't find the file on any directory in the search path.
1.1       cgd      1147:      * If the name doesn't contain a slash, that means it doesn't exist.
                   1148:      * If it *does* contain a slash, however, there is still hope: it
                   1149:      * could be in a subdirectory of one of the members of the search
                   1150:      * path. (eg. /usr/include and sys/types.h. The above search would
                   1151:      * fail to turn up types.h in /usr/include, but it *is* in
1.32      pk       1152:      * /usr/include/sys/types.h).
                   1153:      * [ This no longer applies: If we find such a beast, we assume there
1.1       cgd      1154:      * will be more (what else can we assume?) and add all but the last
                   1155:      * component of the resulting name onto the search path (at the
1.32      pk       1156:      * end).]
                   1157:      * This phase is only performed if the file is *not* absolute.
1.1       cgd      1158:      */
                   1159:     if (!hasSlash) {
                   1160:        if (DEBUG(DIR)) {
1.39      dsl      1161:            printf("   failed.\n");
1.1       cgd      1162:        }
                   1163:        misses += 1;
                   1164:        return ((char *) NULL);
                   1165:     }
1.12      christos 1166:
1.32      pk       1167:     if (name[0] != '/') {
1.1       cgd      1168:        Boolean checkedDot = FALSE;
1.12      christos 1169:
1.1       cgd      1170:        if (DEBUG(DIR)) {
1.39      dsl      1171:            printf("   Trying subdirectories...\n");
1.1       cgd      1172:        }
1.18      christos 1173:
1.35      sjg      1174:        if (!hasLastDot) {
                   1175:                if (dot) {
                   1176:                        checkedDot = TRUE;
                   1177:                        if ((file = DirLookupSubdir(dot, name)) != NULL)
                   1178:                                return file;
                   1179:                }
                   1180:                if (cur && (file = DirLookupSubdir(cur, name)) != NULL)
                   1181:                        return file;
                   1182:        }
1.18      christos 1183:
1.46      christos 1184:        (void)Lst_Open(path);
1.45      christos 1185:        while ((ln = Lst_Next(path)) != NILLNODE) {
1.46      christos 1186:            p = (Path *)Lst_Datum(ln);
1.21      thorpej  1187:            if (p == dotLast)
                   1188:                continue;
1.35      sjg      1189:            if (p == dot) {
                   1190:                    if (checkedDot)
                   1191:                            continue;
1.1       cgd      1192:                checkedDot = TRUE;
1.35      sjg      1193:            }
1.18      christos 1194:            if ((file = DirLookupSubdir(p, name)) != NULL) {
1.45      christos 1195:                Lst_Close(path);
1.18      christos 1196:                return file;
1.1       cgd      1197:            }
                   1198:        }
1.45      christos 1199:        Lst_Close(path);
1.12      christos 1200:
1.35      sjg      1201:        if (hasLastDot) {
                   1202:                if (dot && !checkedDot) {
                   1203:                        checkedDot = TRUE;
                   1204:                        if ((file = DirLookupSubdir(dot, name)) != NULL)
                   1205:                                return file;
                   1206:                }
                   1207:                if (cur && (file = DirLookupSubdir(cur, name)) != NULL)
                   1208:                        return file;
                   1209:        }
1.21      thorpej  1210:
1.1       cgd      1211:        if (checkedDot) {
                   1212:            /*
                   1213:             * Already checked by the given name, since . was in the path,
                   1214:             * so no point in proceeding...
                   1215:             */
                   1216:            if (DEBUG(DIR)) {
1.39      dsl      1217:                printf("   Checked . already, returning NULL\n");
1.1       cgd      1218:            }
                   1219:            return(NULL);
1.32      pk       1220:        }
                   1221:
                   1222:     } else { /* name[0] == '/' */
                   1223:
                   1224:        /*
                   1225:         * For absolute names, compare directory path prefix against the
                   1226:         * the directory path of each member on the search path for an exact
                   1227:         * match. If we have an exact match on any member of the search path,
                   1228:         * use the cached contents of that member to lookup the final file
                   1229:         * component. If that lookup fails we can safely assume that the
                   1230:         * file does not exist at all.  This is signified by DirLookupAbs()
                   1231:         * returning an empty string.
                   1232:         */
                   1233:        if (DEBUG(DIR)) {
1.39      dsl      1234:            printf("   Trying exact path matches...\n");
1.32      pk       1235:        }
                   1236:
                   1237:        if (!hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL)
                   1238:            return *file?file:NULL;
                   1239:
1.46      christos 1240:        (void)Lst_Open(path);
1.45      christos 1241:        while ((ln = Lst_Next(path)) != NILLNODE) {
1.46      christos 1242:            p = (Path *)Lst_Datum(ln);
1.32      pk       1243:            if (p == dotLast)
                   1244:                continue;
                   1245:            if ((file = DirLookupAbs(p, name, cp)) != NULL) {
1.45      christos 1246:                Lst_Close(path);
1.32      pk       1247:                return *file?file:NULL;
                   1248:            }
                   1249:        }
1.45      christos 1250:        Lst_Close(path);
1.32      pk       1251:
                   1252:        if (hasLastDot && cur && (file = DirLookupAbs(cur, name, cp)) != NULL)
                   1253:            return *file?file:NULL;
1.1       cgd      1254:     }
1.12      christos 1255:
1.1       cgd      1256:     /*
                   1257:      * Didn't find it that way, either. Sigh. Phase 3. Add its directory
                   1258:      * onto the search path in any case, just in case, then look for the
                   1259:      * thing in the hash table. If we find it, grand. We return a new
                   1260:      * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.
                   1261:      * Note that if the directory holding the file doesn't exist, this will
                   1262:      * do an extra search of the final directory on the path. Unless something
                   1263:      * weird happens, this search won't succeed and life will be groovy.
                   1264:      *
                   1265:      * Sigh. We cannot add the directory onto the search path because
                   1266:      * of this amusing case:
                   1267:      * $(INSTALLDIR)/$(FILE): $(FILE)
                   1268:      *
                   1269:      * $(FILE) exists in $(INSTALLDIR) but not in the current one.
                   1270:      * When searching for $(FILE), we will find it in $(INSTALLDIR)
                   1271:      * b/c we added it here. This is not good...
                   1272:      */
                   1273: #ifdef notdef
                   1274:     cp[-1] = '\0';
1.46      christos 1275:     (void)Dir_AddDir(path, name);
1.1       cgd      1276:     cp[-1] = '/';
1.12      christos 1277:
1.1       cgd      1278:     bigmisses += 1;
1.45      christos 1279:     ln = Lst_Last(path);
1.1       cgd      1280:     if (ln == NILLNODE) {
                   1281:        return ((char *) NULL);
                   1282:     } else {
1.46      christos 1283:        p = (Path *)Lst_Datum(ln);
1.1       cgd      1284:     }
1.12      christos 1285:
1.45      christos 1286:     if (Hash_FindEntry(&p->files, cp) != (Hash_Entry *)NULL) {
                   1287:        return (estrdup(name));
1.1       cgd      1288:     } else {
                   1289:        return ((char *) NULL);
                   1290:     }
                   1291: #else /* !notdef */
                   1292:     if (DEBUG(DIR)) {
1.39      dsl      1293:        printf("   Looking for \"%s\" ...\n", name);
1.1       cgd      1294:     }
1.12      christos 1295:
1.1       cgd      1296:     bigmisses += 1;
                   1297:     entry = Hash_FindEntry(&mtimes, name);
                   1298:     if (entry != (Hash_Entry *)NULL) {
                   1299:        if (DEBUG(DIR)) {
1.39      dsl      1300:            printf("   got it (in mtime cache)\n");
1.1       cgd      1301:        }
1.11      christos 1302:        return(estrdup(name));
1.46      christos 1303:     } else if (stat(name, &stb) == 0) {
1.1       cgd      1304:        entry = Hash_CreateEntry(&mtimes, name, (Boolean *)NULL);
                   1305:        if (DEBUG(DIR)) {
1.39      dsl      1306:            printf("   Caching %s for %s\n", Targ_FmtTime(stb.st_mtime),
1.1       cgd      1307:                    name);
                   1308:        }
1.7       cgd      1309:        Hash_SetValue(entry, (long)stb.st_mtime);
1.45      christos 1310:        return (estrdup(name));
1.1       cgd      1311:     } else {
                   1312:        if (DEBUG(DIR)) {
1.39      dsl      1313:            printf("   failed. Returning NULL\n");
1.1       cgd      1314:        }
                   1315:        return ((char *)NULL);
                   1316:     }
                   1317: #endif /* notdef */
                   1318: }
                   1319:
1.40      chuck    1320:
                   1321: /*-
                   1322:  *-----------------------------------------------------------------------
                   1323:  * Dir_FindHereOrAbove  --
                   1324:  *     search for a path starting at a given directory and then working
                   1325:  *     our way up towards the root.
                   1326:  *
                   1327:  * Input:
                   1328:  *     here            starting directory
                   1329:  *     search_path     the path we are looking for
                   1330:  *     result          the result of a successful search is placed here
                   1331:  *     rlen            the length of the result buffer
                   1332:  *                     (typically MAXPATHLEN + 1)
                   1333:  *
                   1334:  * Results:
                   1335:  *     0 on failure, 1 on success [in which case the found path is put
                   1336:  *     in the result buffer].
                   1337:  *
                   1338:  * Side Effects:
                   1339:  *-----------------------------------------------------------------------
                   1340:  */
                   1341: int
                   1342: Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) {
                   1343:
                   1344:        struct stat st;
                   1345:        char dirbase[MAXPATHLEN + 1], *db_end;
                   1346:         char try[MAXPATHLEN + 1], *try_end;
                   1347:
                   1348:        /* copy out our starting point */
                   1349:        snprintf(dirbase, sizeof(dirbase), "%s", here);
                   1350:        db_end = dirbase + strlen(dirbase);
                   1351:
                   1352:        /* loop until we determine a result */
                   1353:        while (1) {
                   1354:
                   1355:                /* try and stat(2) it ... */
                   1356:                snprintf(try, sizeof(try), "%s/%s", dirbase, search_path);
                   1357:                if (stat(try, &st) != -1) {
                   1358:                        /*
                   1359:                         * success!  if we found a file, chop off
                   1360:                         * the filename so we return a directory.
                   1361:                         */
                   1362:                        if ((st.st_mode & S_IFMT) != S_IFDIR) {
                   1363:                                try_end = try + strlen(try);
                   1364:                                while (try_end > try && *try_end != '/')
                   1365:                                        try_end--;
                   1366:                                if (try_end > try)
                   1367:                                        *try_end = 0;   /* chop! */
                   1368:                        }
                   1369:
                   1370:                        /*
                   1371:                         * done!
                   1372:                         */
                   1373:                        snprintf(result, rlen, "%s", try);
                   1374:                        return(1);
                   1375:                }
                   1376:
                   1377:                /*
                   1378:                 * nope, we didn't find it.  if we used up dirbase we've
                   1379:                 * reached the root and failed.
                   1380:                 */
                   1381:                if (db_end == dirbase)
                   1382:                        break;          /* failed! */
                   1383:
                   1384:                /*
                   1385:                 * truncate dirbase from the end to move up a dir
                   1386:                 */
                   1387:                while (db_end > dirbase && *db_end != '/')
                   1388:                        db_end--;
                   1389:                *db_end = 0;            /* chop! */
                   1390:
                   1391:        } /* while (1) */
                   1392:
                   1393:        /*
                   1394:         * we failed...
                   1395:         */
                   1396:        return(0);
                   1397: }
                   1398:
1.1       cgd      1399: /*-
                   1400:  *-----------------------------------------------------------------------
                   1401:  * Dir_MTime  --
                   1402:  *     Find the modification time of the file described by gn along the
                   1403:  *     search path dirSearchPath.
1.12      christos 1404:  *
1.34      wiz      1405:  * Input:
                   1406:  *     gn              the file whose modification time is desired
                   1407:  *
1.1       cgd      1408:  * Results:
                   1409:  *     The modification time or 0 if it doesn't exist
                   1410:  *
                   1411:  * Side Effects:
                   1412:  *     The modification time is placed in the node's mtime slot.
                   1413:  *     If the node didn't have a path entry before, and Dir_FindFile
                   1414:  *     found one for it, the full name is placed in the path slot.
                   1415:  *-----------------------------------------------------------------------
                   1416:  */
                   1417: int
1.34      wiz      1418: Dir_MTime(GNode *gn)
1.1       cgd      1419: {
                   1420:     char          *fullName;  /* the full pathname of name */
                   1421:     struct stat          stb;        /* buffer for finding the mod time */
                   1422:     Hash_Entry   *entry;
1.12      christos 1423:
1.1       cgd      1424:     if (gn->type & OP_ARCHV) {
1.45      christos 1425:        return Arch_MTime(gn);
1.33      pk       1426:     } else if (gn->type & OP_PHONY) {
                   1427:        gn->mtime = 0;
                   1428:        return 0;
1.25      mrg      1429:     } else if (gn->path == (char *)NULL) {
1.33      pk       1430:        if (gn->type & OP_NOPATH)
1.16      mycroft  1431:            fullName = NULL;
                   1432:        else
1.45      christos 1433:            fullName = Dir_FindFile(gn->name, Suff_FindPath(gn));
1.1       cgd      1434:     } else {
                   1435:        fullName = gn->path;
                   1436:     }
1.12      christos 1437:
1.1       cgd      1438:     if (fullName == (char *)NULL) {
1.11      christos 1439:        fullName = estrdup(gn->name);
1.1       cgd      1440:     }
                   1441:
                   1442:     entry = Hash_FindEntry(&mtimes, fullName);
                   1443:     if (entry != (Hash_Entry *)NULL) {
                   1444:        /*
                   1445:         * Only do this once -- the second time folks are checking to
                   1446:         * see if the file was actually updated, so we need to actually go
                   1447:         * to the file system.
                   1448:         */
                   1449:        if (DEBUG(DIR)) {
                   1450:            printf("Using cached time %s for %s\n",
1.7       cgd      1451:                    Targ_FmtTime((time_t)(long)Hash_GetValue(entry)), fullName);
1.1       cgd      1452:        }
1.7       cgd      1453:        stb.st_mtime = (time_t)(long)Hash_GetValue(entry);
1.1       cgd      1454:        Hash_DeleteEntry(&mtimes, entry);
1.46      christos 1455:     } else if (stat(fullName, &stb) < 0) {
1.1       cgd      1456:        if (gn->type & OP_MEMBER) {
1.6       jtc      1457:            if (fullName != gn->path)
                   1458:                free(fullName);
1.45      christos 1459:            return Arch_MemMTime(gn);
1.1       cgd      1460:        } else {
                   1461:            stb.st_mtime = 0;
                   1462:        }
                   1463:     }
                   1464:     if (fullName && gn->path == (char *)NULL) {
                   1465:        gn->path = fullName;
                   1466:     }
1.12      christos 1467:
1.1       cgd      1468:     gn->mtime = stb.st_mtime;
                   1469:     return (gn->mtime);
                   1470: }
                   1471:
                   1472: /*-
                   1473:  *-----------------------------------------------------------------------
                   1474:  * Dir_AddDir --
                   1475:  *     Add the given name to the end of the given path. The order of
                   1476:  *     the arguments is backwards so ParseDoDependency can do a
                   1477:  *     Lst_ForEach of its list of paths...
                   1478:  *
1.34      wiz      1479:  * Input:
                   1480:  *     path            the path to which the directory should be
                   1481:  *                     added
                   1482:  *     name            the name of the directory to add
                   1483:  *
1.1       cgd      1484:  * Results:
                   1485:  *     none
                   1486:  *
                   1487:  * Side Effects:
1.12      christos 1488:  *     A structure is added to the list and the directory is
1.1       cgd      1489:  *     read and hashed.
                   1490:  *-----------------------------------------------------------------------
                   1491:  */
1.17      gwr      1492: Path *
1.34      wiz      1493: Dir_AddDir(Lst path, const char *name)
1.1       cgd      1494: {
1.35      sjg      1495:     LstNode       ln = NILLNODE; /* node in case Path structure is found */
1.34      wiz      1496:     Path         *p = NULL;  /* pointer to new Path structure */
1.1       cgd      1497:     DIR          *d;         /* for reading directory */
1.34      wiz      1498:     struct dirent *dp;       /* entry in directory */
1.21      thorpej  1499:
                   1500:     if (strcmp(name, ".DOTLAST") == 0) {
1.45      christos 1501:        ln = Lst_Find(path, (ClientData)UNCONST(name), DirFindName);
1.21      thorpej  1502:        if (ln != NILLNODE)
1.46      christos 1503:            return (Path *)Lst_Datum(ln);
1.21      thorpej  1504:        else {
                   1505:            dotLast->refCount += 1;
                   1506:            (void)Lst_AtFront(path, (ClientData)dotLast);
                   1507:        }
                   1508:     }
1.12      christos 1509:
1.35      sjg      1510:     if (path)
1.45      christos 1511:        ln = Lst_Find(openDirectories, (ClientData)UNCONST(name), DirFindName);
1.1       cgd      1512:     if (ln != NILLNODE) {
1.46      christos 1513:        p = (Path *)Lst_Datum(ln);
1.1       cgd      1514:        if (Lst_Member(path, (ClientData)p) == NILLNODE) {
                   1515:            p->refCount += 1;
1.46      christos 1516:            (void)Lst_AtEnd(path, (ClientData)p);
1.1       cgd      1517:        }
                   1518:     } else {
                   1519:        if (DEBUG(DIR)) {
1.39      dsl      1520:            printf("Caching %s ...", name);
1.1       cgd      1521:            fflush(stdout);
                   1522:        }
1.12      christos 1523:
1.46      christos 1524:        if ((d = opendir(name)) != (DIR *) NULL) {
1.45      christos 1525:            p = emalloc(sizeof(Path));
                   1526:            p->name = estrdup(name);
1.1       cgd      1527:            p->hits = 0;
                   1528:            p->refCount = 1;
1.45      christos 1529:            Hash_InitTable(&p->files, -1);
1.12      christos 1530:
1.46      christos 1531:            while ((dp = readdir(d)) != (struct dirent *) NULL) {
1.10      christos 1532: #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */
1.1       cgd      1533:                /*
                   1534:                 * The sun directory library doesn't check for a 0 inode
                   1535:                 * (0-inode slots just take up space), so we have to do
                   1536:                 * it ourselves.
                   1537:                 */
                   1538:                if (dp->d_fileno == 0) {
                   1539:                    continue;
                   1540:                }
1.10      christos 1541: #endif /* sun && d_ino */
1.1       cgd      1542:                (void)Hash_CreateEntry(&p->files, dp->d_name, (Boolean *)NULL);
                   1543:            }
1.46      christos 1544:            (void)closedir(d);
                   1545:            (void)Lst_AtEnd(openDirectories, (ClientData)p);
1.17      gwr      1546:            if (path != NULL)
1.46      christos 1547:                (void)Lst_AtEnd(path, (ClientData)p);
1.1       cgd      1548:        }
                   1549:        if (DEBUG(DIR)) {
                   1550:            printf("done\n");
                   1551:        }
                   1552:     }
1.17      gwr      1553:     return p;
1.1       cgd      1554: }
                   1555:
                   1556: /*-
                   1557:  *-----------------------------------------------------------------------
                   1558:  * Dir_CopyDir --
                   1559:  *     Callback function for duplicating a search path via Lst_Duplicate.
                   1560:  *     Ups the reference count for the directory.
                   1561:  *
                   1562:  * Results:
                   1563:  *     Returns the Path it was given.
                   1564:  *
                   1565:  * Side Effects:
                   1566:  *     The refCount of the path is incremented.
                   1567:  *
                   1568:  *-----------------------------------------------------------------------
                   1569:  */
                   1570: ClientData
1.34      wiz      1571: Dir_CopyDir(ClientData p)
1.1       cgd      1572: {
1.6       jtc      1573:     ((Path *) p)->refCount += 1;
1.1       cgd      1574:
                   1575:     return ((ClientData)p);
                   1576: }
                   1577:
                   1578: /*-
                   1579:  *-----------------------------------------------------------------------
                   1580:  * Dir_MakeFlags --
                   1581:  *     Make a string by taking all the directories in the given search
                   1582:  *     path and preceding them by the given flag. Used by the suffix
                   1583:  *     module to create variables for compilers based on suffix search
                   1584:  *     paths.
                   1585:  *
1.34      wiz      1586:  * Input:
                   1587:  *     flag            flag which should precede each directory
                   1588:  *     path            list of directories
                   1589:  *
1.1       cgd      1590:  * Results:
                   1591:  *     The string mentioned above. Note that there is no space between
                   1592:  *     the given flag and each directory. The empty string is returned if
                   1593:  *     Things don't go well.
                   1594:  *
                   1595:  * Side Effects:
                   1596:  *     None
                   1597:  *-----------------------------------------------------------------------
                   1598:  */
                   1599: char *
1.36      christos 1600: Dir_MakeFlags(const char *flag, Lst path)
1.1       cgd      1601: {
                   1602:     char         *str;   /* the string which will be returned */
1.36      christos 1603:     char         *s1, *s2;/* the current directory preceded by 'flag' */
1.1       cgd      1604:     LstNode      ln;     /* the node of the current directory */
                   1605:     Path         *p;     /* the structure describing the current directory */
1.12      christos 1606:
1.45      christos 1607:     str = estrdup("");
1.12      christos 1608:
1.45      christos 1609:     if (Lst_Open(path) == SUCCESS) {
                   1610:        while ((ln = Lst_Next(path)) != NILLNODE) {
1.46      christos 1611:            p = (Path *)Lst_Datum(ln);
1.45      christos 1612:            s2 = str_concat(flag, p->name, 0);
                   1613:            str = str_concat(s1 = str, s2, STR_ADDSPACE);
1.36      christos 1614:            free(s1);
                   1615:            free(s2);
1.1       cgd      1616:        }
1.45      christos 1617:        Lst_Close(path);
1.1       cgd      1618:     }
1.12      christos 1619:
1.1       cgd      1620:     return (str);
                   1621: }
                   1622:
                   1623: /*-
                   1624:  *-----------------------------------------------------------------------
                   1625:  * Dir_Destroy --
                   1626:  *     Nuke a directory descriptor, if possible. Callback procedure
                   1627:  *     for the suffixes module when destroying a search path.
                   1628:  *
1.34      wiz      1629:  * Input:
                   1630:  *     pp              The directory descriptor to nuke
                   1631:  *
1.1       cgd      1632:  * Results:
                   1633:  *     None.
                   1634:  *
                   1635:  * Side Effects:
                   1636:  *     If no other path references this directory (refCount == 0),
                   1637:  *     the Path and all its data are freed.
                   1638:  *
                   1639:  *-----------------------------------------------------------------------
                   1640:  */
                   1641: void
1.34      wiz      1642: Dir_Destroy(ClientData pp)
1.1       cgd      1643: {
1.6       jtc      1644:     Path         *p = (Path *) pp;
1.1       cgd      1645:     p->refCount -= 1;
                   1646:
                   1647:     if (p->refCount == 0) {
                   1648:        LstNode ln;
                   1649:
1.45      christos 1650:        ln = Lst_Member(openDirectories, (ClientData)p);
1.46      christos 1651:        (void)Lst_Remove(openDirectories, ln);
1.1       cgd      1652:
1.45      christos 1653:        Hash_DeleteTable(&p->files);
1.47    ! christos 1654:        free(p->name);
        !          1655:        free(p);
1.1       cgd      1656:     }
                   1657: }
                   1658:
                   1659: /*-
                   1660:  *-----------------------------------------------------------------------
                   1661:  * Dir_ClearPath --
                   1662:  *     Clear out all elements of the given search path. This is different
                   1663:  *     from destroying the list, notice.
                   1664:  *
1.34      wiz      1665:  * Input:
                   1666:  *     path            Path to clear
                   1667:  *
1.1       cgd      1668:  * Results:
                   1669:  *     None.
                   1670:  *
                   1671:  * Side Effects:
                   1672:  *     The path is set to the empty list.
                   1673:  *
                   1674:  *-----------------------------------------------------------------------
                   1675:  */
                   1676: void
1.34      wiz      1677: Dir_ClearPath(Lst path)
1.1       cgd      1678: {
                   1679:     Path    *p;
                   1680:     while (!Lst_IsEmpty(path)) {
                   1681:        p = (Path *)Lst_DeQueue(path);
1.6       jtc      1682:        Dir_Destroy((ClientData) p);
1.1       cgd      1683:     }
                   1684: }
1.12      christos 1685:
1.1       cgd      1686:
                   1687: /*-
                   1688:  *-----------------------------------------------------------------------
                   1689:  * Dir_Concat --
                   1690:  *     Concatenate two paths, adding the second to the end of the first.
                   1691:  *     Makes sure to avoid duplicates.
                   1692:  *
1.34      wiz      1693:  * Input:
                   1694:  *     path1           Dest
                   1695:  *     path2           Source
                   1696:  *
1.1       cgd      1697:  * Results:
                   1698:  *     None
                   1699:  *
                   1700:  * Side Effects:
                   1701:  *     Reference counts for added dirs are upped.
                   1702:  *
                   1703:  *-----------------------------------------------------------------------
                   1704:  */
                   1705: void
1.34      wiz      1706: Dir_Concat(Lst path1, Lst path2)
1.1       cgd      1707: {
                   1708:     LstNode ln;
                   1709:     Path    *p;
                   1710:
                   1711:     for (ln = Lst_First(path2); ln != NILLNODE; ln = Lst_Succ(ln)) {
                   1712:        p = (Path *)Lst_Datum(ln);
                   1713:        if (Lst_Member(path1, (ClientData)p) == NILLNODE) {
                   1714:            p->refCount += 1;
                   1715:            (void)Lst_AtEnd(path1, (ClientData)p);
                   1716:        }
                   1717:     }
                   1718: }
                   1719:
                   1720: /********** DEBUG INFO **********/
1.5       cgd      1721: void
1.34      wiz      1722: Dir_PrintDirectories(void)
1.1       cgd      1723: {
                   1724:     LstNode    ln;
                   1725:     Path       *p;
1.12      christos 1726:
1.46      christos 1727:     printf("#*** Directory Cache:\n");
                   1728:     printf("# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n",
1.1       cgd      1729:              hits, misses, nearmisses, bigmisses,
                   1730:              (hits+bigmisses+nearmisses ?
                   1731:               hits * 100 / (hits + bigmisses + nearmisses) : 0));
1.46      christos 1732:     printf("# %-20s referenced\thits\n", "directory");
1.45      christos 1733:     if (Lst_Open(openDirectories) == SUCCESS) {
                   1734:        while ((ln = Lst_Next(openDirectories)) != NILLNODE) {
1.46      christos 1735:            p = (Path *)Lst_Datum(ln);
                   1736:            printf("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
1.1       cgd      1737:        }
1.45      christos 1738:        Lst_Close(openDirectories);
1.1       cgd      1739:     }
                   1740: }
                   1741:
1.34      wiz      1742: static int
                   1743: DirPrintDir(ClientData p, ClientData dummy)
1.12      christos 1744: {
1.46      christos 1745:     printf("%s ", ((Path *) p)->name);
1.6       jtc      1746:     return (dummy ? 0 : 0);
                   1747: }
1.1       cgd      1748:
1.5       cgd      1749: void
1.34      wiz      1750: Dir_PrintPath(Lst path)
1.1       cgd      1751: {
1.45      christos 1752:     Lst_ForEach(path, DirPrintDir, (ClientData)0);
1.1       cgd      1753: }

CVSweb <webmaster@jp.NetBSD.org>