home *** CD-ROM | disk | FTP | other *** search
/ OS/2 Shareware BBS: 10 Tools / 10-Tools.zip / octa21fs.zip / octave / kpathsea / pathsearch.c < prev    next >
C/C++ Source or Header  |  2000-01-15  |  14KB  |  438 lines

  1. /* pathsearch.c: look up a filename in a path.
  2.  
  3. Copyright (C) 1993, 94, 95, 97 Karl Berry.
  4.  
  5. This library is free software; you can redistribute it and/or
  6. modify it under the terms of the GNU Library General Public
  7. License as published by the Free Software Foundation; either
  8. version 2 of the License, or (at your option) any later version.
  9.  
  10. This library 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 GNU
  13. Library General Public License for more details.
  14.  
  15. You should have received a copy of the GNU Library General Public
  16. License along with this library; if not, write to the Free Software
  17. Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
  18.  
  19. /* Modified by Klaus Gebhardt, 1999 */
  20.  
  21. #include <kpathsea/config.h>
  22. #include <kpathsea/c-pathch.h>
  23. #include <kpathsea/c-fopen.h>
  24. #include <kpathsea/absolute.h>
  25. #include <kpathsea/expand.h>
  26. #include <kpathsea/db.h>
  27. #include <kpathsea/pathsearch.h>
  28. #include <kpathsea/readable.h>
  29. #include <kpathsea/str-list.h>
  30. #include <kpathsea/str-llist.h>
  31. #include <kpathsea/variable.h>
  32.  
  33. #include <time.h> /* for `time' */
  34.  
  35. #ifdef __DJGPP__
  36. #include <sys/stat.h>    /* for stat bits */
  37. #endif
  38.  
  39. /* The very first search is for texmf.cnf, called when someone tries to
  40.    initialize the TFM path or whatever.  init_path calls kpse_cnf_get
  41.    which calls kpse_all_path_search to find all the texmf.cnf's.  We
  42.    need to do various special things in this case, since we obviously
  43.    don't yet have the configuration files when we're searching for the
  44.    configuration files.  */
  45. static boolean first_search = true;
  46.  
  47.  
  48.  
  49. /* This function is called after every search (except the first, since
  50.    we definitely want to allow enabling the logging in texmf.cnf) to
  51.    record the filename(s) found in $TEXMFLOG.  */
  52.  
  53. static void
  54. log_search P1C(str_list_type, filenames)
  55. {
  56.   static FILE *log_file = NULL;
  57.   static boolean first_time = true; /* Need to open the log file?  */
  58.   
  59.   if (first_time) {
  60.     /* Get name from either envvar or config file.  */
  61.     string log_name = kpse_var_value ("TEXMFLOG");
  62.     first_time = false;
  63.     if (log_name) {
  64.       log_file = fopen (log_name, FOPEN_A_MODE);
  65.       if (!log_file)
  66.         perror (log_name);
  67.       free (log_name);
  68.     }
  69.   }
  70.  
  71.   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH) || log_file) {
  72.     unsigned e;
  73.  
  74.     /* FILENAMES should never be null, but safety doesn't hurt.  */
  75.     for (e = 0; e < STR_LIST_LENGTH (filenames) && STR_LIST_ELT (filenames, e);
  76.          e++) {
  77.       string filename = STR_LIST_ELT (filenames, e);
  78.  
  79.       /* Only record absolute filenames, for privacy.  */
  80.       if (log_file && kpse_absolute_p (filename, false))
  81.         fprintf (log_file, "%lu %s\n", (long unsigned) time (NULL),
  82.                  filename);
  83.  
  84.       /* And show them online, if debugging.  We've already started
  85.          the debugging line in `search', where this is called, so
  86.          just print the filename here, don't use DEBUGF.  */
  87.       if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  88.         fputs (filename, stderr);
  89.     }
  90.   }
  91. }
  92.  
  93. /* Concatenate each element in DIRS with NAME (assume each ends with a
  94.    /, to save time).  If SEARCH_ALL is false, return the first readable
  95.    regular file.  Else continue to search for more.  In any case, if
  96.    none, return a list containing just NULL.
  97.  
  98.    We keep a single buffer for the potential filenames and reallocate
  99.    only when necessary.  I'm not sure it's noticeably faster, but it
  100.    does seem cleaner.  (We do waste a bit of space in the return
  101.    value, though, since we don't shrink it to the final size returned.)  */
  102.  
  103. #define INIT_ALLOC 75  /* Doesn't much matter what this number is.  */
  104.  
  105. static str_list_type
  106. dir_list_search P3C(str_llist_type *, dirs,  const_string, name,
  107.                     boolean, search_all)
  108. {
  109.   str_llist_elt_type *elt;
  110.   str_list_type ret;
  111.   unsigned name_len = strlen (name);
  112.   unsigned allocated = INIT_ALLOC;
  113.   string potential = xmalloc (allocated);
  114.  
  115.   ret = str_list_init ();
  116.   
  117.   for (elt = *dirs; elt; elt = STR_LLIST_NEXT (*elt))
  118.     {
  119.       const_string dir = STR_LLIST (*elt);
  120.       unsigned dir_len = strlen (dir);
  121.       
  122.       while (dir_len + name_len + 1 > allocated)
  123.         {
  124.           allocated += allocated;
  125.           XRETALLOC (potential, allocated, char);
  126.         }
  127.       
  128.       strcpy (potential, dir);
  129.       strcat (potential, name);
  130.       
  131.       if (kpse_readable_file (potential))
  132.         { 
  133.           str_list_add (&ret, potential);
  134.           
  135.           /* Move this element towards the top of the list.  */
  136.           str_llist_float (dirs, elt);
  137.           
  138.           /* If caller only wanted one file returned, no need to
  139.              terminate the list with NULL; the caller knows to only look
  140.              at the first element.  */
  141.           if (!search_all)
  142.             return ret;
  143.  
  144.           /* Start new filename.  */
  145.           allocated = INIT_ALLOC;
  146.           potential = xmalloc (allocated);
  147.         }
  148.     }
  149.   
  150.   /* If we get here, either we didn't find any files, or we were finding
  151.      all the files.  But we're done with the last filename, anyway.  */
  152.   free (potential);
  153.   
  154.   return ret;
  155. }
  156.  
  157. /* This is called when NAME is absolute or explicitly relative; if it's
  158.    readable, return (a list containing) it; otherwise, return NULL.  */
  159.  
  160. static str_list_type
  161. absolute_search P1C(string, name)
  162. {
  163.   str_list_type ret_list;
  164.   string found = kpse_readable_file (name);
  165.   
  166.   /* Some old compilers can't initialize structs.  */
  167.   ret_list = str_list_init ();
  168.  
  169.   /* If NAME wasn't found, free the expansion.  */
  170.   if (name != found)
  171.     free (name);
  172.  
  173.   /* Add `found' to the return list even if it's null; that tells
  174.      the caller we didn't find anything.  */
  175.   str_list_add (&ret_list, found);
  176.   
  177.   return ret_list;
  178. }
  179.  
  180. /* This is the hard case -- look for NAME in PATH.  If ALL is false,
  181.    return the first file found.  Otherwise, search all elements of PATH.  */
  182.  
  183. static str_list_type
  184. path_search P4C(const_string, path,  string, name,
  185.                 boolean, must_exist,  boolean, all)
  186. {
  187.   string elt;
  188.   str_list_type ret_list;
  189.   boolean done = false;
  190.   ret_list = str_list_init (); /* some compilers lack struct initialization */
  191.  
  192.   for (elt = kpse_path_element (path); !done && elt;
  193.        elt = kpse_path_element (NULL)) {
  194.     str_list_type *found;
  195.     boolean allow_disk_search = true;
  196.  
  197.     if (*elt == '!' && *(elt + 1) == '!') {
  198.       /* Those magic leading chars in a path element means don't search the
  199.          disk for this elt.  And move past the magic to get to the name.  */
  200.       allow_disk_search = false;
  201.       elt += 2;
  202.     }
  203.  
  204.     /* Do not touch the device if present */
  205.     if (NAME_BEGINS_WITH_DEVICE (elt)) {
  206.       while (IS_DIR_SEP (*(elt + 2)) && IS_DIR_SEP (*(elt + 3))) {
  207.     *(elt + 2) = *(elt + 1);
  208.     *(elt + 1) = *elt;
  209.     elt++;
  210.       }
  211.     } else {
  212.       /* We never want to search the whole disk.  */
  213.       while (IS_DIR_SEP (*elt) && IS_DIR_SEP (*(elt + 1)))
  214.         elt++;
  215.     }
  216.     
  217.     /* Try ls-R, unless we're searching for texmf.cnf.  Our caller
  218.        (search), also tests first_search, and does the resetting.  */
  219.     found = first_search ? NULL : kpse_db_search (name, elt, all);
  220.  
  221.     /* Search the filesystem if (1) the path spec allows it, and either
  222.          (2a) we are searching for texmf.cnf ; or
  223.          (2b) no db exists; or 
  224.          (2c) no db's are relevant to this elt; or
  225.          (3) MUST_EXIST && NAME was not in the db.
  226.        In (2*), `found' will be NULL.
  227.        In (3),  `found' will be an empty list. */
  228.     if (allow_disk_search && (!found || (must_exist && !STR_LIST (*found)))) {
  229.       str_llist_type *dirs = kpse_element_dirs (elt);
  230.       if (dirs && *dirs) {
  231.         if (!found)
  232.           found = XTALLOC1 (str_list_type);
  233.         *found = dir_list_search (dirs, name, all);
  234.       }
  235.     }
  236.  
  237.     /* Did we find anything anywhere?  */
  238.     /* Braces added ... Klaus Gebhardt, 19.04.1999 */
  239.     if (found && STR_LIST (*found))
  240.       {
  241.     if (all)
  242.       str_list_concat (&ret_list, *found);
  243.     else
  244.       {
  245.         str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
  246.         done = true;
  247.       }
  248.       }
  249.  
  250.     /* Free the list space, if any (but not the elements).  */
  251.     if (found) {
  252.       str_list_free (found);
  253.       free (found);
  254.     }
  255.   }
  256.  
  257.   /* Free the expanded name we were passed.  It can't be in the return
  258.      list, since the path directories got unconditionally prepended.  */
  259.   free (name);
  260.   
  261.   return ret_list;
  262. }      
  263.  
  264. /* Search PATH for ORIGINAL_NAME.  If ALL is false, or ORIGINAL_NAME is
  265.    absolute_p, check ORIGINAL_NAME itself.  Otherwise, look at each
  266.    element of PATH for the first readable ORIGINAL_NAME.
  267.    
  268.    Always return a list; if no files are found, the list will
  269.    contain just NULL.  If ALL is true, the list will be
  270.    terminated with NULL.  */
  271.  
  272. static string *
  273. search P4C(const_string, path,  const_string, original_name,
  274.            boolean, must_exist,  boolean, all)
  275. {
  276.   str_list_type ret_list;
  277.   string name;
  278.   boolean absolute_p;
  279.  
  280. #ifdef __DJGPP__
  281.   /* We will use `stat' heavily, so let's request for
  282.      the fastest possible version of `stat', by telling
  283.      it what members of struct stat do we really need.
  284.  
  285.      We need to set this on each call because this is a
  286.      library function; the caller might need other options
  287.      from `stat'.  Thus save the flags and restore them
  288.      before exit.
  289.  
  290.      This call tells `stat' that we do NOT need to recognize
  291.      executable files (neither by an extension nor by a magic
  292.      signature); that we do NOT need time stamp of root directories;
  293.      and that we do NOT need the write access bit in st_mode.
  294.  
  295.      Note that `kpse_set_progname' needs the EXEC bits,
  296.      but it was already called by the time we get here.  */
  297.   unsigned short save_djgpp_flags  = _djstat_flags;
  298.  
  299.   _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
  300.           | _STAT_ROOT_TIME | _STAT_WRITEBIT;
  301. #endif
  302.  
  303.   /* Make a leading ~ count as an absolute filename, and expand $FOO's.  */
  304.   name = kpse_expand (original_name);
  305.   
  306.   /* If the first name is absolute or explicitly relative, no need to
  307.      consider PATH at all.  */
  308.   absolute_p = kpse_absolute_p (name, true);
  309.   
  310.   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  311.     DEBUGF4 ("start search(file=%s, must_exist=%d, find_all=%d, path=%s).\n",
  312.              name, must_exist, all, path);
  313.  
  314.   /* Find the file(s). */
  315.   ret_list = absolute_p ? absolute_search (name)
  316.                         : path_search (path, name, must_exist, all);
  317.   
  318.   /* Append NULL terminator if we didn't find anything at all, or we're
  319.      supposed to find ALL and the list doesn't end in NULL now.  */
  320.   if (STR_LIST_LENGTH (ret_list) == 0
  321.       || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
  322.     str_list_add (&ret_list, NULL);
  323.  
  324.   /* The very first search is for texmf.cnf.  We can't log that, since
  325.      we want to allow setting TEXMFLOG in texmf.cnf.  */
  326.   if (first_search) {
  327.     first_search = false;
  328.   } else {
  329.     /* Record the filenames we found, if desired.  And wrap them in a
  330.        debugging line if we're doing that.  */
  331.     if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  332.       DEBUGF1 ("search(%s) =>", original_name);
  333.     log_search (ret_list);
  334.     if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
  335.       putc ('\n', stderr);
  336.   }  
  337.  
  338. #ifdef __DJGPP__
  339.   /* Undo any side effects.  */
  340.   _djstat_flags = save_djgpp_flags;
  341. #endif
  342.  
  343.   return STR_LIST (ret_list);
  344. }
  345.  
  346. /* Search PATH for the first NAME.  */
  347.  
  348. string
  349. kpse_path_search P3C(const_string, path,  const_string, name,
  350.                      boolean, must_exist)
  351. {
  352.   static string *ret_list = 0;
  353.  
  354.   if (ret_list)
  355.     {
  356.       free (ret_list);
  357.       ret_list = 0;  /* Don't let an interrupt in search() cause trouble */
  358.     }
  359.  
  360.   ret_list = search (path, name, must_exist, false);
  361.  
  362.   return *ret_list;  /* Freeing this is caller's responsibility */
  363. }
  364.  
  365.  
  366. /* Search all elements of PATH for files named NAME.  Not sure if it's
  367.    right to assert `must_exist' here, but it suffices now.  */
  368.  
  369. string *
  370. kpse_all_path_search P2C(const_string, path,  const_string, name)
  371. {
  372.   string *ret = search (path, name, true, true);
  373.   return ret;
  374. }
  375.  
  376. #ifdef TEST
  377.  
  378. void
  379. test_path_search (const_string path, const_string file)
  380. {
  381.   string answer;
  382.   string *answer_list;
  383.   
  384.   printf ("\nSearch %s for %s:\t", path, file);
  385.   answer = kpse_path_search (path, file);
  386.   puts (answer ? answer : "(nil)");
  387.  
  388.   printf ("Search %s for all %s:\t", path, file);
  389.   answer_list = kpse_all_path_search (path, file);
  390.   putchar ('\n');
  391.   while (*answer_list)
  392.     {
  393.       putchar ('\t');
  394.       puts (*answer_list);
  395.       answer_list++;
  396.     }
  397. }
  398.  
  399. #define TEXFONTS "/usr/local/lib/tex/fonts"
  400.  
  401. int
  402. main ()
  403. {
  404.   /* All lists end with NULL.  */
  405.   test_path_search (".", "nonexistent");
  406.   test_path_search (".", "/nonexistent");
  407.   test_path_search ("/k" ENV_SEP_STRING ".", "kpathsea.texi");
  408.   test_path_search ("/k" ENV_SEP_STRING ".", "/etc/fstab");
  409.   test_path_search ("." ENV_SEP_STRING TEXFONTS "//", "cmr10.tfm");
  410.   test_path_search ("." ENV_SEP_STRING TEXFONTS "//", "logo10.tfm");
  411.   test_path_search (TEXFONTS "//times" ENV_SEP_STRING "."
  412.                     ENV_SEP_STRING ENV_SEP_STRING, "ptmr.vf");
  413.   test_path_search (TEXFONTS "/adobe//" ENV_SEP_STRING
  414.                     "/usr/local/src/TeX+MF/typefaces//", "plcr.pfa");
  415.   
  416.   test_path_search ("~karl", ".bashrc");
  417.   test_path_search ("/k", "~karl/.bashrc");
  418.  
  419.   xputenv ("NONEXIST", "nonexistent");
  420.   test_path_search (".", "$NONEXISTENT");
  421.   xputenv ("KPATHSEA", "kpathsea");
  422.   test_path_search ("/k" ENV_SEP_STRING ".", "$KPATHSEA.texi");  
  423.   test_path_search ("/k" ENV_SEP_STRING ".", "${KPATHSEA}.texi");  
  424.   test_path_search ("$KPATHSEA" ENV_SEP_STRING ".", "README");  
  425.   test_path_search ("." ENV_SEP_STRING "$KPATHSEA", "README");  
  426.   
  427.   return 0;
  428. }
  429.  
  430. #endif /* TEST */
  431.  
  432.  
  433. /*
  434. Local variables:
  435. test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a"
  436. End:
  437. */
  438.