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 / pathsearch.c < prev    next >
C/C++ Source or Header  |  1994-03-24  |  10KB  |  328 lines

  1. /* pathsearch.c: look up a filename in a path.
  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. #include <kpathsea/absolute.h>
  21. #include <kpathsea/expand.h>
  22. #include <kpathsea/db.h>
  23. #include <kpathsea/pathsearch.h>
  24. #include <kpathsea/readable.h>
  25. #include <kpathsea/str-list.h>
  26.  
  27.  
  28. /* Concatenate each element in DIRS with NAME (assume each ends with a
  29.    /, to save time).  If SEARCH_ALL is false, return the first readable
  30.    regular file.  Else continue to search for more.  In any case, if
  31.    none, return a list containing just NULL.
  32.  
  33.    We keep a single buffer for the potential filenames and reallocate
  34.    only when necessary.  I'm not sure it's noticeably faster, but it
  35.    does seem cleaner.  (We do waste a bit of space in the return
  36.    value, though, since we don't shrink it to the final size returned.)  */
  37.  
  38. #define INIT_ALLOC 75  /* Doesn't much matter what this number is.  */
  39.  
  40. static str_list_type
  41. dir_list_search P3C(str_llist_type *, dirs,  const_string, name,
  42.                     boolean, search_all)
  43. {
  44.   str_llist_elt_type *elt;
  45.   str_list_type ret;
  46.   unsigned name_len = strlen (name);
  47.   unsigned allocated = INIT_ALLOC;
  48.   string potential = xmalloc (allocated);
  49.  
  50.   ret = str_list_init ();
  51.   
  52.   for (elt = *dirs; elt; elt = STR_LLIST_NEXT (*elt))
  53.     {
  54.       const_string dir = STR_LLIST (*elt);
  55.       unsigned dir_len = strlen (dir);
  56.       
  57.       while (dir_len + name_len + 1 > allocated)
  58.         {
  59.           allocated += allocated;
  60.           XRETALLOC (potential, allocated, char);
  61.         }
  62.       
  63.       strcpy (potential, dir);
  64.       strcat (potential, name);
  65.       
  66.       if (kpse_readable_file (potential))
  67.         { 
  68.           str_list_add (&ret, potential);
  69.           
  70.           /* Move this element towards the top of the list.  */
  71.           str_llist_float (dirs, elt);
  72.           
  73.           /* If caller only wanted one file returned, no need to
  74.              terminate the list with NULL; the caller knows to only look
  75.              at the first element.  */
  76.           if (!search_all)
  77.             return ret;
  78.  
  79.           /* Start new filename.  */
  80.           allocated = INIT_ALLOC;
  81.           potential = xmalloc (allocated);
  82.         }
  83.     }
  84.   
  85.   /* If we get here, either we didn't find any files, or we were finding
  86.      all the files.  But we're done with the last filename, anyway.  */
  87.   free (potential);
  88.   
  89.   return ret;
  90. }
  91.  
  92. /* This is called when NAME is absolute or explicitly relative; if it's
  93.    readable, return (a list containing) it; otherwise, return NULL.  */
  94.  
  95. static str_list_type
  96. absolute_search P1C(string, name)
  97. {
  98.   str_list_type ret_list;
  99.   string found = kpse_readable_file (name);
  100.   
  101.   /* Some old compilers can't initialize structs.  */
  102.   ret_list = str_list_init ();
  103.  
  104.   /* If NAME wasn't found, free the expansion.  */
  105.   if (name != found)
  106.     free (name);
  107.  
  108.   /* Add `found' to the return list even if it's null; that tells
  109.      the caller we didn't find anything.  */
  110.   str_list_add (&ret_list, found);
  111.   
  112.   return ret_list;
  113. }
  114.  
  115. /* If DB_DIR is a prefix of PATH_ELT, return true; otherwise false.
  116.    That is, the question is whether to try the db for a file looked up
  117.    in PATH_ELT.  If PATH_ELT == ".", for example, the answer is no. If
  118.    PATH_ELT == "/usr/local/lib/texmf/fonts//tfm", the answer is yes.
  119.    
  120.    We do a simple string compare, ignoring //. In practice I think //'s
  121.    will always be after DB_DIR.  */
  122.    
  123. static boolean
  124. elt_in_db P1C(const_string, path_elt)
  125. {
  126.   const_string db_dir = DB_DIR;
  127.   while (*db_dir++ == *path_elt++)
  128.     { /* If we've matched the entire db directory, it's good.  */
  129.       if (*db_dir == 0)
  130.         return true;
  131.       
  132.       /* If we've reached the end of PATH_ELT, but not the end of the db
  133.          directory, that's no good.  */
  134.       if (*path_elt == 0)
  135.         break;
  136.     }
  137.   return false;
  138. }
  139.  
  140.  
  141. /* This is the hard case -- look for NAME in PATH.  If
  142.    ALL is false, just return the first file found.  Otherwise,
  143.    search all elements of PATH.  */
  144.  
  145. static str_list_type
  146. path_search P4C(const_string, path,  string, name,
  147.                 boolean, must_exist,  boolean, all)
  148. {
  149.   string elt;
  150.   str_list_type ret_list;
  151.   boolean done = false;
  152.   ret_list = str_list_init ();
  153.  
  154.   for (elt = kpse_path_element (path); !done && elt;
  155.        elt = kpse_path_element (NULL))
  156.     {
  157.       boolean try_db;
  158.       boolean forbid_fs_search = false;
  159.       str_list_type *found = NULL;
  160.       
  161.       if (*elt == '%' && *(elt + 1) == '%')
  162.         { /* Magic leading chars in a path element means don't search the
  163.              disk regardless.  And take off the magic for use below.  */
  164.           forbid_fs_search = true;
  165.           elt += 2;
  166.         }
  167.       
  168.       /* Try the prebuilt db only if it's relevant to this path element. */
  169.       try_db = elt_in_db (elt);
  170.       found = try_db ? kpse_db_search (name, elt, all) : NULL;
  171.       
  172.       /* Search the filesystem if (1) the path spec allows it, and either
  173.          (2a) the db was irrelevant to ELT (try_db == false); or
  174.          (2b) no db exists (kpse_db_search returns NULL); or
  175.          (3) NAME was not in the db (kpse_db_search returns an empty list)
  176.              and MUST_EXIST.
  177.          In (2a) and (2b), `found' will be NULL.  */
  178.       if (!forbid_fs_search && (!found || (!STR_LIST (*found) && must_exist)))
  179.         {
  180.           str_llist_type *dirs = kpse_element_dirs (elt);
  181.           if (dirs && *dirs)
  182.             {
  183.               if (!found)
  184.                 found = XTALLOC1 (str_list_type);
  185.               *found = dir_list_search (dirs, name, all);
  186.             }
  187.         }
  188.  
  189.       /* Did we find anything anywhere?  */
  190.       if (found && STR_LIST (*found))
  191.         if (all)
  192.           str_list_concat (&ret_list, *found);
  193.         else
  194.           {
  195.             str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
  196.             done = true;
  197.           }
  198.       
  199.       /* Free the list space, if any (but not the elements).  */
  200.       if (found)
  201.         free (found);
  202.     }
  203.  
  204.   /* Free the expanded name we were passed.  It can't be in the return
  205.      list, since the path directories got unconditionally prepended.  */
  206.   free (name);
  207.   
  208.   return ret_list;
  209. }      
  210.  
  211. /* Search PATH for ORIGINAL_NAME.  If ALL is false, or
  212.    ORIGINAL_NAME is absolute_p, check
  213.    ORIGINAL_NAME itself.  Otherwise, look at each element of PATH for
  214.    the first readable ORIGINAL_NAME.
  215.    
  216.    Always return a list; if no files are found, the list will
  217.    contain just NULL.  If ALL is true, the list will be
  218.    terminated with NULL.  */
  219.  
  220. static string *
  221. search P4C(const_string, path,  const_string, original_name,
  222.            boolean, must_exist,  boolean, all)
  223. {
  224.   str_list_type ret_list;
  225.  
  226.   /* Make a leading ~ count as an absolute filename, and expand $FOO's.  */
  227.   string name = kpse_expand (original_name);
  228.   
  229.   /* If the first name is absolute or explicitly relative, no need to
  230.      consider PATH at all.  */
  231.   boolean absolute_p = kpse_absolute_p (name);
  232.   
  233.   /* Find the file(s). */
  234.   ret_list = absolute_p ? absolute_search (name)
  235.                         : path_search (path, name, must_exist, all);
  236.   
  237.   /* Append NULL terminator if we didn't find anything at all, or we're
  238.      supposed to find ALL and the list doesn't end in NULL now.  */
  239.   if (STR_LIST_LENGTH (ret_list) == 0
  240.       || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
  241.     str_list_add (&ret_list, NULL);
  242.  
  243.   return STR_LIST (ret_list);
  244. }
  245.  
  246. /* Search PATH for the first NAME.  */
  247.  
  248. string
  249. kpse_path_search P3C(const_string, path,  const_string, name,
  250.                      boolean, must_exist)
  251. {
  252.   string *ret_list = search (path, name, must_exist, false);
  253.   return *ret_list;
  254. }
  255.  
  256.  
  257. /* Search all elements of PATH for files named NAME.  Not sure if it's
  258.    right to assert `must_exist' here, but it suffices now.  */
  259.  
  260. string *
  261. kpse_all_path_search P2C(const_string, path,  const_string, name)
  262. {
  263.   string *ret = search (path, name, true, true);
  264.   return ret;
  265. }
  266.  
  267. #ifdef TEST
  268.  
  269. void
  270. test_path_search (const_string path, const_string file)
  271. {
  272.   string answer;
  273.   string *answer_list;
  274.   
  275.   printf ("\nSearch %s for %s:\t", path, file);
  276.   answer = kpse_path_search (path, file);
  277.   puts (answer ? answer : "(null)");
  278.  
  279.   printf ("Search %s for all %s:\t", path, file);
  280.   answer_list = kpse_all_path_search (path, file);
  281.   putchar ('\n');
  282.   while (*answer_list)
  283.     {
  284.       putchar ('\t');
  285.       puts (*answer_list);
  286.       answer_list++;
  287.     }
  288. }
  289.  
  290. #define TEXFONTS "/usr/local/lib/tex/fonts"
  291.  
  292. int
  293. main ()
  294. {
  295.   /* All lists end with NULL.  */
  296.   test_path_search (".", "nonexistent");
  297.   test_path_search (".", "/nonexistent");
  298.   test_path_search ("/k:.", "kpathsea.texi");
  299.   test_path_search ("/k:.", "/etc/fstab");
  300.   test_path_search (".:" TEXFONTS "//", "cmr10.tfm");
  301.   test_path_search (".:" TEXFONTS "//", "logo10.tfm");
  302.   test_path_search (TEXFONTS "//times:.::", "ptmr.vf");
  303.   test_path_search (TEXFONTS "/adobe//:"
  304.                     "/usr/local/src/TeX+MF/typefaces//", "plcr.pfa");
  305.   
  306.   test_path_search ("~karl", ".bashrc");
  307.   test_path_search ("/k", "~karl/.bashrc");
  308.  
  309.   xputenv ("NONEXIST", "nonexistent");
  310.   test_path_search (".", "$NONEXISTENT");
  311.   xputenv ("KPATHSEA", "kpathsea");
  312.   test_path_search ("/k:.", "$KPATHSEA.texi");  
  313.   test_path_search ("/k:.", "${KPATHSEA}.texi");  
  314.   test_path_search ("$KPATHSEA:.", "README");  
  315.   test_path_search (".:$KPATHSEA", "README");  
  316.   
  317.   return 0;
  318. }
  319.  
  320. #endif /* TEST */
  321.  
  322.  
  323. /*
  324. Local variables:
  325. test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a"
  326. End:
  327. */
  328.