Back to index

tetex-bin  3.0
pathsearch.c
Go to the documentation of this file.
00001 /* pathsearch.c: look up a filename in a path.
00002 
00003 Copyright (C) 1993, 94, 95, 97 Karl Berry.
00004 Copyright (C) 1997, 98, 99, 2000 Olaf Weber.
00005 
00006 This library is free software; you can redistribute it and/or
00007 modify it under the terms of the GNU Library General Public
00008 License as published by the Free Software Foundation; either
00009 version 2 of the License, or (at your option) any later version.
00010 
00011 This library is distributed in the hope that it will be useful,
00012 but WITHOUT ANY WARRANTY; without even the implied warranty of
00013 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
00014 Library General Public License for more details.
00015 
00016 You should have received a copy of the GNU Library General Public
00017 License along with this library; if not, write to the Free Software
00018 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
00019 
00020 #include <kpathsea/config.h>
00021 #include <kpathsea/c-pathch.h>
00022 #include <kpathsea/c-fopen.h>
00023 #include <kpathsea/absolute.h>
00024 #include <kpathsea/expand.h>
00025 #include <kpathsea/db.h>
00026 #include <kpathsea/pathsearch.h>
00027 #include <kpathsea/readable.h>
00028 #include <kpathsea/str-list.h>
00029 #include <kpathsea/str-llist.h>
00030 #include <kpathsea/variable.h>
00031 
00032 #include <time.h> /* for `time' */
00033 
00034 #ifdef __DJGPP__
00035 #include <sys/stat.h>       /* for stat bits */
00036 #endif
00037 
00038 /* The very first search is for texmf.cnf, called when someone tries to
00039    initialize the TFM path or whatever.  init_path calls kpse_cnf_get
00040    which calls kpse_all_path_search to find all the texmf.cnf's.  We
00041    need to do various special things in this case, since we obviously
00042    don't yet have the configuration files when we're searching for the
00043    configuration files.  */
00044 static boolean first_search = true;
00045 
00046 
00047 
00048 /* This function is called after every search (except the first, since
00049    we definitely want to allow enabling the logging in texmf.cnf) to
00050    record the filename(s) found in $TEXMFLOG.  */
00051 
00052 static void
00053 log_search P1C(str_list_type, filenames)
00054 {
00055   static FILE *log_file = NULL;
00056   static boolean first_time = true; /* Need to open the log file?  */
00057   
00058   if (first_time) {
00059     /* Get name from either envvar or config file.  */
00060     string log_name = kpse_var_value ("TEXMFLOG");
00061     first_time = false;
00062     if (log_name) {
00063       log_file = fopen (log_name, FOPEN_A_MODE);
00064       if (!log_file)
00065         perror (log_name);
00066       free (log_name);
00067     }
00068   }
00069 
00070   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH) || log_file) {
00071     unsigned e;
00072 
00073     /* FILENAMES should never be null, but safety doesn't hurt.  */
00074     for (e = 0; e < STR_LIST_LENGTH (filenames) && STR_LIST_ELT (filenames, e);
00075          e++) {
00076       string filename = STR_LIST_ELT (filenames, e);
00077 
00078       /* Only record absolute filenames, for privacy.  */
00079       if (log_file && kpse_absolute_p (filename, false))
00080         fprintf (log_file, "%lu %s\n", (long unsigned) time (NULL),
00081                  filename);
00082 
00083       /* And show them online, if debugging.  We've already started
00084          the debugging line in `search', where this is called, so
00085          just print the filename here, don't use DEBUGF.  */
00086       if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
00087         putc (' ', stderr);
00088         fputs (filename, stderr);
00089       }
00090     }
00091   }
00092 }
00093 
00094 /* Concatenate each element in DIRS with NAME (assume each ends with a
00095    /, to save time).  If SEARCH_ALL is false, return the first readable
00096    regular file.  Else continue to search for more.  In any case, if
00097    none, return a list containing just NULL.
00098 
00099    We keep a single buffer for the potential filenames and reallocate
00100    only when necessary.  I'm not sure it's noticeably faster, but it
00101    does seem cleaner.  (We do waste a bit of space in the return
00102    value, though, since we don't shrink it to the final size returned.)  */
00103 
00104 #define INIT_ALLOC 75  /* Doesn't much matter what this number is.  */
00105 
00106 static str_list_type
00107 dir_list_search P3C(str_llist_type *, dirs,  const_string, name,
00108                     boolean, search_all)
00109 {
00110   str_llist_elt_type *elt;
00111   str_list_type ret;
00112   unsigned name_len = strlen (name);
00113   unsigned allocated = INIT_ALLOC;
00114   string potential = (string)xmalloc (allocated);
00115 
00116   ret = str_list_init ();
00117   
00118   for (elt = *dirs; elt; elt = STR_LLIST_NEXT (*elt))
00119     {
00120       const_string dir = STR_LLIST (*elt);
00121       unsigned dir_len = strlen (dir);
00122       
00123       while (dir_len + name_len + 1 > allocated)
00124         {
00125           allocated += allocated;
00126           XRETALLOC (potential, allocated, char);
00127         }
00128       
00129       strcpy (potential, dir);
00130       strcat (potential, name);
00131       
00132       if (kpse_readable_file (potential))
00133         { 
00134           str_list_add (&ret, potential);
00135           
00136           /* Move this element towards the top of the list.  */
00137           str_llist_float (dirs, elt);
00138           
00139           /* If caller only wanted one file returned, no need to
00140              terminate the list with NULL; the caller knows to only look
00141              at the first element.  */
00142           if (!search_all)
00143             return ret;
00144 
00145           /* Start new filename.  */
00146           allocated = INIT_ALLOC;
00147           potential = (string)xmalloc (allocated);
00148         }
00149     }
00150   
00151   /* If we get here, either we didn't find any files, or we were finding
00152      all the files.  But we're done with the last filename, anyway.  */
00153   free (potential);
00154   
00155   return ret;
00156 }
00157 
00158 static str_list_type
00159 dir_list_search_list P3C(str_llist_type *, dirs,  const_string*, names,
00160                          boolean, search_all)
00161 {
00162   str_llist_elt_type *elt;
00163   str_list_type ret;
00164   unsigned allocated = INIT_ALLOC;
00165   string potential = XTALLOC(allocated, char);
00166 
00167   ret = str_list_init ();
00168 
00169   for (elt = *dirs; elt; elt = STR_LLIST_NEXT(*elt)) {
00170       const_string dir = STR_LLIST (*elt);
00171       unsigned dir_len = strlen (dir);
00172       int i;
00173       
00174       for (i = 0; names[i]; i++) {
00175           const_string name = names[i];
00176           unsigned name_len;
00177 
00178           /* Don't bother with absolute & explicit relative. */
00179           if (kpse_absolute_p(name, true))
00180               continue;
00181           
00182           name_len = strlen(name);
00183 
00184           while (dir_len + name_len + 1 > allocated) {
00185               allocated += allocated;
00186               XRETALLOC (potential, allocated, char);
00187           }
00188 
00189           strcpy (potential, dir);
00190           strcat (potential+dir_len, name);
00191 
00192           if (kpse_readable_file (potential)) {
00193               str_list_add (&ret, potential);
00194 
00195               /* Move this element towards the top of the list.  */
00196               str_llist_float (dirs, elt);
00197 
00198               /* If caller only wanted one file returned, no need to
00199                  terminate the list with NULL; the caller knows to only look
00200                  at the first element.  */
00201               if (!search_all)
00202                   return ret;
00203 
00204               /* Start new filename. */
00205               allocated = INIT_ALLOC;
00206               potential = XTALLOC(allocated, char);
00207           }
00208       }
00209   }
00210 
00211   /* If we get here, either we didn't find any files, or we were finding
00212      all the files.  But we're done with the last filename, anyway.  */
00213   free (potential);
00214   
00215   return ret;
00216 }
00217 
00218 /* This is called when NAME is absolute or explicitly relative; if it's
00219    readable, return (a list containing) it; otherwise, return NULL.  */
00220 
00221 static str_list_type
00222 absolute_search P1C(string, name)
00223 {
00224   str_list_type ret_list;
00225   string found = kpse_readable_file (name);
00226   
00227   /* Some old compilers can't initialize structs.  */
00228   ret_list = str_list_init ();
00229 
00230   /* If NAME wasn't found, free the expansion.  */
00231   if (name != found)
00232     free (name);
00233 
00234   /* Add `found' to the return list even if it's null; that tells
00235      the caller we didn't find anything.  */
00236   str_list_add (&ret_list, found);
00237   
00238   return ret_list;
00239 }
00240 
00241 /* This is the hard case -- look for NAME in PATH.  If ALL is false,
00242    return the first file found.  Otherwise, search all elements of PATH.  */
00243 
00244 static str_list_type
00245 path_search P4C(const_string, path,  string, name,
00246                 boolean, must_exist,  boolean, all)
00247 {
00248   string elt;
00249   str_list_type ret_list;
00250   boolean done = false;
00251   ret_list = str_list_init (); /* some compilers lack struct initialization */
00252 
00253   for (elt = kpse_path_element (path); !done && elt;
00254        elt = kpse_path_element (NULL)) {
00255     str_list_type *found;
00256     boolean allow_disk_search = true;
00257 
00258     if (*elt == '!' && *(elt + 1) == '!') {
00259       /* Those magic leading chars in a path element means don't search the
00260          disk for this elt.  And move past the magic to get to the name.  */
00261       allow_disk_search = false;
00262       elt += 2;
00263     }
00264 
00265     /* See elt-dirs.c for side effects of this function */
00266     kpse_normalize_path(elt);
00267     
00268     /* Try ls-R, unless we're searching for texmf.cnf.  Our caller
00269        (search), also tests first_search, and does the resetting.  */
00270     found = first_search ? NULL : kpse_db_search (name, elt, all);
00271 
00272     /* Search the filesystem if (1) the path spec allows it, and either
00273          (2a) we are searching for texmf.cnf ; or
00274          (2b) no db exists; or 
00275          (2c) no db's are relevant to this elt; or
00276          (3) MUST_EXIST && NAME was not in the db.
00277        In (2*), `found' will be NULL.
00278        In (3),  `found' will be an empty list. */
00279     if (allow_disk_search && (!found || (must_exist && !STR_LIST (*found)))) {
00280       str_llist_type *dirs = kpse_element_dirs (elt);
00281       if (dirs && *dirs) {
00282         if (!found)
00283           found = XTALLOC1 (str_list_type);
00284         *found = dir_list_search (dirs, name, all);
00285       }
00286     }
00287     
00288     /* Did we find anything anywhere?  */
00289     if (found && STR_LIST (*found))
00290       if (all)
00291         str_list_concat (&ret_list, *found);
00292       else {
00293         str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
00294         done = true;
00295       }
00296 
00297     /* Free the list space, if any (but not the elements).  */
00298     if (found) {
00299       str_list_free (found);
00300       free (found);
00301     }
00302   }
00303 
00304   /* Free the expanded name we were passed.  It can't be in the return
00305      list, since the path directories got unconditionally prepended.  */
00306   free (name);
00307   
00308   return ret_list;
00309 }      
00310 
00311 /* Search PATH for ORIGINAL_NAME.  If ALL is false, or ORIGINAL_NAME is
00312    absolute_p, check ORIGINAL_NAME itself.  Otherwise, look at each
00313    element of PATH for the first readable ORIGINAL_NAME.
00314    
00315    Always return a list; if no files are found, the list will
00316    contain just NULL.  If ALL is true, the list will be
00317    terminated with NULL.  */
00318 
00319 static string *
00320 search P4C(const_string, path,  const_string, original_name,
00321            boolean, must_exist,  boolean, all)
00322 {
00323   str_list_type ret_list;
00324   string name;
00325   boolean absolute_p;
00326 
00327 #ifdef __DJGPP__
00328   /* We will use `stat' heavily, so let's request for
00329      the fastest possible version of `stat', by telling
00330      it what members of struct stat do we really need.
00331 
00332      We need to set this on each call because this is a
00333      library function; the caller might need other options
00334      from `stat'.  Thus save the flags and restore them
00335      before exit.
00336 
00337      This call tells `stat' that we do NOT need to recognize
00338      executable files (neither by an extension nor by a magic
00339      signature); that we do NOT need time stamp of root directories;
00340      and that we do NOT need the write access bit in st_mode.
00341 
00342      Note that `kpse_set_progname' needs the EXEC bits,
00343      but it was already called by the time we get here.  */
00344   unsigned short save_djgpp_flags  = _djstat_flags;
00345 
00346   _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
00347                 | _STAT_ROOT_TIME | _STAT_WRITEBIT;
00348 #endif
00349 
00350   /* Make a leading ~ count as an absolute filename, and expand $FOO's.  */
00351   name = kpse_expand (original_name);
00352   
00353   /* If the first name is absolute or explicitly relative, no need to
00354      consider PATH at all.  */
00355   absolute_p = kpse_absolute_p (name, true);
00356   
00357   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
00358     DEBUGF4 ("start search(file=%s, must_exist=%d, find_all=%d, path=%s).\n",
00359              name, must_exist, all, path);
00360 
00361   /* Find the file(s). */
00362   ret_list = absolute_p ? absolute_search (name)
00363                         : path_search (path, name, must_exist, all);
00364   
00365   /* Append NULL terminator if we didn't find anything at all, or we're
00366      supposed to find ALL and the list doesn't end in NULL now.  */
00367   if (STR_LIST_LENGTH (ret_list) == 0
00368       || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
00369     str_list_add (&ret_list, NULL);
00370 
00371   /* The very first search is for texmf.cnf.  We can't log that, since
00372      we want to allow setting TEXMFLOG in texmf.cnf.  */
00373   if (first_search) {
00374     first_search = false;
00375   } else {
00376     /* Record the filenames we found, if desired.  And wrap them in a
00377        debugging line if we're doing that.  */
00378     if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
00379       DEBUGF1 ("search(%s) =>", original_name);
00380     log_search (ret_list);
00381     if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
00382       putc ('\n', stderr);
00383   }  
00384 
00385 #ifdef __DJGPP__
00386   /* Undo any side effects.  */
00387   _djstat_flags = save_djgpp_flags;
00388 #endif
00389 
00390   return STR_LIST (ret_list);
00391 }
00392 
00393 /* Search PATH for NAMES.
00394 
00395    Always return a list; if no files are found, the list will
00396    contain just NULL.  If ALL is true, the list will be
00397    terminated with NULL.  */
00398 static string *
00399 search_list P4C(const_string, path,  const_string*, names,
00400                 boolean, must_exist,  boolean, all)
00401 {
00402   str_list_type ret_list;
00403   const_string* namep;
00404   string elt;
00405   boolean done = false;
00406   boolean all_absolute = true;
00407 
00408 #ifdef __DJGPP__
00409   /* We will use `stat' heavily, so let's request for
00410      the fastest possible version of `stat', by telling
00411      it what members of struct stat do we really need.
00412 
00413      We need to set this on each call because this is a
00414      library function; the caller might need other options
00415      from `stat'.  Thus save the flags and restore them
00416      before exit.
00417 
00418      This call tells `stat' that we do NOT need to recognize
00419      executable files (neither by an extension nor by a magic
00420      signature); that we do NOT need time stamp of root directories;
00421      and that we do NOT need the write access bit in st_mode.
00422 
00423      Note that `kpse_set_progname' needs the EXEC bits,
00424      but it was already called by the time we get here.  */
00425   unsigned short save_djgpp_flags  = _djstat_flags;
00426 
00427   _djstat_flags = _STAT_EXEC_MAGIC | _STAT_EXEC_EXT
00428                 | _STAT_ROOT_TIME | _STAT_WRITEBIT;
00429 #endif
00430 
00431   ret_list = str_list_init();
00432 
00433   if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
00434     DEBUGF1  ("start search(files=[%s", *names);
00435     for (namep = names+1; *namep != NULL; namep++) {
00436       fputc(' ', stderr);
00437       fputs(*namep, stderr);
00438     }
00439     fprintf (stderr, "], must_exist=%d, find_all=%d, path=%s).\n",
00440              must_exist, all, path);
00441   }
00442   
00443   /* No need to do any expansion on names.  */
00444 
00445   /* First catch any absolute or explicit relative names. */
00446   for (namep = names; *namep; namep++) {
00447       if (kpse_absolute_p(*namep, true)) {
00448           if (kpse_readable_file(*namep)) {
00449           str_list_add(&ret_list, xstrdup(*namep));
00450           /* I know, I know... */
00451           if (!all)
00452               goto out;
00453           }
00454       } else {
00455           all_absolute = false;
00456       }
00457   }
00458   /* Shortcut: if we were only given absolute/explicit relative names,
00459      we can skip the rest.  Typically, if one name is absolute, they
00460      all are, because our caller derived them from each other. */
00461   if (all_absolute)
00462       goto out;
00463 
00464   /* Look at each path element in turn. */
00465   for (elt = kpse_path_element (path); !done && elt;
00466        elt = kpse_path_element (NULL))
00467   {
00468     str_list_type *found;
00469     boolean allow_disk_search = true;
00470     if (elt[0] == '!' && elt[1] == '!') {
00471       /* !! magic -> disallow disk searches. */
00472       allow_disk_search = false;
00473       elt += 2;
00474     }
00475 
00476     /* See elt-dirs.c for side effects of this function. */
00477     kpse_normalize_path(elt);
00478 
00479     /* Try ls-R, unless we're searching for texmf.cnf. */
00480     found = first_search ? NULL : kpse_db_search_list(names, elt, all);
00481 
00482     /* Search the filesystem if (1) the path spec allows it, and either
00483          (2a) we are searching for texmf.cnf ; or
00484          (2b) no db exists; or 
00485          (2c) no db's are relevant to this elt; or
00486          (3) MUST_EXIST && NAME was not in the db.
00487        In (2*), `found' will be NULL.
00488        In (3),  `found' will be an empty list. */
00489     if (allow_disk_search && (!found || (must_exist && !STR_LIST(*found)))) {
00490       str_llist_type *dirs = kpse_element_dirs (elt);
00491       if (dirs && *dirs) {
00492         if (!found)
00493           found = XTALLOC1 (str_list_type);
00494         *found = dir_list_search_list (dirs, names, all);
00495       }
00496     }
00497 
00498     /* Did we find anything? */
00499     if (found && STR_LIST (*found)) {
00500       if (all) {
00501         str_list_concat (&ret_list, *found);
00502       } else {
00503         str_list_add (&ret_list, STR_LIST_ELT (*found, 0));
00504         done = true;
00505       }
00506     }
00507   }
00508 
00509  out:
00510   if (STR_LIST_LENGTH (ret_list) == 0
00511       || (all && STR_LIST_LAST_ELT (ret_list) != NULL))
00512     str_list_add (&ret_list, NULL);
00513 
00514   if (first_search) {
00515     first_search = false;
00516   } else {
00517     /* Record the filenames we found, if desired.  And wrap them in a
00518        debugging line if we're doing that.  */
00519     if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) {
00520       DEBUGF1 ("search([%s", *names);
00521       for (namep = names+1; *namep != NULL; namep++) {
00522         fputc(' ', stderr);
00523         fputs(*namep, stderr);
00524       }
00525       fputs ("]) =>", stderr);
00526     }
00527     log_search (ret_list);
00528     if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH))
00529       putc ('\n', stderr);
00530   }
00531 
00532 #ifdef __DJGPP__
00533   /* Undo any side effects.  */
00534   _djstat_flags = save_djgpp_flags;
00535 #endif
00536   
00537   return STR_LIST (ret_list);
00538 }
00539 
00540 /* Search PATH for the first NAME.  */
00541 
00542 string
00543 kpse_path_search P3C(const_string, path,  const_string, name,
00544                      boolean, must_exist)
00545 {
00546   string *ret_list = search (path, name, must_exist, false);
00547   string ret = *ret_list;
00548   free (ret_list);
00549   return ret;
00550 }
00551 
00552 /* Search PATH for the first occurence of one of the NAMES. */
00553 
00554 string
00555 kpse_path_search_list P3C(const_string, path,  const_string*, names,
00556                           boolean, must_exist)
00557 {
00558   string *ret_list = search_list (path, names, must_exist, false);
00559   string ret = *ret_list;
00560   free (ret_list);
00561   return ret;
00562 }    
00563 
00564 /* Search all elements of PATH for files named NAME.  Not sure if it's
00565    right to assert `must_exist' here, but it suffices now.  */
00566 
00567 string *
00568 kpse_all_path_search P2C(const_string, path,  const_string, name)
00569 {
00570   string *ret = search (path, name, true, true);
00571   return ret;
00572 }
00573 
00574 string *
00575 kpse_all_path_search_list P2C(const_string, path,  const_string*, names)
00576 {
00577     string *ret = search_list (path, names, true, true);
00578     return ret;
00579 }
00580 
00581 #ifdef TEST
00582 
00583 void
00584 test_path_search (const_string path, const_string file)
00585 {
00586   string answer;
00587   string *answer_list;
00588   
00589   printf ("\nSearch %s for %s:\t", path, file);
00590   answer = kpse_path_search (path, file);
00591   puts (answer ? answer : "(nil)");
00592 
00593   printf ("Search %s for all %s:\t", path, file);
00594   answer_list = kpse_all_path_search (path, file);
00595   putchar ('\n');
00596   while (*answer_list)
00597     {
00598       putchar ('\t');
00599       puts (*answer_list);
00600       answer_list++;
00601     }
00602 }
00603 
00604 #define TEXFONTS "/usr/local/lib/tex/fonts"
00605 
00606 int
00607 main ()
00608 {
00609   /* All lists end with NULL.  */
00610   test_path_search (".", "nonexistent");
00611   test_path_search (".", "/nonexistent");
00612   test_path_search ("/k" ENV_SEP_STRING ".", "kpathsea.texi");
00613   test_path_search ("/k" ENV_SEP_STRING ".", "/etc/fstab");
00614   test_path_search ("." ENV_SEP_STRING TEXFONTS "//", "cmr10.tfm");
00615   test_path_search ("." ENV_SEP_STRING TEXFONTS "//", "logo10.tfm");
00616   test_path_search (TEXFONTS "//times" ENV_SEP_STRING "."
00617                     ENV_SEP_STRING ENV_SEP_STRING, "ptmr.vf");
00618   test_path_search (TEXFONTS "/adobe//" ENV_SEP_STRING
00619                     "/usr/local/src/TeX+MF/typefaces//", "plcr.pfa");
00620   
00621   test_path_search ("~karl", ".bashrc");
00622   test_path_search ("/k", "~karl/.bashrc");
00623 
00624   xputenv ("NONEXIST", "nonexistent");
00625   test_path_search (".", "$NONEXISTENT");
00626   xputenv ("KPATHSEA", "kpathsea");
00627   test_path_search ("/k" ENV_SEP_STRING ".", "$KPATHSEA.texi");  
00628   test_path_search ("/k" ENV_SEP_STRING ".", "${KPATHSEA}.texi");  
00629   test_path_search ("$KPATHSEA" ENV_SEP_STRING ".", "README");  
00630   test_path_search ("." ENV_SEP_STRING "$KPATHSEA", "README");  
00631   
00632   return 0;
00633 }
00634 
00635 #endif /* TEST */
00636 
00637 
00638 /*
00639 Local variables:
00640 test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a"
00641 End:
00642 */