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

Diff for /src/usr.bin/make/dir.c between version 1.31 and 1.32

version 1.31, 2001/05/03 13:41:03 version 1.32, 2001/05/23 12:34:42
Line 66 
Line 66 
  * SUCH DAMAGE.   * SUCH DAMAGE.
  */   */
   
 /*-  
  * dir.c --  
  *      Directory searching using wildcards and/or normal names...  
  *      Used both for source wildcarding in the Makefile and for finding  
  *      implicit sources.  
  *  
  * The interface for this module is:  
  *      Dir_Init            Initialize the module.  
  *  
  *      Dir_End             Cleanup the module.  
  *  
  *      Dir_HasWildcards    Returns TRUE if the name given it needs to  
  *                          be wildcard-expanded.  
  *  
  *      Dir_Expand          Given a pattern and a path, return a Lst of names  
  *                          which match the pattern on the search path.  
  *  
  *      Dir_FindFile        Searches for a file on a given search path.  
  *                          If it exists, the entire path is returned.  
  *                          Otherwise NULL is returned.  
  *  
  *      Dir_MTime           Return the modification time of a node. The file  
  *                          is searched for along the default search path.  
  *                          The path and mtime fields of the node are filled  
  *                          in.  
  *  
  *      Dir_AddDir          Add a directory to a search path.  
  *  
  *      Dir_MakeFlags       Given a search path and a command flag, create  
  *                          a string with each of the directories in the path  
  *                          preceded by the command flag and all of them  
  *                          separated by a space.  
  *  
  *      Dir_Destroy         Destroy an element of a search path. Frees up all  
  *                          things that can be freed for the element as long  
  *                          as the element is no longer referenced by any other  
  *                          search path.  
  *  
  * For debugging:  
  *      Dir_PrintDirectories    Print stats about the directory cache.  
  */  
   
 #include <stddef.h>  
 #include <stdio.h>  
 #include <sys/types.h>  #include <sys/types.h>
 #include <dirent.h>  
 #include <sys/stat.h>  #include <sys/stat.h>
 #include "make.h"  #include <dirent.h>
   #include <stddef.h>
   #include <stdio.h>
   #include <string.h>
   #include "config.h"
   #include "defines.h"
 #include "ohash.h"  #include "ohash.h"
 #include "dir.h"  #include "dir.h"
   #include "lst.h"
   #include "memory.h"
   #include "buf.h"
   #include "gnode.h"
   #include "arch.h"
   #include "targ.h"
   #include "error.h"
   #include "str.h"
   #include "timestamp.h"
   
 #ifndef lint  
 #if 0  typedef struct Path_ {
 static char sccsid[] = "@(#)dir.c       8.2 (Berkeley) 1/2/94";      int           refCount;     /* Number of paths with this directory */
 #else  #ifdef DEBUG_DIRECTORY_CACHE
 UNUSED      int           hits;         /* the number of times a file in this
 static char rcsid[] = "$OpenBSD$";                                   * directory has been found */
 #endif  #endif
 #endif /* not lint */      struct ohash   files;       /* Hash table of files in directory */
       char          name[1];      /* Name of directory */
   } Path;
   
 /*      A search path consists of a Lst of Path structures. A Path structure  /*      A search path consists of a Lst of Path structures. A Path structure
  *      has in it the name of the directory and a hash table of all the files   *      has in it the name of the directory and a hash table of all the files
Line 193 
Line 164 
  *      sense to replace the access() with a stat() and record the mtime   *      sense to replace the access() with a stat() and record the mtime
  *      in a cache for when Dir_MTime was actually called.  */   *      in a cache for when Dir_MTime was actually called.  */
   
 LIST          dirSearchPath;            /* main search path */  static LIST   thedirSearchPath;         /* main search path */
   Lst           dirSearchPath= &thedirSearchPath;
   
   #ifdef DEBUG_DIRECTORY_CACHE
 /* Variables for gathering statistics on the efficiency of the hashing  /* Variables for gathering statistics on the efficiency of the hashing
  * mechanism.  */   * mechanism.  */
 static int    hits,                     /* Found in directory cache */  static int    hits,                     /* Found in directory cache */
               misses,                   /* Sad, but not evil misses */                misses,                   /* Sad, but not evil misses */
               nearmisses,               /* Found under search path */                nearmisses,               /* Found under search path */
               bigmisses;                /* Sought by itself */                bigmisses;                /* Sought by itself */
   #endif
   
 static Path       *dot;                 /* contents of current directory */  static Path       *dot;                 /* contents of current directory */
   
