X-Git-Url: http://git.sourceforge.jp/view?a=blobdiff_plain;f=gcc%2Fcppfiles.c;h=1ff34ff00846831f8ae736fcc266b44cfe3d4b60;hb=ed4ead76fec2cd562d2c0d7172aa5289bf3113ae;hp=1598a3ee88351a983aa9a6230ec6a452f7f08e00;hpb=6af24d0a730f978321a2c3132560ef33d12b3085;p=pf3gnuchains%2Fgcc-fork.git diff --git a/gcc/cppfiles.c b/gcc/cppfiles.c index 1598a3ee883..1ff34ff0084 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, 2002, 2003 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,17 +22,50 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ #include "config.h" #include "system.h" +#include +#include "coretypes.h" +#include "tm.h" #include "cpplib.h" #include "cpphash.h" #include "intl.h" #include "mkdeps.h" #include "splay-tree.h" +#ifdef ENABLE_VALGRIND_CHECKING +# ifdef HAVE_MEMCHECK_H +# include +# else +# include +# endif +#else +/* Avoid #ifdef:s when we can help it. */ +#define VALGRIND_DISCARD(x) +#endif #ifdef HAVE_MMAP_FILE # include # ifndef MMAP_THRESHOLD # define MMAP_THRESHOLD 3 /* Minimum page count to mmap the file. */ # endif +# if MMAP_THRESHOLD +# define TEST_THRESHOLD(size, pagesize) \ + (size / pagesize >= MMAP_THRESHOLD && (size % pagesize) != 0) + /* Use mmap if the file is big enough to be worth it (controlled + by MMAP_THRESHOLD) and if we can safely count on there being + at least one readable NUL byte after the end of the file's + contents. This is true for all tested operating systems when + the file size is not an exact multiple of the page size. */ +# ifndef __CYGWIN__ +# define SHOULD_MMAP(size, pagesize) TEST_THRESHOLD (size, pagesize) +# else +# define WIN32_LEAN_AND_MEAN +# include + /* Cygwin can't correctly emulate mmap under Windows 9x style systems so + disallow use of mmap on those systems. Windows 9x does not zero fill + memory at EOF and beyond, as required. */ +# define SHOULD_MMAP(size, pagesize) ((GetVersion() & 0x80000000) \ + ? 0 : TEST_THRESHOLD (size, pagesize)) +# endif +# endif #else /* No MMAP_FILE */ # undef MMAP_THRESHOLD @@ -43,102 +76,210 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ # 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 char *header_name; /* the original header found */ + const cpp_hashnode *cmacro; /* macro, if any, preventing reinclusion. */ + const struct cpp_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 */ + unsigned char pch; /* 0: file not known to be a PCH. + 1: file is a PCH + (on return from find_include_file). + 2: file is not and never will be a valid + precompiled header. + 3: file is always a valid precompiled + header. */ +}; + +/* Variable length record files on VMS will have a stat size that includes + record control characters that won't be included in the read size. */ +#ifdef VMS +# define FAB_C_VAR 2 /* variable length records (see Starlet fabdef.h) */ +# define STAT_SIZE_TOO_BIG(ST) ((ST).st_fab_rfm == FAB_C_VAR) +#else +# define STAT_SIZE_TOO_BIG(ST) 0 +#endif + +/* 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) +#define INCLUDE_PCH_P(F) (((F)->pch & 1) != 0) + 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 struct include_file *find_include_file - PARAMS ((cpp_reader *, const char *, - struct file_name_list *)); -static struct include_file *open_include_file - PARAMS ((cpp_reader *, const char *)); + struct cpp_path *)); +static struct cpp_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 struct include_file *validate_pch PARAMS ((cpp_reader *, + const char *, + const char *)); +static struct include_file *open_file_pch PARAMS ((cpp_reader *, + const char *)); static int read_include_file PARAMS ((cpp_reader *, struct include_file *)); -static ssize_t read_with_read PARAMS ((cpp_buffer *, int, ssize_t)); -static ssize_t read_file PARAMS ((cpp_buffer *, int, ssize_t)); - -static void destroy_include_file_node PARAMS ((splay_tree_value)); - -#if 0 -static void hack_vms_include_specification PARAMS ((char *)); -#endif - -#ifndef INCLUDE_LEN_FUDGE -#define INCLUDE_LEN_FUDGE 0 -#endif +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_path. 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); +} -/* We use a splay tree to store information about all the include - files seen in this compilation. The key of each tree node is the - physical path to the file. The value is 0 if the file does not - exist, or a struct include_file pointer. */ +/* Tear down the splay tree. */ +void +_cpp_cleanup_includes (pfile) + cpp_reader *pfile; +{ + splay_tree_delete (pfile->all_include_files); +} +/* Free a node. The path string is automatically freed. */ static void -destroy_include_file_node (v) +destroy_node (v) splay_tree_value v; { - struct include_file *f = (struct include_file *)v; + struct include_file *f = (struct include_file *) v; + if (f) { - if (f->fd != -1) - close (f->fd); + purge_cache (f); free (f); } } +/* Mark a file to not be reread (e.g. #import, read failure). */ void -_cpp_init_include_table (pfile) +_cpp_never_reread (file) + struct include_file *file; +{ + file->cmacro = NEVER_REREAD; +} + +/* Lookup a filename, which is simplified after making a copy, and + create an entry if none exists. */ +static splay_tree_node +find_or_create_entry (pfile, fname) cpp_reader *pfile; + const char *fname; { - pfile->all_include_files - = splay_tree_new ((splay_tree_compare_fn) strcmp, - (splay_tree_delete_key_fn) free, - destroy_include_file_node); + splay_tree_node node; + struct include_file *file; + char *name = xstrdup (fname); + + cpp_simplify_path (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->header_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; } -/* Given a filename, look it up and possibly open it. If the file - does not exist, return NULL. If the file does exist but doesn't - need to be reread, return an include_file entry with fd == -1. - If it needs to be (re)read, return an include_file entry with - fd a file descriptor open on the file. */ +/* Enter a file name in the splay tree, for the sake of cpp_included. */ +void +_cpp_fake_include (pfile, fname) + cpp_reader *pfile; + const char *fname; +{ + find_or_create_entry (pfile, fname); +} +/* 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_include_file (pfile, filename) +open_file (pfile, filename) cpp_reader *pfile; const char *filename; { - splay_tree_node nd; - struct include_file *file = 0; - int fd; + splay_tree_node nd = find_or_create_entry (pfile, filename); + struct include_file *file = (struct include_file *) nd->value; - nd = splay_tree_lookup (pfile->all_include_files, - (splay_tree_key) filename); - - if (nd) + if (file->err_no) { - if (nd->value == 0) - return 0; - - file = (struct include_file *)nd->value; + /* Ugh. handle_missing_header () needs errno to be set. */ + errno = file->err_no; + return 0; + } - if (DO_NOT_REREAD (file)) - { - if (file->fd != -1) - { - close (file->fd); - file->fd = -1; - } - return file; - } + /* Don't reopen an idempotent file. */ + if (DO_NOT_REREAD (file)) + return file; - /* File descriptors are cached for files that might be reread. */ - if (file->fd != -1) - { - lseek (file->fd, 0, SEEK_SET); - 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 @@ -153,49 +294,348 @@ open_include_file (pfile, filename) ourselves. Special case: the empty string is translated to stdin. */ + if (filename[0] == '\0') - fd = 0; + { + file->fd = 0; +#ifdef __DJGPP__ + /* For DJGPP redirected input is opened in text mode. Change it + to binary mode. */ + if (! isatty (file->fd)) + setmode (file->fd, O_BINARY); +#endif + } else - fd = open (filename, O_RDONLY|O_NOCTTY|O_BINARY, 0666); + 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; + close (file->fd); + file->fd = -1; + } + + file->err_no = errno; + return 0; +} + +static struct include_file * +validate_pch (pfile, filename, pchname) + cpp_reader *pfile; + const char *filename; + const char *pchname; +{ + struct include_file * file; + + file = open_file (pfile, pchname); + if (file == NULL) + return NULL; + if ((file->pch & 2) == 0) + file->pch = pfile->cb.valid_pch (pfile, pchname, file->fd); + if (INCLUDE_PCH_P (file)) + { + char *f = xstrdup (filename); + cpp_simplify_path (f); + file->header_name = f; + return file; + } + close (file->fd); + file->fd = -1; + return NULL; +} + - if (fd == -1) +/* Like open_file, but also look for a precompiled header if (a) one exists + and (b) it is valid. */ +static struct include_file * +open_file_pch (pfile, filename) + cpp_reader *pfile; + const char *filename; +{ + if (filename[0] != '\0' + && pfile->cb.valid_pch != NULL) { -#ifdef EACCES - if (errno == EACCES) + size_t namelen = strlen (filename); + char *pchname = alloca (namelen + 5); + struct include_file * file; + splay_tree_node nd; + + memcpy (pchname, filename, namelen); + memcpy (pchname + namelen, ".gch", 5); + + nd = find_or_create_entry (pfile, pchname); + file = (struct include_file *) nd->value; + + if (file != NULL) { - cpp_error (pfile, "included file `%s' exists but is not readable", - filename); + if (stat (file->name, &file->st) == 0 && S_ISDIR (file->st.st_mode)) + { + DIR * thedir; + struct dirent *d; + size_t subname_len = namelen + 64; + char *subname = xmalloc (subname_len); + + thedir = opendir (pchname); + if (thedir == NULL) + return NULL; + memcpy (subname, pchname, namelen + 4); + subname[namelen+4] = '/'; + while ((d = readdir (thedir)) != NULL) + { + if (strlen (d->d_name) + namelen + 7 > subname_len) + { + subname_len = strlen (d->d_name) + namelen + 64; + subname = xrealloc (subname, subname_len); + } + strcpy (subname + namelen + 5, d->d_name); + file = validate_pch (pfile, filename, subname); + if (file) + break; + } + closedir (thedir); + free (subname); + } + else + file = validate_pch (pfile, filename, pchname); + if (file) + return file; } -#endif - /* Nonexistent or inaccessible file. Create a negative node for it. */ - if (nd) + } + return open_file (pfile, filename); +} + +/* 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 bool +stack_include_file (pfile, inc) + cpp_reader *pfile; + struct include_file *inc; +{ + 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)); + + /* Add the file to the dependencies on its first inclusion. */ + if (CPP_OPTION (pfile, deps.style) > !!sysp && !inc->include_count) + { + if (pfile->buffer || CPP_OPTION (pfile, deps.ignore_main_file) == 0) + deps_add_dep (pfile->deps, inc->name); + } + + /* PCH files get dealt with immediately. */ + if (INCLUDE_PCH_P (inc)) + { + pfile->cb.read_pch (pfile, inc->name, inc->fd, inc->header_name); + close (inc->fd); + inc->fd = -1; + return false; + } + + /* Not in cache? */ + if (! inc->buffer) + { + if (read_include_file (pfile, inc)) { - cpp_ice (pfile, - "node for '%s' exists, open failed, error '%s', value %lx\n", - filename, strerror (errno), nd->value); - destroy_include_file_node (nd->value); + /* If an error occurs, do not try to read this file again. */ + _cpp_never_reread (inc); + return false; } - splay_tree_insert (pfile->all_include_files, - (splay_tree_key) xstrdup (filename), 0); - return 0; + /* Mark a regular, zero-length file never-reread. We read it, + NUL-terminate it, and stack it once, 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); + close (inc->fd); + inc->fd = -1; } - /* If we haven't seen this file before, create a positive node for it. */ - if (!nd) + 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++; + + /* Initialize 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; + uchar *buf; +#if MMAP_THRESHOLD + static int pagesize = -1; +#endif + + if (S_ISREG (inc->st.st_mode)) { - file = xnew (struct include_file); - file->cmacro = 0; - file->before = 0; - file->sysp = 0; - file->foundhere = 0; - file->name = xstrdup (filename); - splay_tree_insert (pfile->all_include_files, - (splay_tree_key) file->name, - (splay_tree_value) file); + /* 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, DL_ERROR, "%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 (SHOULD_MMAP (size, pagesize)) + { + buf = (uchar *) mmap (0, size, PROT_READ, MAP_PRIVATE, inc->fd, 0); + if (buf == (uchar *) -1) + goto perror_fail; + + /* We must tell Valgrind that the byte at buf[size] is actually + readable. Discard the handle to avoid handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_READABLE (buf + size, 1)); + + inc->mapped = 1; + } + else +#endif + { + buf = (uchar *) xmalloc (size + 1); + offset = 0; + while (offset < size) + { + count = read (inc->fd, buf + offset, size - offset); + if (count < 0) + goto perror_fail; + if (count == 0) + { + if (!STAT_SIZE_TOO_BIG (inc->st)) + cpp_error (pfile, DL_WARNING, + "%s is shorter than expected", inc->name); + size = offset; + buf = xrealloc (buf, size + 1); + inc->st.st_size = size; + break; + } + offset += count; + } + /* The lexer requires that the buffer be NUL-terminated. */ + buf[size] = '\0'; + } + } + else if (S_ISBLK (inc->st.st_mode)) + { + cpp_error (pfile, DL_ERROR, "%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; - file->fd = fd; - return file; + buf = (uchar *) xmalloc (size + 1); + offset = 0; + while ((count = read (inc->fd, buf + offset, size - offset)) > 0) + { + offset += count; + if (offset == size) + { + size *= 2; + buf = xrealloc (buf, size + 1); + } + } + if (count < 0) + goto perror_fail; + + if (offset + 1 < size) + buf = xrealloc (buf, offset + 1); + + /* The lexer requires that the buffer be NUL-terminated. */ + buf[offset] = '\0'; + inc->st.st_size = offset; + } + + inc->buffer = buf; + return 0; + + perror_fail: + cpp_errno (pfile, DL_ERROR, inc->name); + fail: + return 1; +} + +/* Drop INC's buffer from memory, if we are unlikely to need it again. */ +static void +purge_cache (inc) + struct include_file *inc; +{ + if (inc->buffer) + { +#if MMAP_THRESHOLD + if (inc->mapped) + { + /* Undo the previous annotation for the + known-zero-byte-after-mmap. Discard the handle to avoid + handle leak. */ + VALGRIND_DISCARD (VALGRIND_MAKE_NOACCESS (inc->buffer + + inc->st.st_size, 1)); + 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 @@ -205,429 +645,347 @@ cpp_included (pfile, fname) cpp_reader *pfile; const char *fname; { - struct file_name_list *path; - char *name; + struct cpp_path *path; + char *name, *n; splay_tree_node nd; - if (fname[0] == '/') + 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 + INCLUDE_LEN_FUDGE); - for (path = CPP_OPTION (pfile, quote_include); path; path = path->next) + name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2); + for (path = pfile->quote_include; path; path = path->next) { - memcpy (name, path->name, path->nlen); - name[path->nlen] = '/'; - strcpy (&name[path->nlen+1], fname); - _cpp_simplify_pathname (name); + memcpy (name, path->name, path->len); + name[path->len] = '/'; + strcpy (&name[path->len + 1], fname); if (CPP_OPTION (pfile, remap)) - name = remap_filename (pfile, name, path); + n = remap_filename (pfile, name, path); + else + n = name; - nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name); + nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n); if (nd && nd->value) return 1; } return 0; } -/* Search for include file FNAME in the include chain starting at - SEARCH_START. Return 0 if there is no such file (or it's un-openable), - otherwise an include_file structure, possibly with a file descriptor - open on the file. */ - +/* 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 directive of TYPE #include_next, set INCLUDE_NEXT to true. */ static struct include_file * -find_include_file (pfile, fname, search_start) +find_include_file (pfile, header, type) cpp_reader *pfile; - const char *fname; - struct file_name_list *search_start; + const cpp_token *header; + enum include_type type; { - struct file_name_list *path; - char *name; + const char *fname = (const char *) header->val.str.text; + struct cpp_path *path; struct include_file *file; + char *name, *n; + + if (IS_ABSOLUTE_PATHNAME (fname)) + return open_file_pch (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 = pfile->bracket_include; + else + path = search_from (pfile, type); + + if (path == NULL) + { + cpp_error (pfile, DL_ERROR, "no include path in which to find %s", + fname); + return NO_INCLUDE_PATH; + } - if (fname[0] == '/') - return open_include_file (pfile, fname); - /* Search directory path for the file. */ - name = (char *) alloca (strlen (fname) + pfile->max_include_len - + 2 + INCLUDE_LEN_FUDGE); - for (path = search_start; path; path = path->next) + name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2); + for (; path; path = path->next) { - memcpy (name, path->name, path->nlen); - name[path->nlen] = '/'; - strcpy (&name[path->nlen+1], fname); - _cpp_simplify_pathname (name); + int len = path->len; + memcpy (name, path->name, len); + /* Don't turn / into // or // into ///; // may be a namespace + escape. */ + if (name[len-1] == '/') + len--; + name[len] = '/'; + strcpy (&name[len + 1], fname); if (CPP_OPTION (pfile, remap)) - name = remap_filename (pfile, name, path); + n = remap_filename (pfile, name, path); + else + n = name; - file = open_include_file (pfile, name); + file = open_file_pch (pfile, n); if (file) { - file->sysp = path->sysp; file->foundhere = path; return file; } } + return 0; } -/* #line uses this to save artificial file names. We have to try - opening the file because an all_include_files entry is always - either + or -, there's no 'I don't know' value. */ -const char * -_cpp_fake_include (pfile, fname) +/* 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; - const char *fname; + int syshdr, externc; { - splay_tree_node nd; - struct include_file *file; - char *name; + int flags = 0; - file = find_include_file (pfile, fname, CPP_OPTION (pfile, quote_include)); - if (file) - return file->name; - - name = xstrdup (fname); - _cpp_simplify_pathname (name); - - /* We cannot just blindly insert a node, because there's still the - chance that the node already exists but isn't on the search path. */ - nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) name); - if (nd) - { - free (name); - return (const char *) nd->key; - } - - splay_tree_insert (pfile->all_include_files, (splay_tree_key) name, 0); - return (const char *)name; + /* 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); } -/* Not everyone who wants to set system-header-ness on a buffer can - see the details of struct include_file. This is an exported interface - because fix-header needs it. */ +/* Allow the client to change the current file. Used by the front end + to achieve pseudo-file names like . + If REASON is LC_LEAVE, then NEW_NAME must be NULL. */ void -cpp_make_system_header (pfile, pbuf, flag) +cpp_change_file (pfile, reason, new_name) cpp_reader *pfile; - cpp_buffer *pbuf; - int flag; + enum lc_reason reason; + const char *new_name; { - if (flag < 0 || flag > 2) - cpp_ice (pfile, "cpp_make_system_header: bad flag %d\n", flag); - else if (!pbuf->inc) - cpp_ice (pfile, "cpp_make_system_header called on non-file buffer"); - else - pbuf->inc->sysp = flag; + _cpp_do_file_change (pfile, reason, new_name, 1, 0); } -#define PRINT_THIS_DEP(p, b) (CPP_PRINT_DEPS(p) > (b||p->system_include_depth)) +/* Report on all files that might benefit from a multiple include guard. + Triggered by -H. */ void -_cpp_execute_include (pfile, f, len, no_reinclude, search_start) +_cpp_report_missing_guards (pfile) cpp_reader *pfile; - U_CHAR *f; - unsigned int len; - int no_reinclude; - struct file_name_list *search_start; { - struct include_file *inc; - char *fname = (char *)f; - int angle_brackets = fname[0] == '<'; - - 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'; + int banner = 0; + splay_tree_foreach (pfile->all_include_files, report_missing_guard, + (PTR) &banner); +} - inc = find_include_file (pfile, fname, search_start); +/* Callback function for splay_tree_foreach(). */ +static int +report_missing_guard (n, b) + splay_tree_node n; + void *b; +{ + struct include_file *f = (struct include_file *) n->value; + int *bannerp = (int *) b; - if (inc) + if (f && f->cmacro == 0 && f->include_count == 1) { - if (inc->fd == -1) - return; - - /* For -M, add the file to the dependencies on its first inclusion. */ - if (!inc->before && PRINT_THIS_DEP (pfile, angle_brackets)) - deps_add_dep (pfile->deps, inc->name); - inc->before = 1; - - /* Handle -H option. */ - if (CPP_OPTION (pfile, print_include_names)) + if (*bannerp == 0) { - cpp_buffer *fp = CPP_BUFFER (pfile); - while ((fp = CPP_PREV_BUFFER (fp)) != NULL) - putc ('.', stderr); - fprintf (stderr, " %s\n", inc->name); + fputs (_("Multiple include guards may be useful for:\n"), stderr); + *bannerp = 1; } - - /* Actually process the file. */ - if (no_reinclude) - inc->cmacro = NEVER_REREAD; - - if (read_include_file (pfile, inc)) - { - if (angle_brackets) - pfile->system_include_depth++; - } - return; + fputs (f->name, stderr); + putc ('\n', stderr); } - - if (CPP_OPTION (pfile, print_deps_missing_files) - && PRINT_THIS_DEP (pfile, angle_brackets)) - { - 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); + return 0; +} - p = (char *) alloca (strlen (ptr->name) - + strlen (fname) + 2); - if (*ptr->name != '\0') - { - strcpy (p, ptr->name); - strcat (p, "/"); - } - strcat (p, fname); - _cpp_simplify_pathname (p); - 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) - && ! PRINT_THIS_DEP (pfile, angle_brackets)) - cpp_warning (pfile, "No include path in which to find %s", fname); +/* Create a dependency for file FNAME, or issue an error message as + appropriate. ANGLE_BRACKETS is nonzero if the file was bracketed + like <..>. */ +static void +handle_missing_header (pfile, fname, angle_brackets) + cpp_reader *pfile; + const char *fname; + int angle_brackets; +{ + bool print_dep + = CPP_OPTION (pfile, deps.style) > (angle_brackets || pfile->map->sysp); + + if (CPP_OPTION (pfile, deps.missing_files) && print_dep) + deps_add_dep (pfile->deps, fname); + /* 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. */ else - cpp_error_from_errno (pfile, fname); + cpp_errno (pfile, CPP_OPTION (pfile, deps.style) && ! print_dep + ? DL_WARNING: DL_ERROR, fname); } -/* 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) +/* Handles #include-family directives (distinguished by TYPE), + including HEADER, 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 char *fname; + const cpp_token *header; + enum include_type type; { - struct include_file *f; + bool stacked = false; + struct include_file *inc = find_include_file (pfile, header, type); - if (fname == NULL) - fname = ""; + 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); - f = open_include_file (pfile, fname); + if (type == IT_IMPORT) + _cpp_never_reread (inc); + } - return read_include_file (pfile, f); + return stacked; } -/* Read the file referenced by INC into a new buffer on PFILE's stack. - Return 1 if successful, 0 if not. */ - -static int -read_include_file (pfile, inc) +/* 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; - struct include_file *inc; + const cpp_token *header; { - struct stat st; - ssize_t length; - cpp_buffer *fp; - int fd = inc->fd; + struct include_file *inc = find_include_file (pfile, header, 0); - fp = cpp_push_buffer (pfile, NULL, 0); + if (inc == NULL || inc == NO_INCLUDE_PATH) + return -1; - if (fp == 0) - goto push_fail; + if (inc->fd > 0) + { + close (inc->fd); + inc->fd = -1; + } - if (fstat (fd, &st) < 0) - goto perror_fail; + return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime; +} - /* 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. */ +/* 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; +{ + /* This uses open_file, because we don't allow a PCH to be used as + the toplevel compilation (that would prevent re-compiling an + existing PCH without deleting it first). */ + struct include_file *f = open_file (pfile, fname); - if (S_ISREG (st.st_mode)) + if (f == NULL) { - 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.) 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 (st.st_size > INTTYPE_MAXIMUM (ssize_t)) - { - cpp_error (pfile, "%s is too large", inc->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", inc->name); - } - else if (S_ISBLK (st.st_mode)) - { - cpp_error (pfile, "%s is a block device", inc->name); - goto fail; - } - else if (S_ISDIR (st.st_mode)) - { - cpp_error (pfile, "%s is a directory", 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. */ - length = read_with_read (fp, fd, 8 * 1024); - if (length == -1) - goto perror_fail; + cpp_errno (pfile, DL_ERROR, fname); + return false; } - /* These must be set before prescan. */ - fp->inc = inc; - fp->nominal_fname = inc->name; - - if (length == 0) - inc->cmacro = NEVER_REREAD; - else - /* Temporary - I hope. */ - length = _cpp_prescan (pfile, fp, length); + return stack_include_file (pfile, f); +} - fp->rlimit = fp->buf + length; - fp->cur = fp->buf; - fp->lineno = 1; - fp->line_base = fp->buf; +/* Pushes the given file onto the buffer stack. Returns nonzero if + successful. */ +bool +cpp_push_include (pfile, filename) + cpp_reader *pfile; + const char *filename; +{ + cpp_token header; - /* 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, inc->name); + header.type = CPP_STRING; + header.val.str.text = (const unsigned char *) filename; + header.val.str.len = strlen (filename); + /* Make the command line directive take up a line. */ + pfile->line++; - pfile->input_stack_listing_current = 0; - pfile->only_seen_white = 2; - return 1; + return _cpp_execute_include (pfile, &header, IT_CMDLINE); +} - perror_fail: - cpp_error_from_errno (pfile, inc->name); - /* Do not try to read this file again. */ - close (fd); - inc->fd = -1; - inc->cmacro = NEVER_REREAD; - fail: - cpp_pop_buffer (pfile); - push_fail: - return 0; +/* Do appropriate cleanup when a file INC's buffer is popped off the + input stack. */ +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); } -static ssize_t -read_file (fp, fd, size) - cpp_buffer *fp; - int fd; - ssize_t size; +/* 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 cpp_path * +search_from (pfile, type) + cpp_reader *pfile; + enum include_type type; { - static int pagesize = -1; + cpp_buffer *buffer = pfile->buffer; + unsigned int dlen; - if (size == 0) - return 0; + /* Command line uses the cwd, and does not cache the result. */ + if (type == IT_CMDLINE) + goto use_cwd; - if (pagesize == -1) - pagesize = getpagesize (); + /* Ignore the current file's directory? */ + if (pfile->quote_ignores_source_dir) + return pfile->quote_include; -#if MMAP_THRESHOLD - if (size / pagesize >= MMAP_THRESHOLD) + if (! buffer->search_cached) { - const U_CHAR *result - = (const U_CHAR *) mmap (0, size, PROT_READ, MAP_PRIVATE, fd, 0); - if (result != (const U_CHAR *)-1) + buffer->search_cached = 1; + + dlen = lbasename (buffer->inc->name) - buffer->inc->name; + + if (dlen) { - fp->buf = result; - fp->mapped = 1; - return size; + /* We don't guarantee NAME is null-terminated. This saves + allocating and freeing memory. Drop a trailing '/'. */ + buffer->dir.name = (char *) buffer->inc->name; + if (dlen > 1) + dlen--; + } + else + { + use_cwd: + buffer->dir.name = (char *) "."; + dlen = 1; } - } - /* If mmap fails, try read. If there's really a problem, read will - fail too. */ -#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; + if (dlen > pfile->max_include_len) + pfile->max_include_len = dlen; - 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; + buffer->dir.len = dlen; + buffer->dir.next = pfile->quote_include; + buffer->dir.sysp = pfile->map->sysp; } - if (offset < size) - buf = xrealloc (buf, offset); - fp->buf = buf; - fp->mapped = 0; - return offset; + return &buffer->dir; } /* The file_name_map structure holds a mapping of file names for a @@ -637,9 +995,7 @@ read_with_read (fp, fd, size) such as DOS. The format of the file name map file is just a series of lines with two tokens on each line. The first token is the name to map, and the second token is the actual name to use. */ - -struct file_name_map -{ +struct file_name_map { struct file_name_map *map_next; char *map_from; char *map_to; @@ -648,8 +1004,7 @@ struct file_name_map #define FILE_NAME_MAP_FILE "header.gcc" /* Read a space delimited string of unlimited length from a stdio - file. */ - + file F. */ static char * read_filename_string (ch, f) int ch; @@ -660,10 +1015,10 @@ read_filename_string (ch, f) len = 20; set = alloc = xmalloc (len + 1); - if (! is_space(ch)) + if (! is_space (ch)) { *set++ = ch; - while ((ch = getc (f)) != EOF && ! is_space(ch)) + while ((ch = getc (f)) != EOF && ! is_space (ch)) { if (set - alloc == len) { @@ -680,25 +1035,23 @@ read_filename_string (ch, f) } /* This structure holds a linked list of file name maps, one per directory. */ - -struct file_name_map_list -{ +struct file_name_map_list { struct file_name_map_list *map_list_next; char *map_list_name; struct file_name_map *map_list_map; }; /* Read the file name map file for DIRNAME. */ - static struct file_name_map * 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)) @@ -708,28 +1061,30 @@ 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); while ((ch = getc (f)) != EOF) { char *from, *to; struct file_name_map *ptr; - if (is_space(ch)) + if (is_space (ch)) continue; from = read_filename_string (ch, f); - while ((ch = getc (f)) != EOF && is_hspace(ch)) + while ((ch = getc (f)) != EOF && is_hspace (ch)) ; to = read_filename_string (ch, f); @@ -738,16 +1093,13 @@ 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 { - ptr->map_to = xmalloc (dirlen + strlen (to) + 2); - strcpy (ptr->map_to, dirname); - ptr->map_to[dirlen] = '/'; - strcpy (ptr->map_to + dirlen + 1, to); + ptr->map_to = concat (dirname, "/", to, NULL); free (to); - } + } ptr->map_next = map_list_ptr->map_list_map; map_list_ptr->map_list_map = ptr; @@ -758,34 +1110,41 @@ 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 cpp_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 : "."); + { + /* 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; + } + + /* This works since NAME has not been simplified yet. */ + from = name + loc->len + 1; - if (loc->name_map == (struct file_name_map *)-1) - return name; - - from = name + strlen (loc->name) + 1; - for (map = loc->name_map; map; map = map->map_next) if (!strcmp (map->map_from, from)) return map->map_to; @@ -796,91 +1155,75 @@ 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_error (pfile, DL_ICE, "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; } -/* 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. */ +/* Set the include chain for "" to QUOTE, for <> to BRACKET. If + QUOTE_IGNORES_SOURCE_DIR, then "" includes do not look in the + directory of the including file. -static struct file_name_list * -actual_directory (pfile, fname) + If BRACKET does not lie in the QUOTE chain, it is set to QUOTE. */ +void +cpp_set_include_chains (pfile, quote, bracket, quote_ignores_source_dir) cpp_reader *pfile; - const char *fname; + cpp_path *quote, *bracket; + int quote_ignores_source_dir; { - 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 + pfile->quote_include = quote; + pfile->bracket_include = quote; + pfile->quote_ignores_source_dir = quote_ignores_source_dir; + pfile->max_include_len = 0; + + for (; quote; quote = quote->next) { - dir[0] = '.'; - dir[1] = '\0'; - dlen = 1; + quote->name_map = NULL; + quote->len = strlen (quote->name); + if (quote->len > pfile->max_include_len) + pfile->max_include_len = quote->len; + if (quote == bracket) + pfile->bracket_include = bracket; } +} + +/* 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 +remove_component_p (path) + const char *path; +{ + struct stat s; + int result; + +#ifdef HAVE_LSTAT + result = lstat (path, &s); +#else + result = stat (path, &s); +#endif + + /* 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)->inc->sysp; - 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 @@ -894,400 +1237,118 @@ 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. - */ + 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 (). */ void -_cpp_simplify_pathname (path) - char *path; +cpp_simplify_path (path) + char *path ATTRIBUTE_UNUSED; { - char *from, *to; - char *base; - int absolute = 0; - -#if defined (HAVE_DOS_BASED_FILE_SYSTEM) - /* 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 - 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'; - +#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; -} - -/* 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) - { - - /* 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 */ - { - *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 defined (HAVE_DOS_BASED_FILE_SYSTEM) + /* Convert all backslashes to slashes. */ + for (from = path; *from; from++) + if (*from == '\\') *from = '/'; - if ((basename[-1] == ':') /* vms path spec. */ - || (basename[-1] == ']') - || (basename[-1] == '>')) - unixname = basename; + /* Skip over leading drive letter if present. */ + if (ISALPHA (path[0]) && path[1] == ':') + from = to = &path[2]; 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. */ + from = to = path; +#else + from = to = path; +#endif - if (((basename - fullname) > 1) - && ( (basename[-1] == ']') - || (basename[-1] == '>'))) + /* Remove redundant leading /s. */ + if (*from == '/') { - if (basename[-2] != '.') + absolute = 1; + to++; + from++; + if (*from == '/') { - - /* 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. - - If there are no other slashes then the filename will be - in the "root" directory. Otherwise, we need to add - directory specifications. */ + while (*from == '/') + from++; - if (strchr (unixname, '/') == 0) - { - /* if no directories specified yet and none are following. */ - if (local_ptr[-1] == '[') - { - /* 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 - { + if (*from == '\0') + break; - /* As long as there are still subdirectories to add, do them. */ - while (strchr (unixname, '/') != 0) + if (*from == '.') { - /* 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--; - - 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); + /* Add the component separator. */ + if (to > orig_base) + *to++ = '/'; - /* 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'; +#else /* VMS */ + errno = 0; +#endif /* !VMS */ } -#endif /* VMS */