home *** CD-ROM | disk | FTP | other *** search
- /* pathsrch.c: look for files based on paths, i.e., colon-separated
- lists of directories.
-
- Perhaps we should allow % specifiers in the paths for the resolution, etc.
-
- This is a RISC OS ONLY version!
- Things not needed in RISC OS are cut out
-
- Copyright (C) 1992 Free Software Foundation, Inc.
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2, or (at your option)
- any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "config.h"
-
- #include "c-pathch.h"
- #include "c-namemx.h"
- #include "c-pathmx.h"
- #include "paths.h"
- #include "c-ctype.h"
- #include "riscos_ex.h"
- #include "pathsrch.h"
-
- #define dir_p(name) riscos_isdir(name)
-
- static void add_directory P3H(string **, unsigned *, string);
- static void expand_subdir P3H(string **, unsigned *, string);
- static string readable P1H(string);
- #if 0
- static string *find_dir_list P1H(string);
- static void save_dir_list P2H(string, string *);
- #endif
- boolean riscos_readaccess P1H(string);
-
- /* If FILENAME is absolute or explicitly relative (i.e., starts with
- `@.', or `^.' or there's a ':', '&', '\' or '$' in it), or if DIR_LIST is
- null, we return whether
- FILENAME is readable as-is. Otherwise, we test if FILENAME is in any of
- the directories listed in DIR_LIST. (The last entry of DIR_LIST must
- be null.) We return the complete path if found, NULL else.
-
- In the interests of doing minimal work here, we assume that each
- element of DIR_LIST already ends with a `.'.
-
- DIR_LIST is most conveniently made by calling `initialize_path_list'.
- This is a separate routine because we allow recursive searching, and
- it may take some time to discover the list of directories.
- We do not want to incur that overhead every time we want to look for
- a file.
-
- (Actually, `.' is not hardwired into this routine; we use PATH_SEP,
- defined above.) */
-
- string
- find_path_filename P2C(string, filename, string *, dir_list)
- {
- string found_name = NULL;
-
- /* If FILENAME is absolute or explicitly relative, or if DIR_LIST is
- null, only check if FILENAME is readable. */
- if (riscos_absolute (filename) || dir_list == NULL)
- {
- found_name = readable (filename);
- }
- else
- { /* Test if FILENAME is in any of the directories in DIR_LIST. */
- string save_filename = filename;
- boolean substituting; /* if true, we substituted @ */
-
- while (*dir_list != NULL)
- {
- if (**dir_list == '@') {
- substituting = true;
- filename = concat3(current_path, *dir_list + 1, save_filename);
- }
- else {
- filename = concat (*dir_list, save_filename);
- substituting = false;
- }
-
- found_name = readable (filename);
- if (found_name == NULL)
- {
- free (filename);
- dir_list++;
- }
- else
- {
- if (found_name != filename)
- free (filename);
- if (substituting) found_name = concat(*dir_list, save_filename);
- break;
- }
- }
- }
-
- return found_name;
- }
-
-
- /* If NAME is readable, return it. If the error is ENAMETOOLONG,
- truncate any too-long path components and return the result (unless
- there were no too-long components, i.e., a overall too-long name
- caused the error, in which case return NULL). On any other error,
- return NULL.
-
- POSIX invented this brain-damage of not necessarily truncating
- pathname components; the system's behavior is defined by the value of
- the symbol _POSIX_NO_TRUNC, but you can't change it dynamically!
-
- RISC OS does things a bit differently. We need some kernel routines
- for testing read access, and we don't truncate filenames, as different
- filing systems might allow different filename lengths. */
-
- #include <kernel.h>
-
- static string
- readable (name)
- string name;
- {
- string ret = NULL;
-
- if (riscos_readaccess(name))
- ret = name;
- else
- {
- char *pos;
- #ifdef RISCOS_DEBUG
- fprintf(stderr, "readable(\"%s\") tries ", name);
- #endif
- /* Try .sty file without extension */
- pos = strrchr(name, '.');
- if (pos && strcmp(pos + 1, "sty") == 0) {
- *pos = '\0';
- #ifdef RISCOS_DEBUG
- fprintf(stderr, "\"%s\"\n", name);
- #endif
- if (riscos_readaccess(name)) ret = name;
- else *pos = '.';
- }
- #ifdef RISCOS_DEBUG
- fprintf(stderr, "\n");
- #endif
- }
-
- return ret;
- }
-
- /* OS_File 17: prm p.836 or c.riscos_ex */
-
- boolean
- riscos_readaccess(string name)
- {
- _kernel_osfile_block block;
-
- return (_kernel_osfile(17, name, &block) == 1 && (block.end & 1));
- }
-
- /* Return a NULL-terminated array of directory names, each name ending
- with PATH_SEP, created by parsing the PATH_DELIMITER-separated list
- in the value of the environment variable ENV_NAME, or DEFAULT_PATH if
- the envvar is not set.
-
- A leading or trailing colon in the value of ENV_NAME is replaced by
- DEFAULT_PATH.
-
- Any element of the path that ends with double PATH_SEP characters
- (e.g., `foo..') is replaced by all its subdirectories.
-
- If ENV_NAME is null, only parse DEFAULT_PATH. If both are null, do
- nothing and return NULL.
-
- Under RISC OS all paths have "$Path" appended. */
-
- string *
- initialize_path_list P2C(string, env_name, string, default_path)
- {
- string dir, path;
- string *dir_list = NULL;
- unsigned dir_count = 0;
- string env_value = NULL;
- string orig_path;
-
- if (env_name) {
- string path_name;
- path_name = concat (env_name, "$Path");
- env_value = getenv (path_name);
- }
- orig_path = expand_default (env_value, default_path);
-
- if (orig_path == NULL || *orig_path == 0)
- return NULL;
-
- /* If we've already seen this colon-separated list, then just get it
- back instead of going back to the filesystem. */
- #if 0
- dir_list = find_dir_list (orig_path);
- if (dir_list != NULL)
- return dir_list;
- #endif
-
- if (*orig_path == PATH_DELIMITER)
- add_directory(&dir_list, &dir_count, "@.");
-
- path = concat (PATH_DELIMITER_STRING, orig_path);
- #ifdef RISCOS_DEBUG
- fprintf(stderr, "Parsing \"%s\"\n", path);
- #endif
-
- /* Find each element in the path in turn. */
- for (dir = strtok (path, PATH_DELIMITER_STRING); dir != NULL;
- dir = strtok (NULL, PATH_DELIMITER_STRING))
- {
- int len;
-
- len = strlen (dir);
-
- /* If `dir' is the empty string, skip it. */
- if (len == 0)
- continue;
-
- /* If `dir' ends in double dots, do subdirectories (and remove
- the second dot, so the final pathnames we return don't look
- like foo..bar.). Because we obviously want to do subdirectories
- of `dir', we don't check if it is a leaf. This means that if
- `dir' is `foo..', and `foo' contains only symlinks (so our leaf
- test below would be true), the symlinks are chased. */
- if (len > 2 && dir[len - 1] == PATH_SEP && dir[len - 2] == PATH_SEP)
- {
- dir[len - 1] = 0;
- if (riscos_isdir (dir))
- {
- add_directory (&dir_list, &dir_count, dir);
- expand_subdir (&dir_list, &dir_count, dir);
- }
- }
- else
- { /* Don't bother to add the directory if it doesn't exist. */
- if (riscos_isdir (dir))
- add_directory (&dir_list, &dir_count, dir);
- }
- }
-
- /* Add the terminating null entry to `dir_list'. */
- dir_count++;
- XRETALLOC (dir_list, dir_count, string);
- dir_list[dir_count - 1] = NULL;
-
- #if 0
- /* Save the directory list we just found. */
- save_dir_list (orig_path, dir_list);
- #endif
-
- #ifdef RISCOS_DEBUG
- {
- int i = 0;
- while (dir_list[i]) fprintf(stderr, " \"%s\"\n", dir_list[i++]);
- }
- #endif
-
- return dir_list;
- }
-
- /* Subroutines for `initialize_path_list'. */
-
- /* Add a newly-allocated copy of DIR to the end of the array pointed to
- by DIR_LIST_PTR. Increment DIR_COUNT_PTR.
- UNIX: Append a `/' to DIR if necessary.
- RISCOS: Do not add a '.' in any circumstance.
- We assume DIR is a directory, to avoid unnecessary an
- unnecessary call to `stat'. */
-
- static void
- add_directory (dir_list_ptr, dir_count_ptr, dir)
- string **dir_list_ptr;
- unsigned *dir_count_ptr;
- string dir;
- {
- /* Add `dir' to the list of the directories. */
- (*dir_count_ptr)++;
- XRETALLOC (*dir_list_ptr, *dir_count_ptr, string);
- (*dir_list_ptr)[*dir_count_ptr - 1] = dir;
- #ifdef RISCOS_DEBUG
- fprintf(stderr, " adding subdir \"%s\"\n", dir);
- #endif
- }
-
-
- /* Add DIRNAME to DIR_LIST and look for subdirectories, recursively. We
- assume DIRNAME is the name of a directory. */
-
- /* RISC OS version (does a one-level only search): */
-
- /* OS_GBPB 10: Read directory entries:
- dataptr: pointer to results block
- nbytes: number of objects to read
- fileptr: offset of first item in directory (0 first time)
- buf_len: buffer length (dataptr)
- wild_fld: (wildcarded) specification, NULL for all */
-
- static void
- expand_subdir (dir_list_ptr, dir_count_ptr, dirname)
- string **dir_list_ptr;
- unsigned *dir_count_ptr;
- string dirname;
- {
- _kernel_osgbpb_block block;
- char potential[PATH_MAX];
- unsigned length;
-
- /* Compute the length of DIRNAME, since it's loop-invariant. */
- length = strlen (dirname);
-
- /* Construct the part of the pathname that doesn't change. */
- strcpy (potential, dirname);
- if (potential[length - 1] == PATH_SEP) potential[--length] = 0;
-
- #ifdef RISCOS_DEBUG
- fprintf(stderr, "expanding subdir \"%s\"\n", potential);
- #endif
- /* prepare OS_GBPB calls */
- block.dataptr = xmalloc(250); /* plenty of room */
- block.nbytes = 1;
- block.fileptr = 0;
- block.buf_len = 250;
- block.wild_fld = NULL;
-
- while (_kernel_osgbpb(10, (unsigned) potential, &block) != -2 &&
- block.fileptr != -1) {
- #ifdef RISCOS_DEBUG
- fprintf(stderr, "found \"%s\"\n", (char *)(block.dataptr) + 20);
- #endif
- if (*((char *)(block.dataptr) + 16) == 2) /* dir found, add it to the list */
- {
- char *newfound;
- newfound = xmalloc(length + strlen((char *)(block.dataptr) + 20) + 3);
- strcpy(newfound, potential);
- strcat(newfound, ".");
- strcat(newfound, (char *)(block.dataptr) + 20);
- strcat(newfound, ".");
- add_directory(dir_list_ptr, dir_count_ptr, newfound);
- }
- }
- }
-
-
- /* These routines, while not strictly needed to be exported, are
- plausibly useful to be called by outsiders. */
-
- /* UNIX:
- Replace a leading or trailing `:' in ENV_PATH with DEFAULT_PATH. If
- neither is present, return ENV_PATH if that is non-null, else
- DEFAULT_PATH.
-
- RISC OS:
- Haven't found a way to _include_ the defaults yet */
-
- string
- expand_default (env_path, default_path)
- string env_path;
- string default_path;
- {
- string expansion;
-
- if (env_path == NULL)
- expansion = default_path;
- else
- expansion = env_path;
-
- return expansion;
- }
-
- #if 0 /* maybe I'll implement this one day */
- /* Routines to save and retrieve a directory list keyed by the original
- colon-separated path. This is useful because 1) it can take a
- significant amount of time to discover all the subdirectories of a
- given directory, and 2) many paths all have the same basic default,
- and thus would recompute the directory list. */
-
- typedef struct
- {
- string path;
- string *dir_list;
- } saved_path_entry;
-
- static saved_path_entry *saved_paths = NULL;
- static unsigned saved_paths_length = 0;
-
-
- /* We implement the data structure as a simple linear list, since it's
- unlikely to ever be more than a dozen or so elements long. We don't
- bother to check here if PATH has already been saved; we always add it
- to our list. */
-
- static void
- save_dir_list P2C(string, path, string *, dir_list)
- {
- saved_paths_length++;
- XRETALLOC (saved_paths, saved_paths_length, saved_path_entry);
- saved_paths[saved_paths_length - 1].path = path;
- saved_paths[saved_paths_length - 1].dir_list = dir_list;
- }
-
- /* When we retrieve, just check the list in order. */
-
- static string *
- find_dir_list P1C(string, path)
- {
- unsigned p;
-
- #ifdef RISCOS /* somehow this function doesn't work. But as FileCore caches
- most things, a research is not that slow */
- return NULL;
- #else
-
- for (p = 0; p < saved_paths_length; p++)
- {
- if (strcmp (saved_paths[p].path, path) == 0)
- return saved_paths[p].dir_list;
- }
-
- return NULL;
- #endif
- }
- #endif
-