Line 211 
Line 185 
   
 static struct ohash   openDirectories;  /* cache all open directories */  static struct ohash   openDirectories;  /* cache all open directories */
   
 static struct ohash mtimes;  /* Results of doing a last-resort stat in  /* Global structure used to cache mtimes.  XXX We don't cache an mtime
                              * Dir_FindFile -- if we have to go to the   * before a caller actually looks up for the given time, because of the
                              * system to find the file, we might as well   * possibility a caller might update the file and invalidate the cache
                              * have its mtime on record. XXX: If this is done   * entry, and we don't look up in this cache except as a last resort.
                              * way early, there's a chance other rules will   */
                              * have already updated the file, in which case  static struct ohash mtimes;
                              * we'll update it again. Generally, there won't  
                              * be two rules to update a single file, so this  
                              * should be ok, but... */  
   
   
 /* There are three distinct hash structures:  /* There are three distinct hash structures:
Line 235 
Line 206 
 static struct ohash_info dir_info = { offsetof(Path, name),  static struct ohash_info dir_info = { offsetof(Path, name),
     NULL, hash_alloc, hash_free, element_alloc };      NULL, hash_alloc, hash_free, element_alloc };
   
 static void record_stamp(const char *, TIMESTAMP);  /* add_file(path, name): add a file name to a path hash structure. */
 static void add_file(Path *, const char *);  static void add_file(Path *, const char *);
 static char *find_file_hash(Path *, const char *, const char *, u_int32_t);  /* n = find_file_hashi(p, name, end, hv): retrieve name in a path hash
    *      structure. */
   static char *find_file_hashi(Path *, const char *, const char *, u_int32_t);
   
   /* stamp = find_stampi(name, end): look for (name, end) in the global
    *      cache. */
 static struct file_stamp *find_stampi(const char *, const char *);  static struct file_stamp *find_stampi(const char *, const char *);
   /* record_stamp(name, timestamp): record timestamp for name in the global
    *      cache. */
   static void record_stamp(const char *, TIMESTAMP);
   
   /* free_hash(o): free a ohash structure, where each element can be free'd. */
 static void free_hash(struct ohash *);  static void free_hash(struct ohash *);
   
   /* p = DirReaddiri(name, end): read an actual directory, caching results
    *      as we go.  */
   static Path *DirReaddiri(const char *, const char *);
   /* Handles wildcard expansion on a given directory. */
   static void DirMatchFilesi(const char *, const char *, Path *, Lst);
   /* Handles simple wildcard expansion on a path. */
   static void PathMatchFilesi(const char *, const char *, Lst, Lst);
   /* Handles wildcards expansion except for curly braces. */
   static void DirExpandWildi(const char *, const char *, Lst, Lst);
   #define DirExpandWild(s, l1, l2) DirExpandWildi(s, strchr(s, '\0'), l1, l2)
   /* Handles wildcard expansion including curly braces. */
   static void DirExpandCurlyi(const char *, const char *, Lst, Lst);
   
   /* Debugging: show each word in an expansion list. */
 static Path *DirReaddir(const char *, const char *);  
 static void DirMatchFiles(const char *, Path *, Lst);  
 static void PathMatchFiles(const char *, Lst, Lst);  
 static void DirPrintWord(void *);  static void DirPrintWord(void *);
   /* Debugging: show a dir name in a path. */
 static void DirPrintDir(void *);  static void DirPrintDir(void *);
 static void DirExpandWild(const char *, Lst, Lst);  
 static void DirExpandCurly(const char *, Lst, Lst);  
   
 static void  static void
 record_stamp(file, t)  record_stamp(file, t)
Line 298 
Line 287 
 }  }
   
 static char *  static char *
 find_file_hash(p, file, e, hv)  find_file_hashi(p, file, e, hv)
     Path                *p;      Path                *p;
     const char          *file;      const char          *file;
     const char          *e;      const char          *e;
