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

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

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