home *** CD-ROM | disk | FTP | other *** search
/ The Datafile PD-CD 3 / PDCD_3.iso / tex / texsrc1 / Src / lib / c / pathsrch < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-21  |  12.6 KB  |  437 lines

  1. /* pathsrch.c: look for files based on paths, i.e., colon-separated
  2.    lists of directories.  
  3.    
  4.    Perhaps we should allow % specifiers in the paths for the resolution, etc.
  5.  
  6.    This is a RISC OS ONLY version!
  7.    Things not needed in RISC OS are cut out
  8.  
  9. Copyright (C) 1992 Free Software Foundation, Inc.
  10.  
  11. This program is free software; you can redistribute it and/or modify
  12. it under the terms of the GNU General Public License as published by
  13. the Free Software Foundation; either version 2, or (at your option)
  14. any later version.
  15.  
  16. This program is distributed in the hope that it will be useful,
  17. but WITHOUT ANY WARRANTY; without even the implied warranty of
  18. MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  19. GNU General Public License for more details.
  20.  
  21. You should have received a copy of the GNU General Public License
  22. along with this program; if not, write to the Free Software
  23. Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  24.  
  25. #include "config.h"
  26.  
  27. #include "c-pathch.h"
  28. #include "c-namemx.h"
  29. #include "c-pathmx.h"
  30. #include "paths.h"
  31. #include "c-ctype.h"
  32. #include "riscos_ex.h"
  33. #include "pathsrch.h"
  34.  
  35. #define dir_p(name) riscos_isdir(name)
  36.  
  37. static void add_directory P3H(string **, unsigned *, string);
  38. static void expand_subdir P3H(string **, unsigned *, string);
  39. static string readable P1H(string);
  40. #if 0
  41. static string *find_dir_list P1H(string);
  42. static void save_dir_list P2H(string, string *);
  43. #endif
  44. boolean riscos_readaccess P1H(string);
  45.  
  46. /* If FILENAME is absolute or explicitly relative (i.e., starts with
  47.    `@.', or `^.' or there's a ':', '&', '\' or '$' in it), or if DIR_LIST is
  48.    null, we return whether
  49.    FILENAME is readable as-is.  Otherwise, we test if FILENAME is in any of
  50.    the directories listed in DIR_LIST.  (The last entry of DIR_LIST must
  51.    be null.)  We return the complete path if found, NULL else.
  52.    
  53.    In the interests of doing minimal work here, we assume that each
  54.    element of DIR_LIST already ends with a `.'.
  55.    
  56.    DIR_LIST is most conveniently made by calling `initialize_path_list'.
  57.    This is a separate routine because we allow recursive searching, and
  58.    it may take some time to discover the list of directories.  
  59.    We do not want to incur that overhead every time we want to look for
  60.    a file.
  61.    
  62.    (Actually, `.' is not hardwired into this routine; we use PATH_SEP,
  63.    defined above.)  */
  64.  
  65. string
  66. find_path_filename P2C(string, filename,  string *, dir_list)
  67. {
  68.   string found_name = NULL;
  69.   
  70.   /* If FILENAME is absolute or explicitly relative, or if DIR_LIST is
  71.      null, only check if FILENAME is readable.  */
  72.   if (riscos_absolute (filename) || dir_list == NULL)
  73.   {
  74.     found_name = readable (filename);
  75.   }
  76.   else
  77.     { /* Test if FILENAME is in any of the directories in DIR_LIST.  */
  78.       string save_filename = filename;
  79.       boolean substituting; /* if true, we substituted @ */
  80.       
  81.       while (*dir_list != NULL)
  82.         {
  83.           if (**dir_list == '@') {
  84.             substituting = true;
  85.             filename = concat3(current_path, *dir_list + 1, save_filename);
  86.           }
  87.           else {
  88.             filename = concat (*dir_list, save_filename);
  89.             substituting = false;
  90.           }
  91.  
  92.           found_name = readable (filename);
  93.           if (found_name == NULL)
  94.             {
  95.               free (filename);
  96.               dir_list++;
  97.             }
  98.           else
  99.             {
  100.               if (found_name != filename)
  101.                 free (filename);
  102.               if (substituting) found_name = concat(*dir_list, save_filename);
  103.               break;
  104.             }
  105.         }
  106.     }
  107.   
  108.   return found_name;
  109. }
  110.  
  111.  
  112. /* If NAME is readable, return it.  If the error is ENAMETOOLONG,
  113.    truncate any too-long path components and return the result (unless
  114.    there were no too-long components, i.e., a overall too-long name
  115.    caused the error, in which case return NULL).  On any other error,
  116.    return NULL.
  117.    
  118.    POSIX invented this brain-damage of not necessarily truncating
  119.    pathname components; the system's behavior is defined by the value of
  120.    the symbol _POSIX_NO_TRUNC, but you can't change it dynamically! 
  121.  
  122.    RISC OS does things a bit differently. We need some kernel routines
  123.    for testing read access, and we don't truncate filenames, as different
  124.    filing systems might allow different filename lengths. */
  125.  
  126. #include <kernel.h>
  127.  
  128. static string
  129. readable (name)
  130.     string name;
  131. {
  132.   string ret = NULL;
  133.   
  134.   if (riscos_readaccess(name))
  135.     ret = name;
  136.   else
  137.   {
  138.     char *pos;
  139. #ifdef RISCOS_DEBUG
  140.     fprintf(stderr, "readable(\"%s\") tries ", name);
  141. #endif
  142.     /* Try .sty file without extension */
  143.     pos = strrchr(name, '.');
  144.     if (pos && strcmp(pos + 1, "sty") == 0) {
  145.       *pos = '\0';
  146. #ifdef RISCOS_DEBUG
  147.       fprintf(stderr, "\"%s\"\n", name);
  148. #endif
  149.       if (riscos_readaccess(name)) ret = name;
  150.       else *pos = '.';
  151.     }
  152. #ifdef RISCOS_DEBUG
  153.     fprintf(stderr, "\n");
  154. #endif
  155.   }
  156.   
  157.   return ret;
  158. }
  159.  
  160. /* OS_File 17: prm p.836 or c.riscos_ex */
  161.  
  162. boolean
  163. riscos_readaccess(string name)
  164. {
  165.   _kernel_osfile_block block;
  166.  
  167.   return (_kernel_osfile(17, name, &block) == 1 && (block.end & 1));
  168. }
  169.  
  170. /* Return a NULL-terminated array of directory names, each name ending
  171.    with PATH_SEP, created by parsing the PATH_DELIMITER-separated list
  172.    in the value of the environment variable ENV_NAME, or DEFAULT_PATH if
  173.    the envvar is not set.
  174.    
  175.    A leading or trailing colon in the value of ENV_NAME is replaced by
  176.    DEFAULT_PATH.
  177.    
  178.    Any element of the path that ends with double PATH_SEP characters
  179.    (e.g., `foo..') is replaced by all its subdirectories.
  180.  
  181.    If ENV_NAME is null, only parse DEFAULT_PATH.  If both are null, do
  182.    nothing and return NULL.
  183.  
  184.    Under RISC OS all paths have "$Path" appended. */
  185.  
  186. string *
  187. initialize_path_list P2C(string, env_name,  string, default_path)
  188. {
  189.   string dir, path;
  190.   string *dir_list = NULL;
  191.   unsigned dir_count = 0;
  192.   string env_value = NULL;
  193.   string orig_path;
  194.  
  195.   if (env_name) {
  196.     string path_name;
  197.     path_name = concat (env_name, "$Path");
  198.     env_value = getenv (path_name);
  199.   }
  200.   orig_path = expand_default (env_value, default_path);
  201.  
  202.   if (orig_path == NULL || *orig_path == 0)
  203.     return NULL;
  204.  
  205.   /* If we've already seen this colon-separated list, then just get it
  206.      back instead of going back to the filesystem.  */
  207. #if 0
  208.   dir_list = find_dir_list (orig_path);
  209.   if (dir_list != NULL)
  210.     return dir_list;
  211. #endif
  212.   
  213.   if (*orig_path == PATH_DELIMITER)
  214.     add_directory(&dir_list, &dir_count, "@.");
  215.  
  216.   path = concat (PATH_DELIMITER_STRING, orig_path);
  217. #ifdef RISCOS_DEBUG
  218.   fprintf(stderr, "Parsing \"%s\"\n", path);
  219. #endif
  220.  
  221.   /* Find each element in the path in turn.  */
  222.   for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
  223.        dir = strtok (NULL, PATH_DELIMITER_STRING))
  224.     {
  225.       int len;
  226.  
  227.       len = strlen (dir);
  228.  
  229.       /* If `dir' is the empty string, skip it.  */
  230.       if (len == 0)
  231.         continue;
  232.  
  233.       /* If `dir' ends in double dots, do subdirectories (and remove
  234.          the second dot, so the final pathnames we return don't look
  235.          like foo..bar.).  Because we obviously want to do subdirectories
  236.          of `dir', we don't check if it is a leaf.  This means that if
  237.          `dir' is `foo..', and `foo' contains only symlinks (so our leaf
  238.          test below would be true), the symlinks are chased.  */
  239.       if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP)
  240.         {
  241.           dir[len - 1] = 0;
  242.           if (riscos_isdir (dir))
  243.             {
  244.               add_directory (&dir_list, &dir_count, dir);
  245.               expand_subdir (&dir_list, &dir_count, dir);
  246.             }
  247.         }
  248.       else
  249.         { /* Don't bother to add the directory if it doesn't exist.  */
  250.           if (riscos_isdir (dir))
  251.             add_directory (&dir_list, &dir_count, dir);
  252.         }
  253.     }
  254.   
  255.   /* Add the terminating null entry to `dir_list'.  */
  256.   dir_count++;
  257.   XRETALLOC (dir_list, dir_count, string);
  258.   dir_list[dir_count - 1] = NULL;
  259.   
  260. #if 0
  261.   /* Save the directory list we just found.  */
  262.   save_dir_list (orig_path, dir_list);
  263. #endif
  264.  
  265. #ifdef RISCOS_DEBUG
  266.   {
  267.     int i = 0;
  268.     while (dir_list[i]) fprintf(stderr, "  \"%s\"\n", dir_list[i++]);  
  269.   }
  270. #endif
  271.  
  272.   return dir_list;
  273. }
  274.  
  275. /* Subroutines for `initialize_path_list'.  */
  276.  
  277. /* Add a newly-allocated copy of DIR to the end of the array pointed to
  278.    by DIR_LIST_PTR. Increment DIR_COUNT_PTR.
  279.    UNIX:  Append a `/' to DIR if necessary.
  280.    RISCOS: Do not add a '.' in any circumstance.
  281.    We assume DIR is a directory, to avoid unnecessary an
  282.    unnecessary call to `stat'.  */
  283.  
  284. static void
  285. add_directory (dir_list_ptr, dir_count_ptr, dir)
  286.     string **dir_list_ptr;
  287.     unsigned *dir_count_ptr;
  288.     string dir;
  289. {
  290.   /* Add `dir' to the list of the directories.  */
  291.   (*dir_count_ptr)++;
  292.   XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
  293.   (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
  294. #ifdef RISCOS_DEBUG
  295.   fprintf(stderr, "  adding subdir \"%s\"\n", dir);
  296. #endif
  297. }
  298.  
  299.  
  300. /* Add DIRNAME to DIR_LIST and look for subdirectories, recursively.  We
  301.    assume DIRNAME is the name of a directory.  */
  302.  
  303. /* RISC OS version (does a one-level only search): */
  304.  
  305. /* OS_GBPB 10: Read directory entries:
  306.                dataptr: pointer to results block
  307.                nbytes: number of objects to read
  308.                fileptr: offset of first item in directory (0 first time)
  309.                buf_len: buffer length (dataptr)
  310.                wild_fld: (wildcarded) specification, NULL for all */
  311.  
  312. static void
  313. expand_subdir (dir_list_ptr, dir_count_ptr, dirname)
  314.     string **dir_list_ptr;
  315.     unsigned *dir_count_ptr;
  316.     string dirname;
  317. {
  318.   _kernel_osgbpb_block block;
  319.   char potential[PATH_MAX];
  320.   unsigned length;
  321.   
  322.   /* Compute the length of DIRNAME, since it's loop-invariant.  */
  323.   length = strlen (dirname);
  324.  
  325.   /* Construct the part of the pathname that doesn't change.  */
  326.   strcpy (potential, dirname);
  327.   if (potential[length - 1] == PATH_SEP) potential[--length] = 0;
  328.  
  329. #ifdef RISCOS_DEBUG
  330.   fprintf(stderr, "expanding subdir \"%s\"\n", potential);
  331. #endif
  332.   /* prepare OS_GBPB calls */
  333.   block.dataptr = xmalloc(250); /* plenty of room */
  334.   block.nbytes = 1;
  335.   block.fileptr = 0;
  336.   block.buf_len = 250;
  337.   block.wild_fld = NULL;
  338.  
  339.   while (_kernel_osgbpb(10, (unsigned) potential, &block) != -2 &&
  340.      block.fileptr != -1) {
  341. #ifdef RISCOS_DEBUG
  342.     fprintf(stderr, "found \"%s\"\n", (char *)(block.dataptr) + 20);
  343. #endif
  344.     if (*((char *)(block.dataptr) + 16) == 2) /* dir found, add it to the list */
  345.     {
  346.       char *newfound;
  347.       newfound = xmalloc(length + strlen((char *)(block.dataptr) + 20) + 3);
  348.       strcpy(newfound, potential);
  349.       strcat(newfound, ".");
  350.       strcat(newfound, (char *)(block.dataptr) + 20);
  351.       strcat(newfound, ".");
  352.       add_directory(dir_list_ptr, dir_count_ptr, newfound);
  353.     }
  354.   }
  355. }
  356.  
  357.  
  358. /* These routines, while not strictly needed to be exported, are
  359.    plausibly useful to be called by outsiders.  */
  360.  
  361. /* UNIX:
  362.    Replace a leading or trailing `:' in ENV_PATH with DEFAULT_PATH.  If
  363.    neither is present, return ENV_PATH if that is non-null, else
  364.    DEFAULT_PATH.
  365.  
  366.    RISC OS:
  367.    Haven't found a way to _include_ the defaults yet */
  368.  
  369. string 
  370. expand_default (env_path, default_path)
  371.    string env_path;
  372.    string default_path;
  373. {
  374.   string expansion;
  375.   
  376.   if (env_path == NULL)
  377.     expansion = default_path;
  378.   else
  379.     expansion = env_path;
  380.   
  381.   return expansion;
  382. }
  383.  
  384. #if 0 /* maybe I'll implement this one day */
  385. /* Routines to save and retrieve a directory list keyed by the original
  386.    colon-separated path.  This is useful because 1) it can take a
  387.    significant amount of time to discover all the subdirectories of a
  388.    given directory, and 2) many paths all have the same basic default,
  389.    and thus would recompute the directory list.  */
  390.  
  391. typedef struct
  392. {
  393.   string path;
  394.   string *dir_list;
  395. } saved_path_entry;
  396.  
  397. static saved_path_entry *saved_paths = NULL;
  398. static unsigned saved_paths_length = 0;
  399.  
  400.  
  401. /* We implement the data structure as a simple linear list, since it's
  402.    unlikely to ever be more than a dozen or so elements long.  We don't
  403.    bother to check here if PATH has already been saved; we always add it
  404.    to our list.  */
  405.  
  406. static void
  407. save_dir_list P2C(string, path,  string *, dir_list)
  408. {
  409.   saved_paths_length++;
  410.   XRETALLOC (saved_paths, saved_paths_length, saved_path_entry);
  411.   saved_paths[saved_paths_length - 1].path = path;
  412.   saved_paths[saved_paths_length - 1].dir_list = dir_list;
  413. }
  414.  
  415. /* When we retrieve, just check the list in order.  */
  416.  
  417. static string *
  418. find_dir_list P1C(string, path)
  419. {
  420.   unsigned p;
  421.   
  422. #ifdef RISCOS /* somehow this function doesn't work. But as FileCore caches
  423.                  most things, a research is not that slow */
  424.   return NULL;
  425. #else
  426.  
  427.   for (p = 0; p < saved_paths_length; p++)
  428.     {
  429.       if (strcmp (saved_paths[p].path, path) == 0)
  430.         return saved_paths[p].dir_list;
  431.     }
  432.   
  433.   return NULL;
  434. #endif
  435. }
  436. #endif
  437.