Line 321 
Line 310 
     ohash_delete(h);      ohash_delete(h);
 }  }
   
 /*-  
  *-----------------------------------------------------------------------  /* Side Effects: cache the current directory */
  * Dir_Init --  
  *      initialize things for this module  
  *  
  * Side Effects:  
  *      cache the current directory  
  *-----------------------------------------------------------------------  
  */  
 void  void
 Dir_Init()  Dir_Init()
 {  {
     Lst_Init(&dirSearchPath);      char *dotname = ".";
   
       Lst_Init(dirSearchPath);
     ohash_init(&openDirectories, 4, &dir_info);      ohash_init(&openDirectories, 4, &dir_info);
     ohash_init(&mtimes, 4, &stamp_info);      ohash_init(&mtimes, 4, &stamp_info);
   
     dot = DirReaddir(".", NULL);  
   
       dot = DirReaddiri(dotname, dotname+1);
   
     if (!dot)      if (!dot)
         Error("Can't access current directory");          Error("Can't access current directory");
   
Line 347 
Line 332 
     dot->refCount++;      dot->refCount++;
 }  }
   
 /*-  #ifdef CLEANUP
  *-----------------------------------------------------------------------  
  * Dir_End --  
  *      cleanup things for this module  
  *-----------------------------------------------------------------------  
  */  
 void  void
 Dir_End()  Dir_End()
 {  {
 #ifdef CLEANUP  
     struct Path *p;      struct Path *p;
     unsigned int i;      unsigned int i;
   
     dot->refCount--;      dot->refCount--;
     Dir_Destroy(dot);      Dir_Destroy(dot);
     Lst_Destroy(&dirSearchPath, Dir_Destroy);      Lst_Destroy(dirSearchPath, Dir_Destroy);
     for (p = ohash_first(&openDirectories, &i); p != NULL;      for (p = ohash_first(&openDirectories, &i); p != NULL;
         p = ohash_next(&openDirectories, &i))          p = ohash_next(&openDirectories, &i))
             Dir_Destroy(p);              Dir_Destroy(p);
     ohash_delete(&openDirectories);      ohash_delete(&openDirectories);
     free_hash(&mtimes);      free_hash(&mtimes);
 #endif  
 }  }
   #endif
   
 /*-  
  *-----------------------------------------------------------------------  /* XXX: This code is not 100% correct ([^]] fails) */
  * Dir_HasWildcards  --  bool
  *      see if the given name has any wildcard characters in it  Dir_HasWildcardsi(name, end)
  *      be careful not to expand unmatching brackets or braces.      const char          *name;
  *      XXX: This code is not 100% correct. ([^]] fails etc.)      const char          *end;
  *-----------------------------------------------------------------------  
  */  
 Boolean  
 Dir_HasWildcards(name)  
     const char          *name;  /* name to check */  
 {  {
     const char          *cp;      const char          *cp;
     Boolean             wild = FALSE;      bool                wild = false;
     unsigned long       brace = 0, bracket = 0;      unsigned long       brace = 0, bracket = 0;
   
     for (cp = name; *cp != '\0'; cp++) {      for (cp = name; cp != end; cp++) {
         switch (*cp) {          switch (*cp) {
         case '{':          case '{':
             brace++;              brace++;
             wild = TRUE;              wild = true;
             break;              break;
         case '}':          case '}':
             if (brace == 0)              if (brace == 0)
                 return FALSE;                  return false;
             brace--;              brace--;
             break;              break;
         case '[':          case '[':
             bracket++;              bracket++;
             wild = TRUE;              wild = true;
             break;              break;
         case ']':          case ']':
             if (bracket == 0)              if (bracket == 0)
                 return FALSE;                  return false;
             bracket--;              bracket--;
             break;              break;
         case '?':          case '?':
         case '*':          case '*':
             wild = TRUE;              wild = true;
             break;              break;
         default:          default:
             break;              break;
Line 420 
Line 394 
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * DirMatchFiles --   * DirMatchFilesi --
  *      Given a pattern and a Path structure, see if any files   *      Given a pattern and a Path structure, see if any files
  *      match the pattern and add their names to the 'expansions' list if   *      match the pattern and add their names to the 'expansions' list if
  *      any do. This is incomplete -- it doesn't take care of patterns like   *      any do. This is incomplete -- it doesn't take care of patterns like
  *      src / *src / *.c properly (just *.c on any of the directories), but it   *      src / *src / *.c properly (just *.c on any of the directories), but it
  *      will do for now.   *      will do for now.
  *  
  * Side Effects:  
  *      The directory will be fully hashed when this is done.  
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 DirMatchFiles(pattern, p, expansions)  DirMatchFilesi(pattern, end, p, expansions)
     const char          *pattern;       /* Pattern to look for */      const char          *pattern;       /* Pattern to look for */
       const char          *end;           /* End of pattern */
     Path                *p;             /* Directory to search */      Path                *p;             /* Directory to search */
     Lst                 expansions;     /* Place to store the results */      Lst                 expansions;     /* Place to store the results */
 {  {
     unsigned int        search;         /* Index into the directory's table */      unsigned int        search;         /* Index into the directory's table */
     const char          *entry;         /* Current entry in the table */      const char          *entry;         /* Current entry in the table */
     Boolean             isDot;          /* Is the directory "." ? */      bool                isDot;          /* Is the directory "." ? */
   
     isDot = p->name[0] == '.' && p->name[1] == '\0';      isDot = p->name[0] == '.' && p->name[1] == '\0';
   
Line 451 
Line 423 
          * so they won't match `.*'.  */           * so they won't match `.*'.  */
         if (*pattern != '.' && *entry == '.')          if (*pattern != '.' && *entry == '.')
             continue;              continue;
         if (Str_Match(entry, pattern))          if (Str_Matchi(entry, strchr(entry, '\0'), pattern, end))
             Lst_AtEnd(expansions,              Lst_AtEnd(expansions,
                 isDot ? estrdup(entry) : str_concat(p->name, entry, '/'));                  isDot ? estrdup(entry) : Str_concat(p->name, entry, '/'));
     }      }
 }  }
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * PathMatchFiles --   * PathMatchFilesi --
  *      Traverse directories in the path, calling DirMatchFiles for each.   *      Traverse directories in the path, calling DirMatchFiles for each.
  *      NOTE: This doesn't handle patterns in directories.   *      NOTE: This doesn't handle patterns in directories.
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 PathMatchFiles(word, path, expansions)  PathMatchFilesi(word, end, path, expansions)
     const char  *word;          /* Word to expand */      const char  *word;          /* Word to expand */
       const char  *end;           /* End of word */
     Lst         path;           /* Path on which to look */      Lst         path;           /* Path on which to look */
     Lst         expansions;     /* Place to store the result */      Lst         expansions;     /* Place to store the result */
 {  {
     LstNode     ln;             /* Current node */      LstNode     ln;             /* Current node */
   
     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln))      for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln))
         DirMatchFiles(word, (Path *)Lst_Datum(ln), expansions);          DirMatchFilesi(word, end, (Path *)Lst_Datum(ln), expansions);
 }  }
   
 /*-  
  *-----------------------------------------------------------------------  
  * DirPrintWord --  
  *      Print a word in the list of expansions. Callback for Dir_Expand  
  *      when DEBUG(DIR), via Lst_ForEach.  
  *-----------------------------------------------------------------------  
  */  
 static void  static void
 DirPrintWord(word)  DirPrintWord(word)
     void        *word;      void        *word;
Line 492 
Line 458 
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * DirExpandWild:   * DirExpandWildi:
  *      Expand all wild cards in a fully qualified name, except for   *      Expand all wild cards in a fully qualified name, except for
  *      curly braces.   *      curly braces.
    * Side-effect:
    *      Will hash any directory in which a file is found, and add it to
    *      the path, on the assumption that future lookups will find files
    *      there as well.
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 DirExpandWild(word, path, expansions)  DirExpandWildi(word, end, path, expansions)
     const char  *word;          /* the word to expand */      const char  *word;          /* the word to expand */
       const char  *end;           /* end of word */
     Lst         path;           /* the list of directories in which to find      Lst         path;           /* the list of directories in which to find
                                  * the resulting files */                                   * the resulting files */
     Lst         expansions;     /* the list on which to place the results */      Lst         expansions;     /* the list on which to place the results */
