home *** CD-ROM | disk | FTP | other *** search
/ Geek Gadgets 1 / ADE-1.bin / ade-dist / octave-1.1.1p1-base.tgz / octave-1.1.1p1-base.tar / fsf / octave / kpathsea / elt-dirs.c < prev    next >
C/C++ Source or Header  |  1994-02-24  |  10KB  |  354 lines

  1. /* elt-dirs.c: Translate a path element to its corresponding director{y,ies}.
  2.  
  3. Copyright (C) 1993, 94 Karl Berry.
  4.  
  5. This program 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. This program 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 this program; if not, write to the Free Software
  17. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  18.  
  19. #include <kpathsea/config.h>
  20.  
  21. #include <kpathsea/c-pathch.h>
  22. #include <kpathsea/dir.h>
  23. #include <kpathsea/expand.h>
  24. #include <kpathsea/fn.h>
  25. #include <kpathsea/pathsearch.h>
  26.  
  27. /* To avoid giving prototypes for all the routines and then their real
  28.    definitions, we give all the subroutines first.  The entry point is
  29.    the last routine in the file.  */
  30.  
  31. /* Make a copy of DIR (unless it's null) and save it in L.  Ensure that
  32.    DIR ends with a DIR_SEP for the benefit of later searches.  */
  33.  
  34. static void
  35. dir_list_add P2C(str_llist_type *, l,  const_string, dir)
  36. {
  37.   string saved_dir
  38.     = IS_DIR_SEP (dir[strlen (dir) - 1])
  39.       ? xstrdup (dir)
  40.       : concat (dir, DIR_SEP_STRING);
  41.   
  42.   str_llist_add (l, saved_dir);
  43. }
  44.  
  45.  
  46. /* If DIR is a directory, add it to the list L.  */
  47.  
  48. static void
  49. checked_dir_list_add P2C(str_llist_type *, l,  const_string, dir)
  50. {
  51.   if (dir_p (dir))
  52.     dir_list_add (l, dir);
  53. }
  54.  
  55. /* The cache.  Typically, several paths have the same element; for
  56.    example, /usr/local/lib/texmf/fonts//.  We don't want to compute the
  57.    expansion of such a thing more than once.  */
  58.  
  59. typedef struct
  60. {
  61.   const_string key;
  62.   str_llist_type *value;
  63. } cache_entry;
  64.  
  65. static cache_entry *the_cache = NULL;
  66. static unsigned cache_length = 0;
  67.  
  68.  
  69. /* Associate KEY with VALUE.  We implement the cache as a simple linear
  70.    list, since it's unlikely to ever be more than a dozen or so elements
  71.    long.  We don't bother to check here if PATH has already been saved;
  72.    we always add it to our list.  We copy KEY but not VALUE; not sure
  73.    that's right, but it seems to be all that's needed.  */
  74.  
  75. static void
  76. cache P2C(const_string, key,  str_llist_type *, value)
  77. {
  78.   cache_length++;
  79.   XRETALLOC (the_cache, cache_length, cache_entry);
  80.   the_cache[cache_length - 1].key = xstrdup (key);
  81.   the_cache[cache_length - 1].value = value;
  82. }
  83.  
  84.  
  85. /* To retrieve, just check the list in order.  */
  86.  
  87. static str_llist_type *
  88. cached P1C(const_string, key)
  89. {
  90.   unsigned p;
  91.   
  92.   for (p = 0; p < cache_length; p++)
  93.     {
  94.       if (STREQ (the_cache[p].key, key))
  95.         return the_cache[p].value;
  96.     }
  97.   
  98.   return NULL;
  99. }
  100.  
  101. /* Handle the magic path constructs.  */
  102.  
  103. /* Declare recursively called routine.  */
  104. static void expand_elt P3H(str_llist_type *, const_string, unsigned);
  105.  
  106.  
  107. /* POST is a pointer into the original element (which may no longer be
  108.    ELT) to just after the doubled DIR_SEP, perhaps to the null.  Append
  109.    subdirectories of ELT (up to ELT_LENGTH, which must be a /) to
  110.    STR_LIST_PTR.  */
  111.  
  112. static void
  113. do_subdir P4C(str_llist_type *, str_list_ptr,  const_string, elt,
  114.               unsigned, elt_length,  const_string, post)
  115. {
  116.   DIR *dir;
  117.   struct dirent *e;
  118.   fn_type name;
  119.   
  120.   /* Some old compilers don't allow aggregate initialization.  */
  121.   name = fn_copy0 (elt, elt_length);
  122.   
  123.   assert (IS_DIR_SEP (elt[elt_length - 1]));
  124.   
  125.   /* If we can't open it, quit.  */
  126.   dir = opendir (FN_STRING (name));
  127.   if (dir == NULL)
  128.     {
  129.       fn_free (&name);
  130.       return;
  131.     }
  132.   
  133.   /* Include top-level directory before subdirectories.  */
  134.   if (*post == 0)
  135.     dir_list_add (str_list_ptr, FN_STRING (name));
  136.  
  137.   while ((e = readdir (dir)) != NULL)
  138.     { /* If it's . or .., never mind.  */
  139.       if (!(e->d_name[0] == '.'
  140.             && (e->d_name[1] == 0
  141.                 || (e->d_name[1] == '.' && e->d_name[2] == 0))))
  142.         {
  143.           int links;
  144.           
  145.           /* Construct the potential subdirectory name.  */
  146.           fn_str_grow (&name, e->d_name);
  147.           
  148.           /* If we can't stat it, or if it isn't a directory, continue.  */
  149.           links = dir_links (FN_STRING (name));
  150.  
  151.           if (links >= 0)
  152.             { 
  153.               unsigned potential_length = FN_LENGTH (name);
  154.               
  155.               /* It's a directory, so append the separator.  */
  156.               fn_str_grow (&name, DIR_SEP_STRING);
  157.  
  158.               /* NAME is a subdirectory; recursively expand NAME + POST.  */
  159.               if (*post != 0)
  160.                 {
  161.                   /* But if POST is exactly NAME, don't tack it on, just
  162.                      try it. If the path is /a//b, and there is an
  163.                      actual directory a/b, we want to find it. */
  164.                   if (!STREQ (post, e->d_name))
  165.                     fn_str_grow (&name, post);
  166.                   expand_elt (str_list_ptr, FN_STRING (name),
  167.                               potential_length);
  168.                 }
  169.  
  170.               /* Should we recurse?  To see if the subdirectory is a
  171.                  leaf, check if it has two links (one for . and one for
  172.                  ..).  This means that symbolic links to directories do
  173.                  not affect the leaf-ness.  This is arguably wrong, but
  174.                  the only alternative I know of is to stat every entry
  175.                  in the directory, and that is unacceptably slow.
  176.                  
  177.                  The #ifdef here makes this configurable at
  178.                  compile-time, so that if we're using VMS directories or
  179.                  some such, we can still find subdirectories, even if it
  180.                  is much slower.  */
  181. #ifdef UNIX_ST_NLINK
  182.               if (links > 2)
  183. #endif
  184.                 { /* All criteria are met; find subdirectories.  */
  185.                   do_subdir (str_list_ptr, FN_STRING (name),
  186.                              potential_length, post);
  187.                 }
  188. #ifdef UNIX_ST_NLINK
  189.               else 
  190. #endif
  191.                    if (*post == 0)
  192.                 /* Nothing more to match, no recursive subdirectories to
  193.                    look for: we're done with this branch.  Add it.  */
  194.                 dir_list_add (str_list_ptr, FN_STRING (name));
  195.             }
  196.  
  197.           /* Remove the directory entry we just checked from `name'.  */
  198.           fn_shrink_to (&name, elt_length);
  199.         }
  200.     }
  201.   
  202.   fn_free (&name);
  203.   xclosedir (dir);
  204. }
  205.  
  206.  
  207. /* Assume ELT is non-empty and non-NULL.  Return list of corresponding
  208.    directories (with no terminating NULL entry) in STR_LIST_PTR.  Start
  209.    looking for magic constructs at START.  */
  210.  
  211. static void
  212. expand_elt P3C(str_llist_type *, str_list_ptr,  const_string, elt,
  213.                unsigned, start)
  214. {
  215.   boolean found_special = false;
  216.   const_string dir = elt + start;
  217.   
  218.   while (*dir != 0)
  219.     {
  220.       if (IS_DIR_SEP (*dir))
  221.         {
  222.           /* If two consecutive directory separators, find subdirectories.  */
  223.           if (IS_DIR_SEP (dir[1]))
  224.             {
  225.               do_subdir (str_list_ptr, elt, dir - elt + 1, dir + 2);
  226.               found_special = true;
  227.             }
  228. #if 0
  229.   /* Maybe eventually I'll implement this.  */
  230.           /* If /?, make following component optional.  */
  231.           else if (dir[1] == '?')
  232.             do_optional (str_list_ptr, elt, dir - elt + 1, dir + 2);
  233. #endif
  234.           /* No special stuff at this slash.  Keep going.  */
  235.         }
  236.       
  237.       dir++;
  238.     }
  239.   
  240.   if (!found_special)
  241.     /* When we reach the end of ELT, it will be a normal filename.  */
  242.     checked_dir_list_add (str_list_ptr, elt);
  243. }
  244.  
  245. /* Here is the entry point.  Returns directory list for ELT.  */
  246.  
  247. str_llist_type *
  248. kpse_element_dirs P1C(const_string, elt)
  249. {
  250.   str_llist_type *ret;
  251.  
  252.   /* If given nothing, return nothing.  */
  253.   if (!elt)
  254.     return NULL;
  255.  
  256.   /* If we've already cached the answer for ELT, return it.  */
  257.   ret = cached (elt);
  258.   if (ret)
  259.     return ret;
  260.  
  261.   /* We're going to have a real directory list to return.  */
  262.   ret = XTALLOC1 (str_llist_type);
  263.   *ret = NULL;
  264.   
  265.   /* If ELT is the empty string, just return cwd.  */
  266.   if (*elt == 0)
  267.     { /* Some old compilers do not support aggregate initialization.  */
  268.       char cwd[3];
  269.       cwd[0] = '.';
  270.       cwd[1] = DIR_SEP;
  271.       cwd[2] = 0;
  272.       
  273.       checked_dir_list_add (ret, cwd);
  274.     }
  275.  
  276.   /* OK, so much for the trivial cases.  We handle the hard case in
  277.      a subroutine.  */
  278.   else
  279.     {
  280.       /* If the path starts with ~ or ~user, expand it.  Do this
  281.          before calling `expand_subdir' or `add_directory', so that
  282.          we don't expand the same ~ over and over.  */
  283.       string dir = kpse_expand (elt);
  284.  
  285.       expand_elt (ret, dir, 0);
  286.       
  287.       free (dir);
  288.     }  
  289.  
  290.   /* Remember the directory list we just found, in case future calls are
  291.      made with the same ELT.  */
  292.   cache (elt, ret);
  293.  
  294.   return ret;
  295. }
  296.  
  297. #ifdef TEST
  298.  
  299. void
  300. print_element_dirs (const_string elt)
  301. {
  302.   str_llist_type *dirs;
  303.   
  304.   printf ("Directories of %s:\t", elt ? elt : "(null)");
  305.   fflush (stdout);
  306.   
  307.   dirs = kpse_element_dirs (elt);
  308.   
  309.   if (!dirs)
  310.     printf ("(null)");
  311.   else
  312.     {
  313.       str_llist_elt_type *dir;
  314.       for (dir = *dirs; dir; dir = STR_LLIST_NEXT (*dir))
  315.         {
  316.           string d = STR_LLIST (*dir);
  317.           printf ("%s ", *d ? d : "`'");
  318.         }
  319.     }
  320.   
  321.   putchar ('\n');
  322. }
  323.  
  324. int
  325. main ()
  326. {
  327.   /* DEBUG_SET (DEBUG_STAT); */
  328.  
  329.   /* All lists end with NULL.  */
  330.   print_element_dirs (NULL);    /* */
  331.   print_element_dirs ("");    /* ./ */
  332.   print_element_dirs ("/k");    /* */
  333.   print_element_dirs (".//");    /* ./ ./archive/ */
  334.   print_element_dirs (".//archive");    /* ./ ./archive/ */
  335.   print_element_dirs ("/tmp/fonts//");    /* no need to stat anything */
  336.   print_element_dirs ("/usr/local/lib/tex/fonts//");      /* lots */
  337.   print_element_dirs ("/usr/local/lib/tex/fonts//times"); /* just one */
  338.   print_element_dirs ("/usr/local/lib/tex/fonts//"); /* lots again [cache] */
  339.   print_element_dirs ("~karl");        /* tilde expansion */
  340.   print_element_dirs ("$karl");        /* variable expansion */  
  341.   print_element_dirs ("~${LOGNAME}");    /* both */  
  342.   
  343.   return 0;
  344. }
  345.  
  346. #endif /* TEST */
  347.  
  348.  
  349. /*
  350. Local variables:
  351. test-compile-command: "gcc -posix -g -I. -I.. -DTEST elt-dirs.c kpathsea.a"
  352. End:
  353. */
  354.