home *** CD-ROM | disk | FTP | other *** search
/ OpenStep (Enterprise) / OpenStepENTCD.toast / OEDEV / GNUSRC.Z / dir.c < prev    next >
Encoding:
C/C++ Source or Header  |  1996-03-01  |  17.4 KB  |  671 lines

  1. /* Directory hashing for GNU Make.
  2. Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
  3. This file is part of GNU Make.
  4.  
  5. GNU Make is free software; you can redistribute it and/or modify
  6. it under the terms of the GNU General Public License as published by
  7. the Free Software Foundation; either version 2, or (at your option)
  8. any later version.
  9.  
  10. GNU Make is distributed in the hope that it will be useful,
  11. but WITHOUT ANY WARRANTY; without even the implied warranty of
  12. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  13. GNU General Public License for more details.
  14.  
  15. You should have received a copy of the GNU General Public License
  16. along with GNU Make; see the file COPYING.  If not, write to
  17. the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include "make.h"
  20.  
  21. #if    defined (POSIX) || defined (HAVE_DIRENT_H) || defined (__GNU_LIBRARY__)
  22. #include <dirent.h>
  23. #ifndef    __GNU_LIBRARY__
  24. #define D_NAMLEN(d) strlen((d)->d_name)
  25. #else    /* GNU C library.  */
  26. #define D_NAMLEN(d) ((d)->d_namlen)
  27. #endif    /* Not GNU C library.  */
  28. #else    /* Not POSIX or HAVE_DIRENT_H.  */
  29. #define direct dirent
  30. #define D_NAMLEN(d) ((d)->d_namlen)
  31. #ifdef    HAVE_SYS_NDIR_H
  32. #include <sys/ndir.h>
  33. #endif    /* HAVE_SYS_NDIR_H */
  34. #ifdef    HAVE_SYS_DIR_H
  35. #include <sys/dir.h>
  36. #endif    /* HAVE_SYS_DIR_H */
  37. #ifdef HAVE_NDIR_H
  38. #include <ndir.h>
  39. #endif    /* HAVE_NDIR_H */
  40. #endif    /* POSIX or HAVE_DIRENT_H or __GNU_LIBRARY__.  */
  41.  
  42. #if (defined (POSIX) || defined (WIN32)) && !defined (__GNU_LIBRARY__)
  43. /* Posix does not require that the d_ino field be present, and some
  44.    systems do not provide it. */
  45. #define REAL_DIR_ENTRY(dp) 1
  46. #else
  47. #define REAL_DIR_ENTRY(dp) (dp->d_ino != 0)
  48. #endif /* POSIX */
  49.  
  50. #if defined (__MSDOS__)
  51. #include <ctype.h>
  52.  
  53. static char *
  54. dosify (filename)
  55.      char *filename;
  56. {
  57.   static char dos_filename[14];
  58.   char *df;
  59.   int i;
  60.  
  61.   if (filename == 0)
  62.     return 0;
  63.  
  64.   if (strpbrk (filename, "\"*+,;<=>?[\\]|") != 0)
  65.     return filename;
  66.  
  67.   df = dos_filename;
  68.  
  69.   /* First, transform the name part.  */
  70.   for (i = 0; *filename != '\0' && i < 8 && *filename != '.'; ++i)
  71.     *df++ = tolower (*filename++);
  72.  
  73.   /* Now skip to the next dot.  */
  74.   while (*filename != '\0' && *filename != '.')
  75.     ++filename;
  76.   if (*filename != '\0')
  77.     {
  78.       *df++ = *filename++;
  79.       for (i = 0; *filename != '\0' && i < 3 && *filename != '.'; ++i)
  80.     *df++ = tolower (*filename++);
  81.     }
  82.  
  83.   /* Look for more dots.  */
  84.   while (*filename != '\0' && *filename != '.')
  85.     ++filename;
  86.   if (*filename == '.')
  87.     return filename;
  88.   *df = 0;
  89.   return dos_filename;
  90. }
  91. #endif
  92.  
  93. /* Hash table of directories.  */
  94.  
  95. #ifndef    DIRECTORY_BUCKETS
  96. #define DIRECTORY_BUCKETS 199
  97. #endif
  98.  
  99. struct directory_contents
  100.   {
  101.     struct directory_contents *next;
  102.  
  103. #if defined (WIN32)
  104.     /* 
  105.     ** NT does not support inodes, so we can't use them
  106.     ** to guarantee uniqueness.  Use the path instead.
  107.     */
  108.     const char* path;        /* path to this dir */
  109. #else
  110.     int dev, ino;        /* Device and inode numbers of this dir.  */
  111. #endif
  112.  
  113.     struct dirfile **files;    /* Files in this directory.  */
  114.     DIR *dirstream;        /* Stream reading this directory.  */
  115.   };
  116.  
  117. /* Table of directory contents hashed by device and inode number.  */
  118. static struct directory_contents *directories_contents[DIRECTORY_BUCKETS];
  119.  
  120. struct directory
  121.   {
  122.     struct directory *next;
  123.  
  124.     char *name;            /* Name of the directory.  */
  125.  
  126.     /* The directory's contents.  This data may be shared by several
  127.        entries in the hash table, which refer to the same directory
  128.        (identified uniquely by `dev' and `ino') under different names.  */
  129.     struct directory_contents *contents;
  130.   };
  131.  
  132. /* Table of directories hashed by name.  */
  133. static struct directory *directories[DIRECTORY_BUCKETS];
  134.  
  135.  
  136. /* Never have more than this many directories open at once.  */
  137.  
  138. #define MAX_OPEN_DIRECTORIES 10
  139.  
  140. static unsigned int open_directories = 0;
  141.  
  142.  
  143. /* Hash table of files in each directory.  */
  144.  
  145. struct dirfile
  146.   {
  147.     struct dirfile *next;
  148.     char *name;            /* Name of the file.  */
  149.     char impossible;        /* This file is impossible.  */
  150.   };
  151.  
  152. #ifndef    DIRFILE_BUCKETS
  153. #define DIRFILE_BUCKETS 107
  154. #endif
  155.  
  156. static int dir_contents_file_exists_p ();
  157.  
  158. /* Find the directory named NAME and return its `struct directory'.  */
  159.  
  160. static struct directory *
  161. find_directory (name)
  162.      register char *name;
  163. {
  164.   register unsigned int hash = 0;
  165.   register char *p;
  166.   register struct directory *dir;
  167.  
  168.   for (p = name; *p != '\0'; ++p)
  169.     HASH (hash, *p);
  170.   hash %= DIRECTORY_BUCKETS;
  171.  
  172.   for (dir = directories[hash]; dir != 0; dir = dir->next)
  173.     if (streq (dir->name, name))
  174.       break;
  175.  
  176.   if (dir == 0)
  177.     {
  178.       struct stat st;
  179.  
  180.       /* The directory was not found.  Create a new entry for it.  */
  181.  
  182.       dir = (struct directory *) xmalloc (sizeof (struct directory));
  183.       dir->next = directories[hash];
  184.       directories[hash] = dir;
  185.       dir->name = savestring (name, p - name);
  186.  
  187.       /* The directory is not in the name hash table.
  188.      Find its device and inode numbers, and look it up by them.  */
  189.  
  190.       if (safe_stat (name, &st) < 0)
  191.     /* Couldn't stat the directory.  Mark this by
  192.        setting the `contents' member to a nil pointer.  */
  193.     dir->contents = 0;
  194.       else
  195.     {
  196.       /* Search the contents hash table; device and inode are the key.  */
  197.  
  198.       struct directory_contents *dc;
  199.  
  200. #if defined (WIN32)
  201.           {
  202.           char* s;
  203.           for (hash=0, s=name; *s; s++) hash = hash ^ *s;
  204.           }
  205. #else
  206.       hash = ((unsigned int) st.st_dev << 16) | (unsigned int) st.st_ino;
  207. #endif
  208.       hash %= DIRECTORY_BUCKETS;
  209.  
  210.       for (dc = directories_contents[hash]; dc != 0; dc = dc->next)
  211. #if defined (WIN32)
  212.             if (!strcmp (dc->path, name))
  213. #else
  214.         if (dc->dev == st.st_dev && dc->ino == st.st_ino)
  215. #endif
  216.           break;
  217.  
  218.       if (dc == 0)
  219.         {
  220.           /* Nope; this really is a directory we haven't seen before.  */
  221.  
  222.           dc = (struct directory_contents *)
  223.         xmalloc (sizeof (struct directory_contents));
  224.  
  225.           /* Enter it in the contents hash table.  */
  226. #if defined (WIN32)
  227.               /* Allocate  a copy of the path.  I would like to */
  228.               /* free  this copy when  the  structure is freed, */
  229.               /* but it appears that these structures are never */
  230.               /* freed!                                         */
  231.               dc->path = savestring (name, strlen (name));
  232. #else
  233.           dc->dev = st.st_dev;
  234.           dc->ino = st.st_ino;
  235. #endif
  236.           dc->next = directories_contents[hash];
  237.           directories_contents[hash] = dc;
  238.  
  239.           dc->dirstream = opendir (name);
  240.           if (dc->dirstream == 0)
  241.         /* Couldn't open the directory.  Mark this by
  242.            setting the `files' member to a nil pointer.  */
  243.         dc->files = 0;
  244.           else
  245.         {
  246.           /* Allocate an array of buckets for files and zero it.  */
  247.           dc->files = (struct dirfile **)
  248.             xmalloc (sizeof (struct dirfile *) * DIRFILE_BUCKETS);
  249.           bzero ((char *) dc->files,
  250.              sizeof (struct dirfile *) * DIRFILE_BUCKETS);
  251.  
  252.           /* Keep track of how many directories are open.  */
  253.           ++open_directories;
  254.           if (open_directories == MAX_OPEN_DIRECTORIES)
  255.             /* We have too many directories open already.
  256.                Read the entire directory and then close it.  */
  257.             (void) dir_contents_file_exists_p (dc, (char *) 0);
  258.         }
  259.         }
  260.  
  261.       /* Point the name-hashed entry for DIR at its contents data.  */
  262.       dir->contents = dc;
  263.     }
  264. #ifdef WIN32
  265.       /* Note that directories are read lazily, and so the directory is held
  266.          open until it is closed by dir_contents_file_exists_p(), and this
  267.          only happens if the directory is searched for a nonexistent file.
  268.          Under Windows NT, you cannot delete a directory if a process has it
  269.          open, so "make clean" can sometimes fail because make itself has
  270.          some VPATHed directory open.  The solution is to always force a
  271.          nonlazy read by querying for a nonexistent file before returning
  272.          the new directory.  Since slashes are not legal filename
  273.          characters, the file a/\b is guaranteed not to exist.  */
  274.       dir_contents_file_exists_p (dir->contents, "a/\\b");
  275. #endif
  276.  
  277.     }
  278.  
  279.   return dir;
  280. }
  281.  
  282. /* Return 1 if the name FILENAME is entered in DIR's hash table.
  283.    FILENAME must contain no slashes.  */
  284.  
  285. static int
  286. dir_contents_file_exists_p (dir, filename)
  287.      register struct directory_contents *dir;
  288.      register char *filename;
  289. {
  290.   register unsigned int hash;
  291.   register char *p;
  292.   register struct dirfile *df;
  293.   register struct dirent *d;
  294.  
  295.   if (dir == 0 || dir->files == 0)
  296.     /* The directory could not be stat'd or opened.  */
  297.     return 0;
  298.  
  299. #if defined (__MSDOS__) 
  300.   filename = dosify (filename);
  301. #endif
  302.  
  303.   hash = 0;
  304.   if (filename != 0)
  305.     {
  306.       if (*filename == '\0')
  307.     /* Checking if the directory exists.  */
  308.     return 1;
  309.  
  310.       for (p = filename; *p != '\0'; ++p)
  311.     HASH (hash, *p);
  312.       hash %= DIRFILE_BUCKETS;
  313.  
  314.       /* Search the list of hashed files.  */
  315.  
  316.       for (df = dir->files[hash]; df != 0; df = df->next)
  317.     if (streq (df->name, filename))
  318.       return !df->impossible;
  319.     }
  320.  
  321.   /* The file was not found in the hashed list.
  322.      Try to read the directory further.  */
  323.  
  324.   if (dir->dirstream == 0)
  325.     /* The directory has been all read in.  */
  326.     return 0;
  327.  
  328.   while ((d = readdir (dir->dirstream)) != 0)
  329.     {
  330.       /* Enter the file in the hash table.  */
  331.       register unsigned int newhash = 0;
  332.       unsigned int len;
  333.       register unsigned int i;
  334.  
  335.       if (!REAL_DIR_ENTRY (d))
  336.     continue;
  337.  
  338.       len = D_NAMLEN (d);
  339.       while (d->d_name[len - 1] == '\0')
  340.     --len;
  341.  
  342.       for (i = 0; i < len; ++i)
  343.     HASH (newhash, d->d_name[i]);
  344.       newhash %= DIRFILE_BUCKETS;
  345.  
  346.       df = (struct dirfile *) xmalloc (sizeof (struct dirfile));
  347.       df->next = dir->files[newhash];
  348.       dir->files[newhash] = df;
  349.       df->name = savestring (d->d_name, len);
  350.       df->impossible = 0;
  351.  
  352.       /* Check if the name matches the one we're searching for.  */
  353.       if (filename != 0
  354.       && newhash == hash && streq (d->d_name, filename))
  355.     return 1;
  356.     }
  357.  
  358.   /* If the directory has been completely read in,
  359.      close the stream and reset the pointer to nil.  */
  360.   if (d == 0)
  361.     {
  362.       --open_directories;
  363.       closedir (dir->dirstream);
  364.       dir->dirstream = 0;
  365.     }
  366.  
  367.   return 0;
  368. }
  369.  
  370. /* Return 1 if the name FILENAME in directory DIRNAME
  371.    is entered in the dir hash table.
  372.    FILENAME must contain no slashes.  */
  373.  
  374. int
  375. dir_file_exists_p (dirname, filename)
  376.      register char *dirname;
  377.      register char *filename;
  378. {
  379.   return dir_contents_file_exists_p (find_directory (dirname)->contents,
  380.                      filename);
  381. }
  382.  
  383. /* Return 1 if the file named NAME exists.  */
  384.  
  385. int
  386. file_exists_p (name)
  387.      register char *name;
  388. {
  389.   char *dirend;
  390.   char *dirname;
  391.  
  392. #ifndef    NO_ARCHIVES
  393.   if (ar_name (name))
  394.     return ar_member_date (name) != (time_t) -1;
  395. #endif
  396.  
  397.   dirend = rindex (name, '/');
  398.   if (dirend == 0)
  399.     return dir_file_exists_p (".", name);
  400.  
  401.   dirname = (char *) alloca (dirend - name + 1);
  402.   bcopy (name, dirname, dirend - name);
  403.   dirname[dirend - name] = '\0';
  404.   return dir_file_exists_p (dirname, dirend + 1);
  405. }
  406.  
  407. /* Mark FILENAME as `impossible' for `file_impossible_p'.
  408.    This means an attempt has been made to search for FILENAME
  409.    as an intermediate file, and it has failed.  */
  410.  
  411. void
  412. file_impossible (filename)
  413.      register char *filename;
  414. {
  415.   char *dirend;
  416.   register char *p = filename;
  417.   register unsigned int hash;
  418.   register struct directory *dir;
  419.   register struct dirfile *new;
  420.  
  421.   dirend = rindex (p, '/');
  422.   if (dirend == 0)
  423.     dir = find_directory (".");
  424.   else
  425.     {
  426.       char *dirname = (char *) alloca (dirend - p + 1);
  427.       bcopy (p, dirname, dirend - p);
  428.       dirname[dirend - p] = '\0';
  429.       dir = find_directory (dirname);
  430.       filename = p = dirend + 1;
  431.     }
  432.  
  433.   for (hash = 0; *p != '\0'; ++p)
  434.     HASH (hash, *p);
  435.   hash %= DIRFILE_BUCKETS;
  436.  
  437.   if (dir->contents == 0)
  438.     {
  439.       /* The directory could not be stat'd.  We allocate a contents
  440.      structure for it, but leave it out of the contents hash table.  */
  441.       dir->contents = (struct directory_contents *)
  442.     xmalloc (sizeof (struct directory_contents));
  443. #if defined (WIN32)
  444.       dir->contents->path = NULL;
  445. #else
  446.       dir->contents->dev = dir->contents->ino = 0;
  447. #endif
  448.       dir->contents->files = 0;
  449.       dir->contents->dirstream = 0;
  450.     }
  451.  
  452.   if (dir->contents->files == 0)
  453.     {
  454.       /* The directory was not opened; we must allocate the hash buckets.  */
  455.       dir->contents->files = (struct dirfile **)
  456.     xmalloc (sizeof (struct dirfile) * DIRFILE_BUCKETS);
  457.       bzero ((char *) dir->contents->files,
  458.          sizeof (struct dirfile) * DIRFILE_BUCKETS);
  459.     }
  460.  
  461.   /* Make a new entry and put it in the table.  */
  462.  
  463.   new = (struct dirfile *) xmalloc (sizeof (struct dirfile));
  464.   new->next = dir->contents->files[hash];
  465.   dir->contents->files[hash] = new;
  466.   new->name = savestring (filename, strlen (filename));
  467.   new->impossible = 1;
  468. }
  469.  
  470. /* Return nonzero if FILENAME has been marked impossible.  */
  471.  
  472. int
  473. file_impossible_p (filename)
  474.      char *filename;
  475. {
  476.   char *dirend;
  477.   register char *p = filename;
  478.   register unsigned int hash;
  479.   register struct directory_contents *dir;
  480.   register struct dirfile *next;
  481.  
  482.   dirend = rindex (filename, '/');
  483.   if (dirend == 0)
  484.     dir = find_directory (".")->contents;
  485.   else
  486.     {
  487.       char *dirname = (char *) alloca (dirend - filename + 1);
  488.       bcopy (p, dirname, dirend - p);
  489.       dirname[dirend - p] = '\0';
  490.       dir = find_directory (dirname)->contents;
  491.       p = dirend + 1;
  492.     }
  493.  
  494.   if (dir == 0 || dir->files == 0)
  495.     /* There are no files entered for this directory.  */
  496.     return 0;
  497.  
  498. #if defined (__MSDOS__) 
  499.   p = filename = dosify (p);
  500. #endif
  501.  
  502.   for (hash = 0; *p != '\0'; ++p)
  503.     HASH (hash, *p);
  504.   hash %= DIRFILE_BUCKETS;
  505.  
  506.   for (next = dir->files[hash]; next != 0; next = next->next)
  507.     if (streq (filename, next->name))
  508.       return next->impossible;
  509.  
  510.   return 0;
  511. }
  512.  
  513. /* Return the already allocated name in the
  514.    directory hash table that matches DIR.  */
  515.  
  516. char *
  517. dir_name (dir)
  518.      char *dir;
  519. {
  520.   return find_directory (dir)->name;
  521. }
  522.  
  523. /* Print the data base of directories.  */
  524.  
  525. void
  526. print_dir_data_base ()
  527. {
  528.   register unsigned int i, dirs, files, impossible;
  529.   register struct directory *dir;
  530.  
  531.   puts ("\n# Directories\n");
  532.  
  533.   dirs = files = impossible = 0;
  534.   for (i = 0; i < DIRECTORY_BUCKETS; ++i)
  535.     for (dir = directories[i]; dir != 0; dir = dir->next)
  536.       {
  537.     ++dirs;
  538.     if (dir->contents == 0)
  539.       printf ("# %s: could not be stat'd.\n", dir->name);
  540.     else if (dir->contents->files == 0)
  541. #if defined (WIN32)
  542.           printf ("# %s (path %s): could not be opened.\n",
  543.                   dir->name, dir->contents->path);
  544. #else
  545.       printf ("# %s (device %d, inode %d): could not be opened.\n",
  546.           dir->name, dir->contents->dev, dir->contents->ino);
  547. #endif
  548.     else
  549.       {
  550.         register unsigned int f = 0, im = 0;
  551.         register unsigned int j;
  552.         register struct dirfile *df;
  553.         for (j = 0; j < DIRFILE_BUCKETS; ++j)
  554.           for (df = dir->contents->files[j]; df != 0; df = df->next)
  555.         if (df->impossible)
  556.           ++im;
  557.         else
  558.           ++f;
  559. #if defined (WIN32)
  560.             printf ("# %s (path %s): ",
  561.                     dir->name, dir->contents->path);
  562. #else
  563.         printf ("# %s (device %d, inode %d): ",
  564.             dir->name, dir->contents->dev, dir->contents->ino);
  565. #endif
  566.         if (f == 0)
  567.           fputs ("No", stdout);
  568.         else
  569.           printf ("%u", f);
  570.         fputs (" files, ", stdout);
  571.         if (im == 0)
  572.           fputs ("no", stdout);
  573.         else
  574.           printf ("%u", im);
  575.         fputs (" impossibilities", stdout);
  576.         if (dir->contents->dirstream == 0)
  577.           puts (".");
  578.         else
  579.           puts (" so far.");
  580.         files += f;
  581.         impossible += im;
  582.       }
  583.       }
  584.  
  585.   fputs ("\n# ", stdout);
  586.   if (files == 0)
  587.     fputs ("No", stdout);
  588.   else
  589.     printf ("%u", files);
  590.   fputs (" files, ", stdout);
  591.   if (impossible == 0)
  592.     fputs ("no", stdout);
  593.   else
  594.     printf ("%u", impossible);
  595.   printf (" impossibilities in %u directories.\n", dirs);
  596. }
  597.  
  598. /* Hooks for globbing.  */
  599.  
  600. #include <glob.h>
  601.  
  602. /* Structure describing state of iterating through a directory hash table.  */
  603.  
  604. struct dirstream
  605.   {
  606.     struct directory_contents *contents; /* The directory being read.  */
  607.  
  608.     unsigned int bucket;    /* Current hash bucket.  */
  609.     struct dirfile *elt;    /* Current elt in bucket.  */
  610.   };
  611.  
  612. /* Forward declarations.  */
  613. static __ptr_t open_dirstream __P ((const char *));
  614. static const char *read_dirstream __P ((__ptr_t));
  615.  
  616. static __ptr_t
  617. open_dirstream (directory)
  618.      const char *directory;
  619. {
  620.   struct dirstream *new;
  621.   struct directory *dir = find_directory (directory);
  622.  
  623.   if (dir->contents == 0 || dir->contents->files == 0)
  624.     /* DIR->contents is nil if the directory could not be stat'd.
  625.        DIR->contents->files is nil if it could not be opened.  */
  626.     return 0;
  627.  
  628.   /* Read all the contents of the directory now.  There is no benefit
  629.      in being lazy, since glob will want to see every file anyway.  */
  630.  
  631.   (void) dir_contents_file_exists_p (dir->contents, (char *) 0);
  632.  
  633.   new = (struct dirstream *) xmalloc (sizeof (struct dirstream));
  634.   new->contents = dir->contents;
  635.   new->bucket = 0;
  636.   new->elt = new->contents->files[0];
  637.  
  638.   return (__ptr_t) new;
  639. }
  640.  
  641. static const char *
  642. read_dirstream (stream)
  643.      __ptr_t stream;
  644. {
  645.   struct dirstream *const ds = (struct dirstream *) stream;
  646.   register struct dirfile *df;
  647.  
  648.   while (ds->bucket < DIRFILE_BUCKETS)
  649.     {
  650.       while ((df = ds->elt) != 0)
  651.     {
  652.       ds->elt = df->next;
  653.       if (!df->impossible)
  654.         return df->name;
  655.     }
  656.       if (++ds->bucket == DIRFILE_BUCKETS)
  657.           break;
  658.       ds->elt = ds->contents->files[ds->bucket];
  659.     }
  660.  
  661.   return 0;
  662. }
  663.  
  664. void
  665. init_dir ()
  666. {
  667.   __glob_opendir_hook = open_dirstream;
  668.   __glob_readdir_hook = read_dirstream;
  669.   __glob_closedir_hook = (void (*) __P ((__ptr_t stream))) free;
  670. }
  671.