Line 507 
Line 478 
     const char  *cp;      const char  *cp;
     const char  *slash;         /* keep track of first slash before wildcard */      const char  *slash;         /* keep track of first slash before wildcard */
   
     slash = strchr(word, '/');      slash = memchr(word, '/', end - word);
     if (slash == NULL) {      if (slash == NULL) {
         /* First the files in dot.  */          /* First the files in dot.  */
         DirMatchFiles(word, dot, expansions);          DirMatchFilesi(word, end, dot, expansions);
   
         /* Then the files in every other directory on the path.  */          /* Then the files in every other directory on the path.  */
         PathMatchFiles(word, path, expansions);          PathMatchFilesi(word, end, path, expansions);
         return;          return;
     }      }
     /* The thing has a directory component -- find the first wildcard      /* The thing has a directory component -- find the first wildcard
      * in the string.  */       * in the string.  */
     slash = word;      slash = word;
     for (cp = word; *cp; cp++) {      for (cp = word; cp != end; cp++) {
         if (*cp == '/')          if (*cp == '/')
             slash = cp;              slash = cp;
         if (*cp == '?' || *cp == '[' || *cp == '*') {          if (*cp == '?' || *cp == '[' || *cp == '*') {
Line 535 
Line 506 
                  * i.e. if the path contains ../Etc/Object and we're                   * i.e. if the path contains ../Etc/Object and we're
                  * looking for Etc, it won't be found. */                   * looking for Etc, it won't be found. */
                 if (dirpath != NULL) {                  if (dirpath != NULL) {
                     char *dp = &dirpath[strlen(dirpath) - 1];                      char *dp;
                     LIST temp;                      LIST temp;
   
                     if (*dp == '/')                      dp = strchr(dirpath, '\0');
                         *dp = '\0';                      while (dp > dirpath && dp[-1] == '/')
                           dp--;
   
                     Lst_Init(&temp);                      Lst_Init(&temp);
                     Dir_AddDir(&temp, dirpath, NULL);                      Dir_AddDiri(&temp, dirpath, dp);
                     PathMatchFiles(slash+1, &temp, expansions);                      PathMatchFilesi(slash+1, end, &temp, expansions);
                     Lst_Destroy(&temp, NOFREE);                      Lst_Destroy(&temp, NOFREE);
                 }                  }
             } else              } else
                 /* Start the search from the local directory.  */                  /* Start the search from the local directory.  */
                 PathMatchFiles(word, path, expansions);                  PathMatchFilesi(word, end, path, expansions);
             return;              return;
         }          }
     }      }
     /* Return the file -- this should never happen.  */      /* Return the file -- this should never happen.  */
     PathMatchFiles(word, path, expansions);      PathMatchFilesi(word, end, path, expansions);
 }  }
   
 /*-  /*-
Line 560 
Line 533 
  * DirExpandCurly --   * DirExpandCurly --
  *      Expand curly braces like the C shell, and other wildcards as per   *      Expand curly braces like the C shell, and other wildcards as per
  *      Str_Match.   *      Str_Match.
  *      Note the special behavior: if curly expansion yields a result with   *      XXX: if curly expansion yields a result with
  *      no wildcards, the result is placed on the list WITHOUT CHECKING   *      no wildcards, the result is placed on the list WITHOUT CHECKING
  *      FOR ITS EXISTENCE.   *      FOR ITS EXISTENCE.
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  */   */
 static void  static void
 DirExpandCurly(word, path, expansions)  DirExpandCurlyi(word, endw, path, expansions)
     const char  *word;          /* Entire word to expand */      const char  *word;          /* Entire word to expand */
       const char  *endw;          /* End of word */
     Lst         path;           /* Search path to use */      Lst         path;           /* Search path to use */
     Lst         expansions;     /* Place to store the expansions */      Lst         expansions;     /* Place to store the expansions */
 {  {
Line 575 
Line 549 
                                  * expansion before calling Dir_Expand */                                   * expansion before calling Dir_Expand */
     LIST        curled;         /* Queue of words to expand */      LIST        curled;         /* Queue of words to expand */
     char        *toexpand;      /* Current word to expand */      char        *toexpand;      /* Current word to expand */
     Boolean     dowild;         /* Wildcard left after curlies ? */      bool        dowild;         /* Wildcard left after curlies ? */
   
     /* Determine once and for all if there is something else going on */      /* Determine once and for all if there is something else going on */
     dowild = FALSE;      dowild = false;
     for (cp2 = word; *cp2 != '\0'; cp2++)      for (cp2 = word; cp2 != endw; cp2++)
         if (*cp2 == '*' || *cp2 == '?' || *cp2 == '[') {          if (*cp2 == '*' || *cp2 == '?' || *cp2 == '[') {
                 dowild = TRUE;                  dowild = true;
                 break;                  break;
         }          }
   
     /* Prime queue with copy of initial word */      /* Prime queue with copy of initial word */
     Lst_Init(&curled);      Lst_Init(&curled);
     Lst_EnQueue(&curled, estrdup(word));      Lst_EnQueue(&curled, Str_dupi(word, endw));
     while ((toexpand = (char *)Lst_DeQueue(&curled)) != NULL) {      while ((toexpand = (char *)Lst_DeQueue(&curled)) != NULL) {
         const char      *brace;          const char      *brace;
         const char      *start; /* Start of current chunk of brace clause */          const char      *start; /* Start of current chunk of brace clause */
Line 651 
Line 625 
     }      }
 }  }
   
 /*-  /* Side effects:
  *-----------------------------------------------------------------------   *      Dir_Expandi will hash directories that were not yet visited */
  * Dir_Expand  --  
  *      Expand the given word into a list of words by globbing it looking  
  *      in the directories on the given search path.  
  *  
  * Results:  
  *      A list of words consisting of the files which exist along the search  
  *      path matching the given pattern.  
  *  
  * Side Effects:  
  *      Directories may be opened.  
  *-----------------------------------------------------------------------  
  */  
 void  void
 Dir_Expand(word, path, expansions)  Dir_Expandi(word, end, path, expansions)
     const char  *word;          /* the word to expand */      const char  *word;          /* the word to expand */
       const char  *end;           /* end of word */
     Lst         path;           /* the list of directories in which to find      Lst         path;           /* the list of directories in which to find
                                  * the resulting files */                                   * the resulting files */
     Lst         expansions;     /* the list on which to place the results */      Lst         expansions;     /* the list on which to place the results */
 {  {
     const char  *cp;      const char  *cp;
   
     if (DEBUG(DIR))      if (DEBUG(DIR)) {
         printf("expanding \"%s\"...", word);          char *s = Str_dupi(word, end);
           printf("expanding \"%s\"...", s);
           free(s);
       }
   
     cp = strchr(word, '{');      cp = memchr(word, '{', end - word);
     if (cp)      if (cp)
         DirExpandCurly(word, path, expansions);          DirExpandCurlyi(word, end, path, expansions);
     else      else
         DirExpandWild(word, path, expansions);          DirExpandWildi(word, end, path, expansions);
   
     if (DEBUG(DIR)) {      if (DEBUG(DIR)) {
         Lst_Every(expansions, DirPrintWord);          Lst_Every(expansions, DirPrintWord);
Line 689 
Line 655 
     }      }
 }  }
   
   
 /*-  /*-
  *-----------------------------------------------------------------------  
  * Dir_FindFilei  --  
  *      Find the file with the given name along the given search path.  
  *  
  * Results:  
  *      The path to the file or NULL. This path is guaranteed to be in a  
  *      different part of memory than name and so may be safely free'd.  
  *  
  * Side Effects:   * Side Effects:
  *      If the file is found in a directory which is not on the path   *      If the file is found in a directory which is not on the path
  *      already (either 'name' is absolute or it is a relative path   *      already (either 'name' is absolute or it is a relative path
  *      [ dir1/.../dirn/file ] which exists below one of the directories   *      [ dir1/.../dirn/file ] which exists below one of the directories
  *      already on the search path), its directory is added to the end   *      already on the search path), its directory is added to the end
  *      of the path on the assumption that there will be more files in   *      of the path on the assumption that there will be more files in
  *      that directory later on. Sometimes this is true. Sometimes not.   *      that directory later on.
  *-----------------------------------------------------------------------  
  */   */
 char *  char *
 Dir_FindFilei(name, end, path)  Dir_FindFilei(name, end, path)
     const char          *name;  /* the file to find */      const char          *name;
     const char          *end;   /* end of file name */      const char          *end;
     Lst                 path;   /* List of directories to search */      Lst                 path;
 {  {
       Path                *p;     /* current path member */
     char                *p1;    /* pointer into p->name */      char                *p1;    /* pointer into p->name */
     const char          *p2;    /* pointer into name */      const char          *p2;    /* pointer into name */
     LstNode             ln;     /* a list element */      LstNode             ln;     /* a list element */
     char                *file;  /* the current filename to check */      char                *file;  /* the current filename to check */
     char                *temp;  /* index into file */      char                *temp;  /* index into file */
     Path                *p;     /* current path member */  
     const char          *cp;    /* index of first slash, if any */      const char          *cp;    /* index of first slash, if any */
     Boolean             hasSlash;      bool                hasSlash;
     struct stat         stb;    /* Buffer for stat, if necessary */      struct stat         stb;    /* Buffer for stat, if necessary */
     struct file_stamp   *entry; /* Entry for mtimes table */      struct file_stamp   *entry; /* Entry for mtimes table */
     u_int32_t           hv;      u_int32_t           hv;     /* hash value for last component in file name */
     char                *q;     /* Copy of name, end */      char                *q;     /* Str_dupi(name, end) */
   
     /* Find the final component of the name and note whether it has a      /* Find the final component of the name and note whether name has a
      * slash in it (the name, I mean).  */       * slash in it */
     cp = lastchar(name, end, '/');      cp = Str_rchri(name, end, '/');
     if (cp) {      if (cp) {
         hasSlash = TRUE;          hasSlash = true;
         cp++;          cp++;
     } else {      } else {
         hasSlash = FALSE;          hasSlash = false;
         cp = name;          cp = name;
     }      }
   
Line 742 
Line 700 
     if (DEBUG(DIR))      if (DEBUG(DIR))
         printf("Searching for %s...", name);          printf("Searching for %s...", name);
     /* No matter what, we always look for the file in the current directory      /* No matter what, we always look for the file in the current directory
      * before anywhere else and we *do not* add the ./ to it if it exists.       * before anywhere else and we always return exactly what the caller
      * This is so there are no conflicts between what the user specifies       * specified. */
      * (fish.c) and what pmake finds (./fish.c).  */  
     if ((!hasSlash || (cp - name == 2 && *name == '.')) &&      if ((!hasSlash || (cp - name == 2 && *name == '.')) &&
         find_file_hash(dot, cp, end, hv) != NULL) {          find_file_hashi(dot, cp, end, hv) != NULL) {
             if (DEBUG(DIR))              if (DEBUG(DIR))
                 printf("in '.'\n");                  printf("in '.'\n");
   #ifdef DEBUG_DIRECTORY_CACHE
             hits++;              hits++;
             dot->hits++;              dot->hits++;
             return interval_dup(name, end);  #endif
               return Str_dupi(name, end);
     }      }
   
     /* We look through all the directories on the path seeking one which      /* Then, we look through all the directories on path, seeking one
      * contains the final component of the given name and whose final       * containing the final component of name and whose final
      * component(s) match the name's initial component(s). If such a beast       * component(s) match name's initial component(s).
      * is found, we concatenate the directory name and the final component       * If found, we concatenate the directory name and the
      * and return the resulting string. If we don't find any such thing,       * final component and return the resulting string.  */
      * we go on to phase two...  */  
     for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {      for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
         p = (Path *)Lst_Datum(ln);          p = (Path *)Lst_Datum(ln);
         if (DEBUG(DIR))          if (DEBUG(DIR))
             printf("%s...", p->name);              printf("%s...", p->name);
         if (find_file_hash(p, cp, end, hv) != NULL) {          if (find_file_hashi(p, cp, end, hv) != NULL) {
             if (DEBUG(DIR))              if (DEBUG(DIR))
                 printf("here...");                  printf("here...");
             if (hasSlash) {              if (hasSlash) {
Line 786 
Line 744 
                     continue;                      continue;
                 }                  }
             }              }
             file = str_concati(p->name, cp, end, '/');              file = Str_concati(p->name, strchr(p->name, '\0'), cp, end, '/');
             if (DEBUG(DIR))              if (DEBUG(DIR))
                 printf("returning %s\n", file);                  printf("returning %s\n", file);
   #ifdef DEBUG_DIRECTORY_CACHE
             p->hits++;              p->hits++;
             hits++;              hits++;
   #endif
             return file;              return file;
         } else if (hasSlash) {          } else if (hasSlash) {
             /* If the file has a leading path component and that component              /* If the file has a leading path component and that component
Line 806 
Line 766 
         }          }
     }      }
   
     /* We didn't find the file on any existing members of the directory.      /* We didn't find the file on any existing member of the path.
      * If the name doesn't contain a slash, that means it doesn't exist.       * If the name doesn't contain a slash, end of story.
      * If it *does* contain a slash, however, there is still hope: it       * If it does contain a slash, however, it could be in a subdirectory
      * could be in a subdirectory of one of the members of the search       * of one of the members of the search path. (eg., for path=/usr/include
      * path. (eg. /usr/include and sys/types.h. The above search would       * and name=sys/types.h, the above search fails to turn up types.h
      * fail to turn up types.h in /usr/include, but it *is* in       * in /usr/include, even though /usr/include/sys/types.h exists).
      * /usr/include/sys/types.h) If we find such a beast, we assume there       *
      * will be more (what else can we assume?) and add all but the last       * We only perform this look-up for non-absolute file names.
      * component of the resulting name onto the search path (at the       *
      * end). This phase is only performed if the file is *not* absolute.  */       * Whenever we score a hit, we assume there will be more matches from
        * that directory, and append all but the last component of the
        * resulting name onto the search path. */
     if (!hasSlash) {      if (!hasSlash) {
         if (DEBUG(DIR))          if (DEBUG(DIR))
             printf("failed.\n");              printf("failed.\n");
   #ifdef DEBUG_DIRECTORY_CACHE
         misses++;          misses++;
   #endif
         return NULL;          return NULL;
     }      }
   
     if (*name != '/') {      if (*name != '/') {
         Boolean checkedDot = FALSE;          bool checkedDot = false;
   
         if (DEBUG(DIR))          if (DEBUG(DIR))
             printf("failed. Trying subdirectories...");              printf("failed. Trying subdirectories...");
         for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {          for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) {
             p = (Path *)Lst_Datum(ln);              p = (Path *)Lst_Datum(ln);
             if (p != dot)              if (p != dot)
                 file = str_concati(p->name, name, end, '/');                  file = Str_concati(p->name, strchr(p->name, '\0'), name, end, '/');
             else {              else {
                 /* Checking in dot -- DON'T put a leading ./ on the thing.  */                  /* Checking in dot -- DON'T put a leading ./ on the thing.  */
                 file = interval_dup(name, end);                  file = Str_dupi(name, end);
                 checkedDot = TRUE;                  checkedDot = true;
             }              }
             if (DEBUG(DIR))              if (DEBUG(DIR))
                 printf("checking %s...", file);                  printf("checking %s...", file);
Line 843 
Line 807 
             if (stat(file, &stb) == 0) {              if (stat(file, &stb) == 0) {
                 TIMESTAMP mtime;                  TIMESTAMP mtime;
   
                 grab_stat(stb, mtime);                  ts_set_from_stat(stb, mtime);
                 if (DEBUG(DIR))                  if (DEBUG(DIR))
                     printf("got it.\n");                      printf("got it.\n");
   
                 /* We've found another directory to search. We know there's                  /* We've found another directory to search. We know there
                  * a slash in 'file' because we put one there. We call                   * is a slash in 'file'. We call Dir_AddDiri to add the
                  * Dir_AddDir to add this new directory onto the existing                   * new directory onto the existing search path. Once
                  * search path. Once that's done, we return the file name,                   * that's done, we return the file name, knowing that
                  * knowing that should a file in this directory ever be                   * should a file in this directory ever be referenced again
                  * referenced again in such a manner, we will find it                   * in such a manner, we will find it without having to do
                  * without having to do numerous access calls. Hurrah!  */                   * numerous access calls.  */
                 temp = strrchr(file, '/');                  temp = strrchr(file, '/');
                 Dir_AddDir(path, file, temp);                  Dir_AddDiri(path, file, temp);
   
                 /* Save the modification time so if it's needed, we don't have                  /* Save the modification time so if it's needed, we don't have
                  * to fetch it again.  */                   * to fetch it again.  */
Line 863 
Line 827 
                     printf("Caching %s for %s\n", Targ_FmtTime(mtime),                      printf("Caching %s for %s\n", Targ_FmtTime(mtime),
                             file);                              file);
                 record_stamp(file, mtime);                  record_stamp(file, mtime);
   #ifdef DEBUG_DIRECTORY_CACHE
                 nearmisses++;                  nearmisses++;
   #endif
                 return file;                  return file;
             } else              } else
                 free(file);                  free(file);
Line 881 
Line 847 
         }          }
     }      }
   
     /* Didn't find it that way, either. Sigh. Phase 3. Add its directory      /* Didn't find it that way, either. Last resort: look for the file
      * onto the search path in any case, just in case, then look for the       * in the global mtime cache, then on the disk.
      * thing in the hash table. If we find it, grand. We return a new       * If this doesn't succeed, we finally return a NULL pointer.
      * copy of the name. Otherwise we sadly return a NULL pointer. Sigh.  
      * Note that if the directory holding the file doesn't exist, this will  
      * do an extra search of the final directory on the path. Unless something  
      * weird happens, this search won't succeed and life will be groovy.  
      *       *
      * Sigh. We cannot add the directory onto the search path because       * We cannot add this directory onto the search path because
      * of this amusing case:       * of this amusing case:
      * $(INSTALLDIR)/$(FILE): $(FILE)       * $(INSTALLDIR)/$(FILE): $(FILE)
      *       *
      * $(FILE) exists in $(INSTALLDIR) but not in the current one.       * $(FILE) exists in $(INSTALLDIR) but not in the current one.
      * When searching for $(FILE), we will find it in $(INSTALLDIR)       * When searching for $(FILE), we will find it in $(INSTALLDIR)
      * b/c we added it here. This is not good...  */       * b/c we added it here. This is not good...  */
 #ifdef notdef      q = Str_dupi(name, end);
     Dir_AddDir(path, name, cp-1);  
   
     bigmisses += 1;  
     ln = Lst_Last(path);  
     if (ln == NULL)  
         return NULL;  
     else  
         p = (Path *)Lst_Datum(ln);  
   
     if (find_file_hash(p, cp, e, hv) != NULL)  
         return estrdup(name);  
     else  
         return NULL;  
 #else /* !notdef */  
     q = interval_dup(name, end);  
     if (DEBUG(DIR))      if (DEBUG(DIR))
         printf("Looking for \"%s\"...", q);          printf("Looking for \"%s\"...", q);
   
   #ifdef DEBUG_DIRECTORY_CACHE
     bigmisses++;      bigmisses++;
   #endif
     entry = find_stampi(name, end);      entry = find_stampi(name, end);
     if (entry != NULL) {      if (entry != NULL) {
         if (DEBUG(DIR))          if (DEBUG(DIR))
Line 924 
Line 873 
     } else if (stat(q, &stb) == 0) {      } else if (stat(q, &stb) == 0) {
         TIMESTAMP mtime;          TIMESTAMP mtime;
   
         grab_stat(stb, mtime);          ts_set_from_stat(stb, mtime);
         if (DEBUG(DIR))          if (DEBUG(DIR))
             printf("Caching %s for %s\n", Targ_FmtTime(mtime),              printf("Caching %s for %s\n", Targ_FmtTime(mtime),
                     q);                      q);
Line 936 
Line 885 
         free(q);          free(q);
         return NULL;          return NULL;
     }      }
 #endif /* notdef */  
 }  }
   
 /*-  
  *-----------------------------------------------------------------------  
  * Dir_MTime  --  
  *      Find the modification time of the file described by gn along the  
  *      search path dirSearchPath.  
  *  
  * Results:  
  *      The modification time or OUT_OF_DATE if it doesn't exist  
  *  
  * Side Effects:  
  *      The modification time is placed in the node's mtime slot.  
  *      If the node didn't have a path entry before, and Dir_FindFile  
  *      found one for it, the full name is placed in the path slot.  
  *-----------------------------------------------------------------------  
  */  
 TIMESTAMP  
 Dir_MTime(gn)  
     GNode         *gn;        /* the file whose modification time is  
                                * desired */  
 {  
     char          *fullName;  /* the full pathname of name */  
     struct stat   stb;        /* buffer for finding the mod time */  
     struct file_stamp  
                   *entry;  
     unsigned int  slot;  
     TIMESTAMP     mtime;  
   
     if (gn->type & OP_ARCHV)  
         return Arch_MTime(gn);  
   
     if (gn->path == NULL) {  
         fullName = Dir_FindFile(gn->name, &dirSearchPath);  
         if (fullName == NULL)  
             fullName = estrdup(gn->name);  
     } else  
         fullName = gn->path;  
   
     slot = ohash_qlookup(&mtimes, fullName);  
     entry = ohash_find(&mtimes, slot);  
     if (entry != NULL) {  
         /* Only do this once -- the second time folks are checking to  
          * see if the file was actually updated, so we need to actually go  
          * to the file system.  */  
         if (DEBUG(DIR))  
             printf("Using cached time %s for %s\n",  
                     Targ_FmtTime(entry->mtime), fullName);  
         mtime = entry->mtime;  
         free(entry);  
         ohash_remove(&mtimes, slot);  
     } else if (stat(fullName, &stb) == 0)  
         grab_stat(stb, mtime);  
     else {  
         if (gn->type & OP_MEMBER) {  
             if (fullName != gn->path)  
                 free(fullName);  
             return Arch_MemMTime(gn);  
         } else  
             set_out_of_date(mtime);  
     }  
     if (fullName && gn->path == NULL)  
         gn->path = fullName;  
   
     gn->mtime = mtime;  
     return gn->mtime;  
 }  
   
 /* Read a directory, either from the disk, or from the cache.  */  /* Read a directory, either from the disk, or from the cache.  */
 static Path *  static Path *
 DirReaddir(name, end)  DirReaddiri(name, end)
     const char          *name;      const char          *name;
     const char          *end;      const char          *end;
 {  {
Line 1023 
Line 905 
         return p;          return p;
   
     p = ohash_create_entry(&dir_info, name, &end);      p = ohash_create_entry(&dir_info, name, &end);
   #ifdef DEBUG_DIRECTORY_CACHE
     p->hits = 0;      p->hits = 0;
   #endif
     p->refCount = 0;      p->refCount = 0;
     ohash_init(&p->files, 4, &file_info);      ohash_init(&p->files, 4, &file_info);
   
Line 1058 
Line 942 
   
 /*-  /*-
  *-----------------------------------------------------------------------   *-----------------------------------------------------------------------
  * Dir_AddDir --   * Dir_AddDiri --
  *      Add the given name to the end of the given path. The order of   *      Add the given name to the end of the given path. The order of
  *      the arguments is backwards so ParseDoDependency can do a   *      the arguments is backwards so ParseDoDependency can do a
  *      Lst_ForEach of its list of paths...   *      Lst_ForEach of its list of paths...
Line 1070 
Line 954 
  */   */
   
 void  void
 Dir_AddDir(path, name, end)  Dir_AddDiri(path, name, end)
     Lst         path;   /* the path to which the directory should be added */      Lst         path;   /* the path to which the directory should be added */
     const char  *name;  /* the name of the directory to add */      const char  *name;  /* the name of the directory to add */
     const char  *end;      const char  *end;
 {  {
     Path        *p;     /* pointer to new Path structure */      Path        *p;     /* pointer to new Path structure */
   
     p = DirReaddir(name, end);      p = DirReaddiri(name, end);
     if (p == NULL)      if (p == NULL)
         return;          return;
     if (p->refCount == 0)      if (p->refCount == 0)
         Lst_AtEnd(path, p);          Lst_AtEnd(path, p);
     else if (Lst_AddNew(path, p) == FAILURE)      else if (!Lst_AddNew(path, p))
         return;          return;
     p->refCount++;      p->refCount++;
 }  }
Line 1185 
Line 1069 
   
     for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) {      for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) {
         p = (Path *)Lst_Datum(ln);          p = (Path *)Lst_Datum(ln);
         if (Lst_AddNew(path1, p) == SUCCESS)          if (Lst_AddNew(path1, p))
             p->refCount++;              p->refCount++;
     }      }
 }  }
   
 /********** DEBUG INFO **********/  #ifdef DEBUG_DIRECTORY_CACHE
 void  void
 Dir_PrintDirectories()  Dir_PrintDirectories()
 {  {
Line 1207 
Line 1091 
         p = ohash_next(&openDirectories, &i))          p = ohash_next(&openDirectories, &i))
             printf("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);              printf("# %-20s %10d\t%4d\n", p->name, p->refCount, p->hits);
 }  }
   #endif
   
 static void  static void
 DirPrintDir(p)  DirPrintDir(p)
