X-Git-Url: https://git.libre-soc.org/?a=blobdiff_plain;f=gcc%2Fcppfiles.c;h=bacb8e18ef97d06087b897bcf9fc13440b2ca547;hb=6f32162af66395b5ae357d714d8d18fb6f666feb;hp=295590203963237a65857239adcd000f91d338a8;hpb=f8f769ea4e694c516b9631ae8f2215cc6d5fb96f;p=gcc.git diff --git a/gcc/cppfiles.c b/gcc/cppfiles.c index 29559020396..bacb8e18ef9 100644 --- a/gcc/cppfiles.c +++ b/gcc/cppfiles.c @@ -1,6 +1,6 @@ /* Part of CPP library. (include file handling) Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998, - 1999, 2000 Free Software Foundation, Inc. + 1999, 2000, 2001 Free Software Foundation, Inc. Written by Per Bothner, 1994. Based on CCCP program by Paul Rubin, June 1986 Adapted to ANSI C, Richard Stallman, Jan 1987 @@ -22,11 +22,11 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" -#include "hashtab.h" #include "cpplib.h" #include "cpphash.h" #include "intl.h" #include "mkdeps.h" +#include "splay-tree.h" #ifdef HAVE_MMAP_FILE # include @@ -39,116 +39,417 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ # define MMAP_THRESHOLD 0 #endif -static IHASH *redundant_include_p PARAMS ((cpp_reader *, IHASH *, - struct file_name_list *)); -static IHASH *make_IHASH PARAMS ((const char *, const char *, - struct file_name_list *, - unsigned int, IHASH **)); +#ifndef O_BINARY +# define O_BINARY 0 +#endif + +/* If errno is inspected immediately after a system call fails, it will be + nonzero, and no error number will ever be zero. */ +#ifndef ENOENT +# define ENOENT 0 +#endif +#ifndef ENOTDIR +# define ENOTDIR 0 +#endif + +/* Suppress warning about function macros used w/o arguments in traditional + C. It is unlikely that glibc's strcmp macro helps this file at all. */ +#undef strcmp + +/* This structure is used for the table of all includes. */ +struct include_file +{ + const char *name; /* actual path name of file */ + const cpp_hashnode *cmacro; /* macro, if any, preventing reinclusion. */ + const struct search_path *foundhere; + /* location in search path where file was + found, for #include_next and sysp. */ + const unsigned char *buffer; /* pointer to cached file contents */ + struct stat st; /* copy of stat(2) data for file */ + int fd; /* fd open on file (short term storage only) */ + int err_no; /* errno obtained if opening a file failed */ + unsigned short include_count; /* number of times file has been read */ + unsigned short refcnt; /* number of stacked buffers using this file */ + unsigned char mapped; /* file buffer is mmapped */ +}; + +/* The cmacro works like this: If it's NULL, the file is to be + included again. If it's NEVER_REREAD, the file is never to be + included again. Otherwise it is a macro hashnode, and the file is + to be included again if the macro is defined. */ +#define NEVER_REREAD ((const cpp_hashnode *)-1) +#define DO_NOT_REREAD(inc) \ +((inc)->cmacro && ((inc)->cmacro == NEVER_REREAD \ + || (inc)->cmacro->type == NT_MACRO)) +#define NO_INCLUDE_PATH ((struct include_file *) -1) + static struct file_name_map *read_name_map PARAMS ((cpp_reader *, const char *)); static char *read_filename_string PARAMS ((int, FILE *)); static char *remap_filename PARAMS ((cpp_reader *, char *, - struct file_name_list *)); -static struct file_name_list *actual_directory - PARAMS ((cpp_reader *, const char *)); -static unsigned int hash_IHASH PARAMS ((const void *)); -static int eq_IHASH PARAMS ((const void *, const void *)); -static int find_include_file PARAMS ((cpp_reader *, const char *, - struct file_name_list *, - IHASH **, int *)); -static inline int open_include_file PARAMS ((cpp_reader *, const char *)); -static int read_include_file PARAMS ((cpp_reader *, int, IHASH *)); -static ssize_t read_with_read PARAMS ((cpp_buffer *, int, ssize_t)); -static ssize_t read_file PARAMS ((cpp_buffer *, int, ssize_t)); - -#if 0 -static void hack_vms_include_specification PARAMS ((char *)); -#endif + struct search_path *)); +static struct search_path *search_from PARAMS ((cpp_reader *, + enum include_type)); +static struct include_file * + find_include_file PARAMS ((cpp_reader *, const cpp_token *, + enum include_type)); +static struct include_file *open_file PARAMS ((cpp_reader *, const char *)); +static int read_include_file PARAMS ((cpp_reader *, struct include_file *)); +static bool stack_include_file PARAMS ((cpp_reader *, struct include_file *)); +static void purge_cache PARAMS ((struct include_file *)); +static void destroy_node PARAMS ((splay_tree_value)); +static int report_missing_guard PARAMS ((splay_tree_node, void *)); +static splay_tree_node find_or_create_entry PARAMS ((cpp_reader *, + const char *)); +static void handle_missing_header PARAMS ((cpp_reader *, const char *, int)); +static int remove_component_p PARAMS ((const char *)); + +/* Set up the splay tree we use to store information about all the + file names seen in this compilation. We also have entries for each + file we tried to open but failed; this saves system calls since we + don't try to open it again in future. + + The key of each node is the file name, after processing by + _cpp_simplify_pathname. The path name may or may not be absolute. + The path string has been malloced, as is automatically freed by + registering free () as the splay tree key deletion function. + + A node's value is a pointer to a struct include_file, and is never + NULL. */ +void +_cpp_init_includes (pfile) + cpp_reader *pfile; +{ + pfile->all_include_files + = splay_tree_new ((splay_tree_compare_fn) strcmp, + (splay_tree_delete_key_fn) free, + destroy_node); +} -/* Initial size of include hash table. */ -#define IHASHSIZE 50 +/* Tear down the splay tree. */ +void +_cpp_cleanup_includes (pfile) + cpp_reader *pfile; +{ + splay_tree_delete (pfile->all_include_files); +} -#ifndef INCLUDE_LEN_FUDGE -#define INCLUDE_LEN_FUDGE 0 -#endif +/* Free a node. The path string is automatically freed. */ +static void +destroy_node (v) + splay_tree_value v; +{ + struct include_file *f = (struct include_file *)v; -/* Calculate hash of an IHASH entry. */ -static unsigned int -hash_IHASH (x) - const void *x; + if (f) + { + purge_cache (f); + free (f); + } +} + +/* Mark a file to not be reread (e.g. #import, read failure). */ +void +_cpp_never_reread (file) + struct include_file *file; { - const IHASH *i = (const IHASH *)x; - return i->hash; + file->cmacro = NEVER_REREAD; } -/* Compare an existing IHASH structure with a potential one. */ -static int -eq_IHASH (x, y) - const void *x; - const void *y; +/* Lookup a filename, which is simplified after making a copy, and + create an entry if none exists. errno is nonzero iff a (reported) + stat() error occurred during simplification. */ +static splay_tree_node +find_or_create_entry (pfile, fname) + cpp_reader *pfile; + const char *fname; { - const char *a = ((const IHASH *)x)->nshort; - const char *b = ((const IHASH *)y)->nshort; - return !strcmp (a, b); + splay_tree_node node; + struct include_file *file; + char *name = xstrdup (fname); + + _cpp_simplify_pathname (name); + node = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name); + if (node) + free (name); + else + { + file = xcnew (struct include_file); + file->name = name; + file->err_no = errno; + node = splay_tree_insert (pfile->all_include_files, + (splay_tree_key) file->name, + (splay_tree_value) file); + } + + return node; } -/* Init the hash table. In here so it can see the hash and eq functions. */ +/* Enter a file name in the splay tree, for the sake of cpp_included. */ void -_cpp_init_include_hash (pfile) +_cpp_fake_include (pfile, fname) cpp_reader *pfile; + const char *fname; { - pfile->all_include_files - = htab_create (IHASHSIZE, hash_IHASH, eq_IHASH, free); + find_or_create_entry (pfile, fname); } -/* Return 0 if the file pointed to by IHASH has never been included before, - -1 if it has been included before and need not be again, - or a pointer to an IHASH entry which is the file to be reread. - "Never before" is with respect to the position in ILIST. +/* Given a file name, look it up in the cache; if there is no entry, + create one with a non-NULL value (regardless of success in opening + the file). If the file doesn't exist or is inaccessible, this + entry is flagged so we don't attempt to open it again in the + future. If the file isn't open, open it. The empty string is + interpreted as stdin. + + Returns an include_file structure with an open file descriptor on + success, or NULL on failure. */ + +static struct include_file * +open_file (pfile, filename) + cpp_reader *pfile; + const char *filename; +{ + splay_tree_node nd = find_or_create_entry (pfile, filename); + struct include_file *file = (struct include_file *) nd->value; + + if (file->err_no) + { + /* Ugh. handle_missing_header () needs errno to be set. */ + errno = file->err_no; + return 0; + } + + /* Don't reopen an idempotent file. */ + if (DO_NOT_REREAD (file)) + return file; + + /* Don't reopen one which is already loaded. */ + if (file->buffer != NULL) + return file; + + /* We used to open files in nonblocking mode, but that caused more + problems than it solved. Do take care not to acquire a + controlling terminal by mistake (this can't happen on sane + systems, but paranoia is a virtue). + + Use the three-argument form of open even though we aren't + specifying O_CREAT, to defend against broken system headers. + + O_BINARY tells some runtime libraries (notably DJGPP) not to do + newline translation; we can handle DOS line breaks just fine + ourselves. - This will not detect redundancies involving odd uses of the - `current directory' rule for "" includes. They aren't quite - pathological, but I think they are rare enough not to worry about. - The simplest example is: + Special case: the empty string is translated to stdin. */ - top.c: - #include "a/a.h" - #include "b/b.h" + if (filename[0] == '\0') + file->fd = 0; + else + file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666); + + if (file->fd != -1 && fstat (file->fd, &file->st) == 0) + { + if (!S_ISDIR (file->st.st_mode)) + return file; + /* If it's a directory, we return null and continue the search + as the file we're looking for may appear elsewhere in the + search path. */ + errno = ENOENT; + } - a/a.h: - #include "../b/b.h" + file->err_no = errno; + return 0; +} - and the problem is that for `current directory' includes, - ihash->foundhere is not on any of the global include chains, - so the test below (i->foundhere == l) may be false even when - the directories are in fact the same. */ +/* Place the file referenced by INC into a new buffer on the buffer + stack, unless there are errors, or the file is not re-included + because of e.g. multiple-include guards. Returns true if a buffer + is stacked. */ -static IHASH * -redundant_include_p (pfile, ihash, ilist) +static bool +stack_include_file (pfile, inc) cpp_reader *pfile; - IHASH *ihash; - struct file_name_list *ilist; + struct include_file *inc; { - struct file_name_list *l; - IHASH *i; - - if (! ihash->foundhere) - return 0; - - for (i = ihash; i; i = i->next_this_file) - for (l = ilist; l; l = l->next) - if (i->foundhere == l) - /* The control_macro works like this: If it's NULL, the file - is to be included again. If it's "", the file is never to - be included again. If it's a string, the file is not to be - included again if the string is the name of a defined macro. */ - return (i->control_macro - && (i->control_macro[0] == '\0' - || cpp_defined (pfile, i->control_macro, - ustrlen (i->control_macro)))) - ? (IHASH *)-1 : i; + cpp_buffer *fp; + int sysp; + const char *filename; + + if (DO_NOT_REREAD (inc)) + return false; + sysp = MAX ((pfile->map ? pfile->map->sysp : 0), + (inc->foundhere ? inc->foundhere->sysp : 0)); + + /* For -M, add the file to the dependencies on its first inclusion. */ + if (CPP_OPTION (pfile, print_deps) > sysp && !inc->include_count) + deps_add_dep (pfile->deps, inc->name); + + /* Not in cache? */ + if (! inc->buffer) + { + /* Mark a regular, zero-length file never-reread. Zero-length + files are stacked the first time, so preprocessing a main + file of zero length does not raise an error. */ + if (S_ISREG (inc->st.st_mode) && inc->st.st_size == 0) + _cpp_never_reread (inc); + else if (read_include_file (pfile, inc)) + { + /* If an error occurs, do not try to read this file again. */ + _cpp_never_reread (inc); + return false; + } + close (inc->fd); + inc->fd = -1; + } + + if (pfile->buffer) + /* We don't want MI guard advice for the main file. */ + inc->include_count++; + + /* Push a buffer. */ + fp = cpp_push_buffer (pfile, inc->buffer, inc->st.st_size, + /* from_stage3 */ CPP_OPTION (pfile, preprocessed), 0); + fp->inc = inc; + fp->inc->refcnt++; + + /* Initialise controlling macro state. */ + pfile->mi_valid = true; + pfile->mi_cmacro = 0; + + /* Generate the call back. */ + filename = inc->name; + if (*filename == '\0') + filename = _(""); + _cpp_do_file_change (pfile, LC_ENTER, filename, 1, sysp); + + return true; +} + +/* Read the file referenced by INC into the file cache. + + If fd points to a plain file, we might be able to mmap it; we can + definitely allocate the buffer all at once. If fd is a pipe or + terminal, we can't do either. If fd is something weird, like a + block device, we don't want to read it at all. + + Unfortunately, different systems use different st.st_mode values + for pipes: some have S_ISFIFO, some S_ISSOCK, some are buggy and + zero the entire struct stat except a couple fields. Hence we don't + even try to figure out what something is, except for plain files + and block devices. + + FIXME: Flush file cache and try again if we run out of memory. */ + +static int +read_include_file (pfile, inc) + cpp_reader *pfile; + struct include_file *inc; +{ + ssize_t size, offset, count; + U_CHAR *buf; +#if MMAP_THRESHOLD + static int pagesize = -1; +#endif + + if (S_ISREG (inc->st.st_mode)) + { + /* off_t might have a wider range than ssize_t - in other words, + the max size of a file might be bigger than the address + space. We can't handle a file that large. (Anyone with + a single source file bigger than 2GB needs to rethink + their coding style.) Some systems (e.g. AIX 4.1) define + SSIZE_MAX to be much smaller than the actual range of the + type. Use INTTYPE_MAXIMUM unconditionally to ensure this + does not bite us. */ + if (inc->st.st_size > INTTYPE_MAXIMUM (ssize_t)) + { + cpp_error (pfile, "%s is too large", inc->name); + goto fail; + } + size = inc->st.st_size; + + inc->mapped = 0; +#if MMAP_THRESHOLD + if (pagesize == -1) + pagesize = getpagesize (); + + if (size / pagesize >= MMAP_THRESHOLD) + { + buf = (U_CHAR *) mmap (0, size, PROT_READ, MAP_PRIVATE, inc->fd, 0); + if (buf == (U_CHAR *)-1) + goto perror_fail; + inc->mapped = 1; + } + else +#endif + { + buf = (U_CHAR *) xmalloc (size); + offset = 0; + while (offset < size) + { + count = read (inc->fd, buf + offset, size - offset); + if (count < 0) + goto perror_fail; + if (count == 0) + { + cpp_warning (pfile, "%s is shorter than expected", inc->name); + break; + } + offset += count; + } + } + } + else if (S_ISBLK (inc->st.st_mode)) + { + cpp_error (pfile, "%s is a block device", inc->name); + goto fail; + } + else + { + /* 8 kilobytes is a sensible starting size. It ought to be + bigger than the kernel pipe buffer, and it's definitely + bigger than the majority of C source files. */ + size = 8 * 1024; + + buf = (U_CHAR *) xmalloc (size); + offset = 0; + while ((count = read (inc->fd, buf + offset, size - offset)) > 0) + { + offset += count; + if (offset == size) + buf = xrealloc (buf, (size *= 2)); + } + if (count < 0) + goto perror_fail; + + if (offset < size) + buf = xrealloc (buf, offset); + inc->st.st_size = offset; + } + + inc->buffer = buf; return 0; + + perror_fail: + cpp_error_from_errno (pfile, inc->name); + fail: + return 1; +} + +static void +purge_cache (inc) + struct include_file *inc; +{ + if (inc->buffer) + { +#if MMAP_THRESHOLD + if (inc->mapped) + munmap ((PTR) inc->buffer, inc->st.st_size); + else +#endif + free ((PTR) inc->buffer); + inc->buffer = NULL; + } } /* Return 1 if the file named by FNAME has been included before in @@ -158,179 +459,340 @@ cpp_included (pfile, fname) cpp_reader *pfile; const char *fname; { - IHASH dummy, *ptr; - dummy.nshort = fname; - dummy.hash = _cpp_calc_hash ((const U_CHAR *)fname, strlen (fname)); - ptr = htab_find_with_hash (pfile->all_include_files, - (const void *)&dummy, dummy.hash); - return (ptr != NULL); + struct search_path *path; + char *name, *n; + splay_tree_node nd; + + if (IS_ABSOLUTE_PATHNAME (fname)) + { + /* Just look it up. */ + nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname); + return (nd && nd->value); + } + + /* Search directory path for the file. */ + name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2); + for (path = CPP_OPTION (pfile, quote_include); path; path = path->next) + { + memcpy (name, path->name, path->len); + name[path->len] = '/'; + strcpy (&name[path->len + 1], fname); + if (CPP_OPTION (pfile, remap)) + n = remap_filename (pfile, name, path); + else + n = name; + + nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n); + if (nd && nd->value) + return 1; + } + return 0; } -/* Create an IHASH entry and insert it in SLOT. */ -static IHASH * -make_IHASH (name, fname, path, hash, slot) - const char *name, *fname; - struct file_name_list *path; - unsigned int hash; - IHASH **slot; +/* Search for HEADER. Return 0 if there is no such file (or it's + un-openable), in which case an error code will be in errno. If + there is no include path to use it returns NO_INCLUDE_PATH, + otherwise an include_file structure. If this request originates + from a #include_next directive, set INCLUDE_NEXT to true. */ + +static struct include_file * +find_include_file (pfile, header, type) + cpp_reader *pfile; + const cpp_token *header; + enum include_type type; { - IHASH *ih; - if (path == ABSOLUTE_PATH) + const char *fname = (const char *) header->val.str.text; + struct search_path *path; + struct include_file *file; + char *name, *n; + + if (IS_ABSOLUTE_PATHNAME (fname)) + return open_file (pfile, fname); + + /* For #include_next, skip in the search path past the dir in which + the current file was found, but if it was found via an absolute + path use the normal search logic. */ + if (type == IT_INCLUDE_NEXT && pfile->buffer->inc->foundhere) + path = pfile->buffer->inc->foundhere->next; + else if (header->type == CPP_HEADER_NAME) + path = CPP_OPTION (pfile, bracket_include); + else + path = search_from (pfile, type); + + if (path == NULL) { - ih = (IHASH *) xmalloc (sizeof (IHASH) + strlen (name)); - ih->nshort = ih->name; + cpp_error (pfile, "No include path in which to find %s", fname); + return NO_INCLUDE_PATH; } - else + + /* Search directory path for the file. */ + name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2); + for (; path; path = path->next) { - char *s; - - if ((s = strstr (name, fname)) != NULL) - { - ih = (IHASH *) xmalloc (sizeof (IHASH) + strlen (name)); - ih->nshort = ih->name + (s - name); - } + memcpy (name, path->name, path->len); + name[path->len] = '/'; + strcpy (&name[path->len + 1], fname); + if (CPP_OPTION (pfile, remap)) + n = remap_filename (pfile, name, path); else + n = name; + + file = open_file (pfile, n); + if (file) { - ih = (IHASH *) xmalloc (sizeof (IHASH) + strlen (name) - + strlen (fname) + 1); - ih->nshort = ih->name + strlen (name) + 1; - strcpy ((char *)ih->nshort, fname); + file->foundhere = path; + return file; } } - strcpy ((char *)ih->name, name); - ih->foundhere = path; - ih->control_macro = NULL; - ih->hash = hash; - ih->next_this_file = *slot; - *slot = ih; - return ih; -} -/* Centralize calls to open(2) here. This provides a hook for future - changes which might, e.g. look for and open a precompiled version - of the header. It also means all the magic currently associated - with calling open is in one place, and if we ever need more, it'll - be in one place too. + return 0; +} - We used to open files in nonblocking mode, but that caused more - problems than it solved. Do take care not to acquire a controlling - terminal by mistake (this can't happen on sane systems, but - paranoia is a virtue). +/* Not everyone who wants to set system-header-ness on a buffer can + see the details of a buffer. This is an exported interface because + fix-header needs it. */ +void +cpp_make_system_header (pfile, syshdr, externc) + cpp_reader *pfile; + int syshdr, externc; +{ + int flags = 0; - Use the three-argument form of open even though we aren't - specifying O_CREAT, to defend against broken system headers. */ + /* 1 = system header, 2 = system header to be treated as C. */ + if (syshdr) + flags = 1 + (externc != 0); + _cpp_do_file_change (pfile, LC_RENAME, pfile->map->to_file, + SOURCE_LINE (pfile->map, pfile->line), flags); +} -static inline int -open_include_file (pfile, filename) - cpp_reader *pfile ATTRIBUTE_UNUSED; - const char *filename; +/* Report on all files that might benefit from a multiple include guard. + Triggered by -H. */ +void +_cpp_report_missing_guards (pfile) + cpp_reader *pfile; { - return open (filename, O_RDONLY|O_NOCTTY, 0666); + int banner = 0; + splay_tree_foreach (pfile->all_include_files, report_missing_guard, + (PTR) &banner); } -/* Search for include file FNAME in the include chain starting at - SEARCH_START. Return -2 if this file doesn't need to be included - (because it was included already and it's marked idempotent), - -1 if an error occurred, or a file descriptor open on the file. - *IHASH is set to point to the include hash entry for this file, and - *BEFORE is set to 1 if the file was included before (but needs to be read - again). */ static int -find_include_file (pfile, fname, search_start, ihash, before) - cpp_reader *pfile; - const char *fname; - struct file_name_list *search_start; - IHASH **ihash; - int *before; +report_missing_guard (n, b) + splay_tree_node n; + void *b; { - struct file_name_list *path; - IHASH *ih, **slot; - IHASH dummy; - int f; - char *name; - - dummy.nshort = fname; - dummy.hash = _cpp_calc_hash ((const U_CHAR *)fname, strlen (fname)); - path = (fname[0] == '/') ? ABSOLUTE_PATH : search_start; - slot = (IHASH **) htab_find_slot_with_hash (pfile->all_include_files, - (const void *) &dummy, - dummy.hash, INSERT); + struct include_file *f = (struct include_file *) n->value; + int *bannerp = (int *)b; - if (*slot && (ih = redundant_include_p (pfile, *slot, path))) + if (f && f->cmacro == 0 && f->include_count == 1) { - if (ih == (IHASH *)-1) - return -2; - - *before = 1; - *ihash = ih; - return open_include_file (pfile, ih->name); + if (*bannerp == 0) + { + fputs (_("Multiple include guards may be useful for:\n"), stderr); + *bannerp = 1; + } + fputs (f->name, stderr); + putc ('\n', stderr); } + return 0; +} - if (path == ABSOLUTE_PATH) - { - name = (char *) fname; - f = open_include_file (pfile, name); - } - else +/* Create a dependency, or issue an error message as appropriate. */ +static void +handle_missing_header (pfile, fname, angle_brackets) + cpp_reader *pfile; + const char *fname; + int angle_brackets; +{ + int print_dep = CPP_PRINT_DEPS(pfile) > (angle_brackets || pfile->map->sysp); + + if (CPP_OPTION (pfile, print_deps_missing_files) && print_dep) { - /* Search directory path, trying to open the file. */ - name = (char *) alloca (strlen (fname) + pfile->max_include_len - + 2 + INCLUDE_LEN_FUDGE); - do + if (!angle_brackets || IS_ABSOLUTE_PATHNAME (fname)) + deps_add_dep (pfile->deps, fname); + else { - memcpy (name, path->name, path->nlen); - name[path->nlen] = '/'; - strcpy (&name[path->nlen+1], fname); - _cpp_simplify_pathname (name); - if (CPP_OPTION (pfile, remap)) - name = remap_filename (pfile, name, path); - - f = open_include_file (pfile, name); -#ifdef EACCES - if (f == -1 && errno == EACCES) + /* If requested as a system header, assume it belongs in + the first system header directory. */ + struct search_path *ptr = CPP_OPTION (pfile, bracket_include); + char *p; + int len = 0, fname_len = strlen (fname); + + if (ptr) + len = ptr->len; + + p = (char *) alloca (len + fname_len + 2); + if (len) { - cpp_error (pfile, - "included file `%s' exists but is not readable", - name); - return -1; + memcpy (p, ptr->name, len); + p[len++] = '/'; } -#endif - if (f >= 0) - break; - path = path->next; + memcpy (p + len, fname, fname_len + 1); + deps_add_dep (pfile->deps, p); } - while (path); } - if (f == -1) + /* If -M was specified, then don't count this as an error, because + we can still produce correct output. Otherwise, we can't produce + correct output, because there may be dependencies we need inside + the missing file, and we don't know what directory this missing + file exists in. FIXME: Use a future cpp_diagnotic_with_errno () + for both of these cases. */ + else if (CPP_PRINT_DEPS (pfile) && ! print_dep) + cpp_warning (pfile, "%s: %s", fname, xstrerror (errno)); + else + cpp_error_from_errno (pfile, fname); +} + +/* Handles #include-family directives, and the command line -imacros + and -include. Returns true if a buffer was stacked. */ +bool +_cpp_execute_include (pfile, header, type) + cpp_reader *pfile; + const cpp_token *header; + enum include_type type; +{ + bool stacked = false; + struct include_file *inc = find_include_file (pfile, header, type); + + if (inc == 0) + handle_missing_header (pfile, (const char *) header->val.str.text, + header->type == CPP_HEADER_NAME); + else if (inc != NO_INCLUDE_PATH) + { + stacked = stack_include_file (pfile, inc); + + if (type == IT_IMPORT) + _cpp_never_reread (inc); + } + + return stacked; +} + +/* Locate HEADER, and determine whether it is newer than the current + file. If it cannot be located or dated, return -1, if it is newer + newer, return 1, otherwise 0. */ +int +_cpp_compare_file_date (pfile, header) + cpp_reader *pfile; + const cpp_token *header; +{ + struct include_file *inc = find_include_file (pfile, header, 0); + + if (inc == NULL || inc == NO_INCLUDE_PATH) return -1; - ih = make_IHASH (name, fname, path, dummy.hash, slot); - *before = 0; - *ihash = ih; - return f; + if (inc->fd > 0) + { + close (inc->fd); + inc->fd = -1; + } + + return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime; } -/* Create a dummy IHASH entry for FNAME, and return its name pointer. - This is used by #line. */ -const char * -_cpp_fake_ihash (pfile, fname) + +/* Push an input buffer and load it up with the contents of FNAME. If + FNAME is "", read standard input. Return true if a buffer was + stacked. */ +bool +_cpp_read_file (pfile, fname) cpp_reader *pfile; const char *fname; { - IHASH *ih, **slot; - IHASH dummy; - - dummy.nshort = fname; - dummy.hash = _cpp_calc_hash ((const U_CHAR *)fname, strlen (fname)); - slot = (IHASH **) htab_find_slot_with_hash (pfile->all_include_files, - (const void *) &dummy, - dummy.hash, INSERT); - if (*slot) - return (*slot)->name; - ih = make_IHASH (fname, 0, ABSOLUTE_PATH, dummy.hash, slot); - return ih->name; + struct include_file *f = open_file (pfile, fname); + + if (f == NULL) + { + cpp_error_from_errno (pfile, fname); + return false; + } + + return stack_include_file (pfile, f); +} + +/* Do appropriate cleanup when a file buffer is popped off the input + stack. Push the next -include file, if any remain. */ +void +_cpp_pop_file_buffer (pfile, inc) + cpp_reader *pfile; + struct include_file *inc; +{ + /* Record the inclusion-preventing macro, which could be NULL + meaning no controlling macro. */ + if (pfile->mi_valid && inc->cmacro == NULL) + inc->cmacro = pfile->mi_cmacro; + + /* Invalidate control macros in the #including file. */ + pfile->mi_valid = false; + + inc->refcnt--; + if (inc->refcnt == 0 && DO_NOT_REREAD (inc)) + purge_cache (inc); + + /* Don't generate a callback for popping the main file. */ + if (pfile->buffer) + { + _cpp_do_file_change (pfile, LC_LEAVE, 0, 0, 0); + + /* Finally, push the next -included file, if any. */ + if (!pfile->buffer->prev) + _cpp_push_next_buffer (pfile); + } } +/* Returns the first place in the include chain to start searching for + "" includes. This involves stripping away the basename of the + current file, unless -I- was specified. + + If we're handling -include or -imacros, use the "" chain, but with + the preprocessor's cwd prepended. */ +static struct search_path * +search_from (pfile, type) + cpp_reader *pfile; + enum include_type type; +{ + cpp_buffer *buffer = pfile->buffer; + unsigned int dlen; + + /* Command line uses the cwd, and does not cache the result. */ + if (type == IT_CMDLINE) + goto use_cwd; + + /* Ignore the current file's directory if -I- was given. */ + if (CPP_OPTION (pfile, ignore_srcdir)) + return CPP_OPTION (pfile, quote_include); + + if (! buffer->search_cached) + { + buffer->search_cached = 1; + + dlen = lbasename (buffer->inc->name) - buffer->inc->name; + + if (dlen) + { + /* We don't guarantee NAME is null-terminated. This saves + allocating and freeing memory. Drop a trailing '/'. */ + buffer->dir.name = buffer->inc->name; + if (dlen > 1) + dlen--; + } + else + { + use_cwd: + buffer->dir.name = "."; + dlen = 1; + } + + if (dlen > pfile->max_include_len) + pfile->max_include_len = dlen; + + buffer->dir.len = dlen; + buffer->dir.next = CPP_OPTION (pfile, quote_include); + buffer->dir.sysp = pfile->map->sysp; + } + + return &buffer->dir; +} /* The file_name_map structure holds a mapping of file names for a particular directory. This mapping is read from the file named @@ -397,10 +859,11 @@ read_name_map (pfile, dirname) cpp_reader *pfile; const char *dirname; { - register struct file_name_map_list *map_list_ptr; + struct file_name_map_list *map_list_ptr; char *name; FILE *f; + /* Check the cache of directories, and mappings in their remap file. */ for (map_list_ptr = CPP_OPTION (pfile, map_list); map_list_ptr; map_list_ptr = map_list_ptr->map_list_next) if (! strcmp (map_list_ptr->map_list_name, dirname)) @@ -410,15 +873,18 @@ read_name_map (pfile, dirname) xmalloc (sizeof (struct file_name_map_list))); map_list_ptr->map_list_name = xstrdup (dirname); + /* The end of the list ends in NULL. */ + map_list_ptr->map_list_map = NULL; + name = (char *) alloca (strlen (dirname) + strlen (FILE_NAME_MAP_FILE) + 2); strcpy (name, dirname); if (*dirname) strcat (name, "/"); strcat (name, FILE_NAME_MAP_FILE); f = fopen (name, "r"); - if (!f) - map_list_ptr->map_list_map = (struct file_name_map *)-1; - else + + /* Silently return NULL if we cannot open. */ + if (f) { int ch; int dirlen = strlen (dirname); @@ -440,7 +906,7 @@ read_name_map (pfile, dirname) ptr->map_from = from; /* Make the real filename absolute. */ - if (*to == '/') + if (IS_ABSOLUTE_PATHNAME (to)) ptr->map_to = to; else { @@ -461,32 +927,39 @@ read_name_map (pfile, dirname) fclose (f); } + /* Add this information to the cache. */ map_list_ptr->map_list_next = CPP_OPTION (pfile, map_list); CPP_OPTION (pfile, map_list) = map_list_ptr; return map_list_ptr->map_list_map; } -/* Remap NAME based on the file_name_map (if any) for LOC. */ - +/* Remap an unsimplified path NAME based on the file_name_map (if any) + for LOC. */ static char * remap_filename (pfile, name, loc) cpp_reader *pfile; char *name; - struct file_name_list *loc; + struct search_path *loc; { struct file_name_map *map; - const char *from, *p, *dir; + const char *from, *p; + char *dir; if (! loc->name_map) - loc->name_map = read_name_map (pfile, - loc->name - ? loc->name : "."); - - if (loc->name_map == (struct file_name_map *)-1) - return name; + { + /* Get a null-terminated path. */ + char *dname = alloca (loc->len + 1); + memcpy (dname, loc->name, loc->len); + dname[loc->len] = '\0'; + + loc->name_map = read_name_map (pfile, dname); + if (! loc->name_map) + return name; + } - from = name + strlen (loc->name) + 1; + /* This works since NAME has not been simplified yet. */ + from = name + loc->len + 1; for (map = loc->name_map; map; map = map->map_next) if (!strcmp (map->map_from, from)) @@ -498,423 +971,48 @@ remap_filename (pfile, name, loc) /usr/include/sys/header.gcc. */ p = strrchr (name, '/'); if (!p) - p = name; - if (loc && loc->name - && strlen (loc->name) == (size_t) (p - name) - && !strncmp (loc->name, name, p - name)) - /* FILENAME is in SEARCHPTR, which we've already checked. */ return name; + /* We know p != name as absolute paths don't call remap_filename. */ if (p == name) - { - dir = "."; - from = name; - } - else - { - char * newdir = (char *) alloca (p - name + 1); - memcpy (newdir, name, p - name); - newdir[p - name] = '\0'; - dir = newdir; - from = p + 1; - } + cpp_ice (pfile, "absolute file name in remap_filename"); + + dir = (char *) alloca (p - name + 1); + memcpy (dir, name, p - name); + dir[p - name] = '\0'; + from = p + 1; for (map = read_name_map (pfile, dir); map; map = map->map_next) - if (! strcmp (map->map_from, name)) + if (! strcmp (map->map_from, from)) return map->map_to; return name; } - -void -_cpp_execute_include (pfile, f, len, no_reinclude, search_start) - cpp_reader *pfile; - U_CHAR *f; - unsigned int len; - int no_reinclude; - struct file_name_list *search_start; -{ - IHASH *ihash; - char *fname = (char *)f; - int fd; - int angle_brackets = fname[0] == '<'; - int before; - - if (!search_start) - { - if (angle_brackets) - search_start = CPP_OPTION (pfile, bracket_include); - else if (CPP_OPTION (pfile, ignore_srcdir)) - search_start = CPP_OPTION (pfile, quote_include); - else - search_start = CPP_BUFFER (pfile)->actual_dir; - } - - if (!search_start) - { - cpp_error (pfile, "No include path in which to find %s", fname); - return; - } - - /* Remove quote marks. */ - fname++; - len -= 2; - fname[len] = '\0'; - - fd = find_include_file (pfile, fname, search_start, &ihash, &before); - - if (fd == -2) - return; - - if (fd == -1) - { - if (CPP_OPTION (pfile, print_deps_missing_files) - && CPP_PRINT_DEPS (pfile) > (angle_brackets || - (pfile->system_include_depth > 0))) - { - if (!angle_brackets) - deps_add_dep (pfile->deps, fname); - else - { - char *p; - struct file_name_list *ptr; - /* If requested as a system header, assume it belongs in - the first system header directory. */ - if (CPP_OPTION (pfile, bracket_include)) - ptr = CPP_OPTION (pfile, bracket_include); - else - ptr = CPP_OPTION (pfile, quote_include); - - p = (char *) alloca (strlen (ptr->name) - + strlen (fname) + 2); - if (*ptr->name != '\0') - { - strcpy (p, ptr->name); - strcat (p, "/"); - } - strcat (p, fname); - deps_add_dep (pfile->deps, p); - } - } - /* If -M was specified, and this header file won't be added to - the dependency list, then don't count this as an error, - because we can still produce correct output. Otherwise, we - can't produce correct output, because there may be - dependencies we need inside the missing file, and we don't - know what directory this missing file exists in. */ - else if (CPP_PRINT_DEPS (pfile) - && (CPP_PRINT_DEPS (pfile) - <= (angle_brackets || (pfile->system_include_depth > 0)))) - cpp_warning (pfile, "No include path in which to find %s", fname); - else - cpp_error_from_errno (pfile, fname); - - return; - } - - /* For -M, add the file to the dependencies on its first inclusion. */ - if (!before && (CPP_PRINT_DEPS (pfile) - > (angle_brackets || (pfile->system_include_depth > 0)))) - deps_add_dep (pfile->deps, ihash->name); - - /* Handle -H option. */ - if (CPP_OPTION (pfile, print_include_names)) - { - cpp_buffer *fp = CPP_BUFFER (pfile); - while ((fp = CPP_PREV_BUFFER (fp)) != NULL) - putc ('.', stderr); - fprintf (stderr, " %s\n", ihash->name); - } - - /* Actually process the file. */ - if (no_reinclude) - ihash->control_macro = U""; - - if (read_include_file (pfile, fd, ihash)) - { - if (angle_brackets) - pfile->system_include_depth++; - } -} - - -/* Push an input buffer and load it up with the contents of FNAME. - If FNAME is "" or NULL, read standard input. */ -int -cpp_read_file (pfile, fname) - cpp_reader *pfile; - const char *fname; -{ - IHASH *ih, **slot; - IHASH dummy; - int f; - - if (fname == NULL) - fname = ""; - - dummy.nshort = fname; - /* _cpp_calc_hash doesn't like zero-length strings. */ - if (*fname == 0) - dummy.hash = 0; - else - dummy.hash = _cpp_calc_hash ((const U_CHAR *)fname, strlen (fname)); - slot = (IHASH **) htab_find_slot_with_hash (pfile->all_include_files, - (const void *) &dummy, - dummy.hash, INSERT); - if (*slot && (ih = redundant_include_p (pfile, *slot, ABSOLUTE_PATH))) - { - if (ih == (IHASH *) -1) - return 1; /* Already included. */ - } - else - ih = make_IHASH (fname, 0, ABSOLUTE_PATH, dummy.hash, slot); - - if (*fname == '\0') - f = 0; - else - f = open_include_file (pfile, fname); - - return read_include_file (pfile, f, ih); -} - -/* Read the contents of FD into the buffer on the top of PFILE's stack. - IHASH points to the include hash entry for the file associated with - FD. - - The caller is responsible for the cpp_push_buffer. */ - +/* Returns true if it is safe to remove the final component of path, + when it is followed by a ".." component. We use lstat to avoid + symlinks if we have it. If not, we can still catch errors with + stat (). */ static int -read_include_file (pfile, fd, ihash) - cpp_reader *pfile; - int fd; - IHASH *ihash; +remove_component_p (path) + const char *path; { - struct stat st; - ssize_t length; - cpp_buffer *fp; + struct stat s; + int result; - fp = cpp_push_buffer (pfile, NULL, 0); - - if (fp == 0) - goto push_fail; - - if (fstat (fd, &st) < 0) - goto perror_fail; - - /* If fd points to a plain file, we might be able to mmap it; we can - definitely allocate the buffer all at once. If fd is a pipe or - terminal, we can't do either. If fd is something weird, like a - block device or a directory, we don't want to read it at all. - - Unfortunately, different systems use different st.st_mode values - for pipes: some have S_ISFIFO, some S_ISSOCK, some are buggy and - zero the entire struct stat except a couple fields. Hence we don't - even try to figure out what something is, except for plain files, - directories, and block devices. */ - - if (S_ISREG (st.st_mode)) - { - ssize_t st_size; - - /* off_t might have a wider range than ssize_t - in other words, - the max size of a file might be bigger than the address - space. We can't handle a file that large. (Anyone with - a single source file bigger than 2GB needs to rethink - their coding style.) */ - if (st.st_size > SSIZE_MAX) - { - cpp_error (pfile, "%s is too large", ihash->name); - goto fail; - } - st_size = st.st_size; - length = read_file (fp, fd, st_size); - if (length == -1) - goto perror_fail; - if (length < st_size) - cpp_warning (pfile, "%s is shorter than expected\n", ihash->name); - } - else if (S_ISBLK (st.st_mode)) - { - cpp_error (pfile, "%s is a block device", ihash->name); - goto fail; - } - else if (S_ISDIR (st.st_mode)) - { - cpp_error (pfile, "%s is a directory", ihash->name); - goto fail; - } - else - { - /* 8 kilobytes is a sensible starting size. It ought to be - bigger than the kernel pipe buffer, and it's definitely - bigger than the majority of C source files. */ - length = read_with_read (fp, fd, 8 * 1024); - if (length == -1) - goto perror_fail; - } - - /* These must be set before prescan. */ - fp->ihash = ihash; - fp->nominal_fname = ihash->name; - - if (length == 0) - ihash->control_macro = U""; /* never re-include */ - else - /* Temporary - I hope. */ - length = _cpp_prescan (pfile, fp, length); - - fp->rlimit = fp->buf + length; - fp->cur = fp->buf; - if (ihash->foundhere != ABSOLUTE_PATH) - fp->system_header_p = ihash->foundhere->sysp; - fp->lineno = 1; - fp->line_base = fp->buf; - - /* The ->actual_dir field is only used when ignore_srcdir is not in effect; - see do_include */ - if (!CPP_OPTION (pfile, ignore_srcdir)) - fp->actual_dir = actual_directory (pfile, ihash->name); - - pfile->input_stack_listing_current = 0; - pfile->only_seen_white = 2; - close (fd); - return 1; - - perror_fail: - cpp_error_from_errno (pfile, ihash->name); - fail: - cpp_pop_buffer (pfile); - push_fail: - close (fd); - return 0; -} - -static ssize_t -read_file (fp, fd, size) - cpp_buffer *fp; - int fd; - ssize_t size; -{ - static int pagesize = -1; - - if (size == 0) - return 0; - - if (pagesize == -1) - pagesize = getpagesize (); - -#if MMAP_THRESHOLD - if (size / pagesize >= MMAP_THRESHOLD) - { - const U_CHAR *result - = (const U_CHAR *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (result != (const U_CHAR *)-1) - { - fp->buf = result; - fp->mapped = 1; - return size; - } - } - /* If mmap fails, try read. If there's really a problem, read will - fail too. */ +#ifdef HAVE_LSTAT + result = lstat (path, &s); +#else + result = stat (path, &s); #endif - return read_with_read (fp, fd, size); -} - -static ssize_t -read_with_read (fp, fd, size) - cpp_buffer *fp; - int fd; - ssize_t size; -{ - ssize_t offset, count; - U_CHAR *buf; - - buf = (U_CHAR *) xmalloc (size); - offset = 0; - while ((count = read (fd, buf + offset, size - offset)) > 0) - { - offset += count; - if (offset == size) - buf = xrealloc (buf, (size *= 2)); - } - if (count < 0) - { - free (buf); - return -1; - } - if (offset == 0) - { - free (buf); - return 0; - } - - if (offset < size) - buf = xrealloc (buf, offset); - fp->buf = buf; - fp->mapped = 0; - return offset; -} - -/* Given a path FNAME, extract the directory component and place it - onto the actual_dirs list. Return a pointer to the allocated - file_name_list structure. These structures are used to implement - current-directory "" include searching. */ - -static struct file_name_list * -actual_directory (pfile, fname) - cpp_reader *pfile; - const char *fname; -{ - char *last_slash, *dir; - size_t dlen; - struct file_name_list *x; - - dir = xstrdup (fname); - last_slash = strrchr (dir, '/'); - if (last_slash) - { - if (last_slash == dir) - { - dlen = 1; - last_slash[1] = '\0'; - } - else - { - dlen = last_slash - dir; - *last_slash = '\0'; - } - } - else - { - dir[0] = '.'; - dir[1] = '\0'; - dlen = 1; - } + /* There's no guarantee that errno will be unchanged, even on + success. Cygwin's lstat(), for example, will often set errno to + ENOSYS. In case of success, reset errno to zero. */ + if (result == 0) + errno = 0; - if (dlen > pfile->max_include_len) - pfile->max_include_len = dlen; - - for (x = pfile->actual_dirs; x; x = x->alloc) - if (!strcmp (x->name, dir)) - { - free (dir); - return x; - } - - /* Not found, make a new one. */ - x = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); - x->name = dir; - x->nlen = dlen; - x->next = CPP_OPTION (pfile, quote_include); - x->alloc = pfile->actual_dirs; - x->sysp = CPP_BUFFER (pfile)->system_header_p; - x->name_map = NULL; - - pfile->actual_dirs = x; - return x; + return result == 0 && S_ISDIR (s.st_mode); } /* Simplify a path name in place, deleting redundant components. This @@ -928,400 +1026,122 @@ actual_directory (pfile, fname) /../quux /quux //quux //quux (POSIX allows leading // as a namespace escape) - Guarantees no trailing slashes. All transforms reduce the length - of the string. - */ -void + Guarantees no trailing slashes. All transforms reduce the length + of the string. Returns PATH. errno is 0 if no error occurred; + nonzero if an error occurred when using stat () or lstat (). */ + +char * _cpp_simplify_pathname (path) char *path; { - char *from, *to; - char *base; - int absolute = 0; +#ifndef VMS + char *from, *to; + char *base, *orig_base; + int absolute = 0; + + errno = 0; + /* Don't overflow the empty path by putting a '.' in it below. */ + if (*path == '\0') + return path; #if defined (HAVE_DOS_BASED_FILE_SYSTEM) - /* Convert all backslashes to slashes. */ - for (from = path; *from; from++) - if (*from == '\\') *from = '/'; + /* Convert all backslashes to slashes. */ + for (from = path; *from; from++) + if (*from == '\\') *from = '/'; - /* Skip over leading drive letter if present. */ - if (ISALPHA (path[0]) && path[1] == ':') - from = to = &path[2]; - else - from = to = path; -#else + /* Skip over leading drive letter if present. */ + if (ISALPHA (path[0]) && path[1] == ':') + from = to = &path[2]; + else from = to = path; +#else + from = to = path; #endif - /* Remove redundant initial /s. */ - if (*from == '/') - { - absolute = 1; - to++; - from++; - if (*from == '/') - { - if (*++from == '/') - /* 3 or more initial /s are equivalent to 1 /. */ - while (*++from == '/'); - else - /* On some hosts // differs from /; Posix allows this. */ - to++; - } - } - base = to; - - for (;;) - { - while (*from == '/') - from++; - - if (from[0] == '.' && from[1] == '/') - from += 2; - else if (from[0] == '.' && from[1] == '\0') - goto done; - else if (from[0] == '.' && from[1] == '.' && from[2] == '/') - { - if (base == to) - { - if (absolute) - from += 3; - else - { - *to++ = *from++; - *to++ = *from++; - *to++ = *from++; - base = to; - } - } - else - { - to -= 2; - while (to > base && *to != '/') to--; - if (*to == '/') - to++; - from += 3; - } - } - else if (from[0] == '.' && from[1] == '.' && from[2] == '\0') - { - if (base == to) - { - if (!absolute) - { - *to++ = *from++; - *to++ = *from++; - } - } - else - { - to -= 2; - while (to > base && *to != '/') to--; - if (*to == '/') - to++; - } - goto done; - } - else - /* Copy this component and trailing /, if any. */ - while ((*to++ = *from++) != '/') - { - if (!to[-1]) - { - to--; - goto done; - } - } - - } - - done: - /* Trim trailing slash */ - if (to[0] == '/' && (!absolute || to > path+1)) - to--; - - /* Change the empty string to "." so that stat() on the result - will always work. */ - if (to == path) - *to++ = '.'; - - *to = '\0'; - - return; -} - -/* It is not clear when this should be used if at all, so I've - disabled it until someone who understands VMS can look at it. */ -#if 0 - -/* Under VMS we need to fix up the "include" specification filename. - - Rules for possible conversions - - fullname tried paths - - name name - ./dir/name [.dir]name - /dir/name dir:name - /name [000000]name, name - dir/name dir:[000000]name, dir:name, dir/name - dir1/dir2/name dir1:[dir2]name, dir1:[000000.dir2]name - path:/name path:[000000]name, path:name - path:/dir/name path:[000000.dir]name, path:[dir]name - path:dir/name path:[dir]name - [path]:[dir]name [path.dir]name - path/[dir]name [path.dir]name - - The path:/name input is constructed when expanding <> includes. */ - - -static void -hack_vms_include_specification (fullname) - char *fullname; -{ - register char *basename, *unixname, *local_ptr, *first_slash; - int f, check_filename_before_returning, must_revert; - char Local[512]; - - check_filename_before_returning = 0; - must_revert = 0; - /* See if we can find a 1st slash. If not, there's no path information. */ - first_slash = strchr (fullname, '/'); - if (first_slash == 0) - return 0; /* Nothing to do!!! */ - - /* construct device spec if none given. */ - - if (strchr (fullname, ':') == 0) + /* Remove redundant leading /s. */ + if (*from == '/') { - - /* If fullname has a slash, take it as device spec. */ - - if (first_slash == fullname) - { - first_slash = strchr (fullname + 1, '/'); /* 2nd slash ? */ - if (first_slash) - *first_slash = ':'; /* make device spec */ - for (basename = fullname; *basename != 0; basename++) - *basename = *(basename+1); /* remove leading slash */ - } - else if ((first_slash[-1] != '.') /* keep ':/', './' */ - && (first_slash[-1] != ':') - && (first_slash[-1] != ']')) /* or a vms path */ + absolute = 1; + to++; + from++; + if (*from == '/') { - *first_slash = ':'; - } - else if ((first_slash[1] == '[') /* skip './' in './[dir' */ - && (first_slash[-1] == '.')) - fullname += 2; - } - - /* Get part after first ':' (basename[-1] == ':') - or last '/' (basename[-1] == '/'). */ - - basename = base_name (fullname); - - local_ptr = Local; /* initialize */ - - /* We are trying to do a number of things here. First of all, we are - trying to hammer the filenames into a standard format, such that later - processing can handle them. - - If the file name contains something like [dir.], then it recognizes this - as a root, and strips the ".]". Later processing will add whatever is - needed to get things working properly. - - If no device is specified, then the first directory name is taken to be - a device name (or a rooted logical). */ - - /* Point to the UNIX filename part (which needs to be fixed!) - but skip vms path information. - [basename != fullname since first_slash != 0]. */ - - if ((basename[-1] == ':') /* vms path spec. */ - || (basename[-1] == ']') - || (basename[-1] == '>')) - unixname = basename; - else - unixname = fullname; - - if (*unixname == '/') - unixname++; - - /* If the directory spec is not rooted, we can just copy - the UNIX filename part and we are done. */ - - if (((basename - fullname) > 1) - && ( (basename[-1] == ']') - || (basename[-1] == '>'))) - { - if (basename[-2] != '.') - { - - /* The VMS part ends in a `]', and the preceding character is not a `.'. - -> PATH]:/name (basename = '/name', unixname = 'name') - We strip the `]', and then splice the two parts of the name in the - usual way. Given the default locations for include files, - we will only use this code if the user specifies alternate locations - with the /include (-I) switch on the command line. */ - - basename -= 1; /* Strip "]" */ - unixname--; /* backspace */ - } - else - { - - /* The VMS part has a ".]" at the end, and this will not do. Later - processing will add a second directory spec, and this would be a syntax - error. Thus we strip the ".]", and thus merge the directory specs. - We also backspace unixname, so that it points to a '/'. This inhibits the - generation of the 000000 root directory spec (which does not belong here - in this case). */ - - basename -= 2; /* Strip ".]" */ - unixname--; /* backspace */ + if (*++from == '/') + /* 3 or more initial /s are equivalent to 1 /. */ + while (*++from == '/'); + else + /* On some hosts // differs from /; Posix allows this. */ + to++; } } - else - + base = orig_base = to; + for (;;) { + int move_base = 0; - /* We drop in here if there is no VMS style directory specification yet. - If there is no device specification either, we make the first dir a - device and try that. If we do not do this, then we will be essentially - searching the users default directory (as if they did a #include "asdf.h"). - - Then all we need to do is to push a '[' into the output string. Later - processing will fill this in, and close the bracket. */ - - if ((unixname != fullname) /* vms path spec found. */ - && (basename[-1] != ':')) - *local_ptr++ = ':'; /* dev not in spec. take first dir */ - - *local_ptr++ = '['; /* Open the directory specification */ - } - - if (unixname == fullname) /* no vms dir spec. */ - { - must_revert = 1; - if ((first_slash != 0) /* unix dir spec. */ - && (*unixname != '/') /* not beginning with '/' */ - && (*unixname != '.')) /* or './' or '../' */ - *local_ptr++ = '.'; /* dir is local ! */ - } - - /* at this point we assume that we have the device spec, and (at least - the opening "[" for a directory specification. We may have directories - specified already. + while (*from == '/') + from++; - If there are no other slashes then the filename will be - in the "root" directory. Otherwise, we need to add - directory specifications. */ + if (*from == '\0') + break; - if (strchr (unixname, '/') == 0) - { - /* if no directories specified yet and none are following. */ - if (local_ptr[-1] == '[') + if (*from == '.') { - /* Just add "000000]" as the directory string */ - strcpy (local_ptr, "000000]"); - local_ptr += strlen (local_ptr); - check_filename_before_returning = 1; /* we might need to fool with this later */ - } - } - else - { - - /* As long as there are still subdirectories to add, do them. */ - while (strchr (unixname, '/') != 0) - { - /* If this token is "." we can ignore it - if it's not at the beginning of a path. */ - if ((unixname[0] == '.') && (unixname[1] == '/')) + if (from[1] == '\0') + break; + if (from[1] == '/') { - /* remove it at beginning of path. */ - if ( ((unixname == fullname) /* no device spec */ - && (fullname+2 != basename)) /* starts with ./ */ - /* or */ - || ((basename[-1] == ':') /* device spec */ - && (unixname-1 == basename))) /* and ./ afterwards */ - *local_ptr++ = '.'; /* make '[.' start of path. */ - unixname += 2; + from += 2; continue; } - - /* Add a subdirectory spec. Do not duplicate "." */ - if ( local_ptr[-1] != '.' - && local_ptr[-1] != '[' - && local_ptr[-1] != '<') - *local_ptr++ = '.'; - - /* If this is ".." then the spec becomes "-" */ - if ( (unixname[0] == '.') - && (unixname[1] == '.') - && (unixname[2] == '/')) + else if (from[1] == '.' && (from[2] == '/' || from[2] == '\0')) { - /* Add "-" and skip the ".." */ - if ((local_ptr[-1] == '.') - && (local_ptr[-2] == '[')) - local_ptr--; /* prevent [.- */ - *local_ptr++ = '-'; - unixname += 3; - continue; + /* Don't simplify if there was no previous component. */ + if (absolute && orig_base == to) + { + from += 2; + continue; + } + /* Don't simplify if the previous component was "../", + or if an error has already occurred with (l)stat. */ + if (base != to && errno == 0) + { + /* We don't back up if it's a symlink. */ + *to = '\0'; + if (remove_component_p (path)) + { + while (to > base && *to != '/') + to--; + from += 2; + continue; + } + } + move_base = 1; } - - /* Copy the subdirectory */ - while (*unixname != '/') - *local_ptr++= *unixname++; - - unixname++; /* Skip the "/" */ } - /* Close the directory specification */ - if (local_ptr[-1] == '.') /* no trailing periods */ - local_ptr--; + /* Add the component separator. */ + if (to > orig_base) + *to++ = '/'; - if (local_ptr[-1] == '[') /* no dir needed */ - local_ptr--; - else - *local_ptr++ = ']'; - } - - /* Now add the filename. */ - - while (*unixname) - *local_ptr++ = *unixname++; - *local_ptr = 0; - - /* Now append it to the original VMS spec. */ - - strcpy ((must_revert==1)?fullname:basename, Local); - - /* If we put a [000000] in the filename, try to open it first. If this fails, - remove the [000000], and return that name. This provides flexibility - to the user in that they can use both rooted and non-rooted logical names - to point to the location of the file. */ - - if (check_filename_before_returning) - { - f = open (fullname, O_RDONLY|O_NONBLOCK); - if (f >= 0) - { - /* The file name is OK as it is, so return it as is. */ - close (f); - return 1; - } - - /* The filename did not work. Try to remove the [000000] from the name, - and return it. */ - - basename = strchr (fullname, '['); - local_ptr = strchr (fullname, ']') + 1; - strcpy (basename, local_ptr); /* this gets rid of it */ + /* Copy this component until the trailing null or '/'. */ + while (*from != '\0' && *from != '/') + *to++ = *from++; + if (move_base) + base = to; } - - return 1; + + /* Change the empty string to "." so that it is not treated as stdin. + Null terminate. */ + if (to == path) + *to++ = '.'; + *to = '\0'; + + return path; +#else /* VMS */ + errno = 0; + return path; +#endif /* !VMS */ } -#endif /* VMS */