Line 1222 
Line 1107 
     Lst_Every(path, DirPrintDir);      Lst_Every(path, DirPrintDir);
 }  }
   
 #ifndef USE_TIMESPEC  TIMESTAMP
 #include <sys/types.h>  Dir_MTime(gn)
 #include <utime.h>      GNode         *gn;        /* the file whose modification time is
 #endif                                 * desired */
 int  
 set_times(f)  
     const char *f;  
 {  {
 #ifdef USE_TIMESPEC      char          *fullName;  /* the full pathname of name */
     struct timeval tv[2];      struct stat   stb;        /* buffer for finding the mod time */
       struct file_stamp
                     *entry;
       unsigned int  slot;
       TIMESTAMP     mtime;
   
     TIMESPEC_TO_TIMEVAL(&tv[0], &now);      if (gn->type & OP_ARCHV)
     TIMESPEC_TO_TIMEVAL(&tv[1], &now);          return Arch_MTime(gn);
     return utimes(f, tv);  
 #else  
     struct utimbuf times;  
   
     times.actime = times.modtime = now;      if (gn->path == NULL) {
     return utime(f, &times);          fullName = Dir_FindFile(gn->name, dirSearchPath);
 #endif          if (fullName == NULL)
               fullName = estrdup(gn->name);
       } else
           fullName = gn->path;
   
       slot = ohash_qlookup(&mtimes, fullName);
       entry = ohash_find(&mtimes, slot);
       if (entry != NULL) {
           /* Only do this once -- the second time folks are checking to
            * see if the file was actually updated, so we need to actually go
            * to the file system.  */
           if (DEBUG(DIR))
               printf("Using cached time %s for %s\n",
                       Targ_FmtTime(entry->mtime), fullName);
           mtime = entry->mtime;
           free(entry);
           ohash_remove(&mtimes, slot);
       } else if (stat(fullName, &stb) == 0)
           ts_set_from_stat(stb, mtime);
       else {
           if (gn->type & OP_MEMBER) {
               if (fullName != gn->path)
                   free(fullName);
               return Arch_MemMTime(gn);
           } else
               ts_set_out_of_date(mtime);
       }
       if (fullName && gn->path == NULL)
           gn->path = fullName;
   
       gn->mtime = mtime;
       return gn->mtime;
 }  }
   

Legend:
Removed from v.1.31  
changed lines
  Added in v.1.32