/* Part of CPP library. (include file handling)
- Copyright (C) 1986, 87, 89, 92 - 95, 1998 Free Software Foundation, Inc.
+ Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
+ 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
You should have received a copy of the GNU General Public License
along with this program; if not, write to the Free Software
-Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
-
- In other words, you are welcome to use, share and improve this program.
- You are forbidden to forbid anyone else to use, share and improve
- what you give them. Help stamp out software-hoarding! */
+Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "config.h"
#include "system.h"
+#include <dirent.h>
+#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 <memcheck.h>
+# else
+# include <valgrind.h>
+# endif
+#else
+/* Avoid #ifdef:s when we can help it. */
+#define VALGRIND_DISCARD(x)
+#endif
+
+#ifdef HAVE_MMAP_FILE
+# include <sys/mman.h>
+# 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 <windows.h>
+ /* 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
+# define MMAP_THRESHOLD 0
+#endif
+
+#ifndef O_BINARY
+# define O_BINARY 0
+#endif
-/* The entry points to this file are: find_include_file, finclude,
- append_include_chain, deps_output, and file_cleanup.
- file_cleanup is only called through CPP_BUFFER(pfile)->cleanup,
- so it's static anyway. */
-
-static void add_import PROTO ((cpp_reader *, int, char *));
-static int lookup_import PROTO ((cpp_reader *, char *,
- struct file_name_list *));
-static int redundant_include_p PROTO ((cpp_reader *, char *));
-static struct file_name_map *read_name_map PROTO ((cpp_reader *, char *));
-static char *read_filename_string PROTO ((int, FILE *));
-static int open_include_file PROTO ((cpp_reader *, char *,
- struct file_name_list *));
-static int safe_read PROTO ((int, char *, int));
-
-/* Not safe to prototype these. */
-extern char *xmalloc();
-extern char *xrealloc();
-
-/* Append a chain of `struct file_name_list's
- to the end of the main include chain.
- FIRST is the beginning of the chain to append, and LAST is the end. */
+/* 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 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 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
-append_include_chain (pfile, first, last)
+_cpp_init_includes (pfile)
cpp_reader *pfile;
- struct file_name_list *first, *last;
{
- struct cpp_options *opts = CPP_OPTIONS (pfile);
- struct file_name_list *dir;
-
- if (!first || !last)
- return;
+ pfile->all_include_files
+ = splay_tree_new ((splay_tree_compare_fn) strcmp,
+ (splay_tree_delete_key_fn) free,
+ destroy_node);
+}
- if (opts->include == 0)
- opts->include = first;
- else
- opts->last_include->next = first;
+/* Tear down the splay tree. */
+void
+_cpp_cleanup_includes (pfile)
+ cpp_reader *pfile;
+{
+ splay_tree_delete (pfile->all_include_files);
+}
- if (opts->first_bracket_include == 0)
- opts->first_bracket_include = first;
+/* 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;
- for (dir = first; ; dir = dir->next) {
- int len = strlen (dir->fname) + INCLUDE_LEN_FUDGE;
- if (len > pfile->max_include_len)
- pfile->max_include_len = len;
- if (dir == last)
- break;
- }
+ if (f)
+ {
+ purge_cache (f);
+ free (f);
+ }
+}
- last->next = NULL;
- opts->last_include = last;
+/* Mark a file to not be reread (e.g. #import, read failure). */
+void
+_cpp_never_reread (file)
+ struct include_file *file;
+{
+ file->cmacro = NEVER_REREAD;
}
-/* Add output to `deps_buffer' for the -M switch.
- STRING points to the text to be output.
- SPACER is ':' for targets, ' ' for dependencies, zero for text
- to be inserted literally. */
+/* 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;
+{
+ 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;
+}
+/* Enter a file name in the splay tree, for the sake of cpp_included. */
void
-deps_output (pfile, string, spacer)
+_cpp_fake_include (pfile, fname)
cpp_reader *pfile;
- char *string;
- int spacer;
+ const char *fname;
{
- int size;
+ find_or_create_entry (pfile, fname);
+}
- if (!*string)
- return;
+/* 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;
-#ifdef VMS
- hack_vms_include_specification (string);
-#endif
+ 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).
- size = strlen (string);
+ Use the three-argument form of open even though we aren't
+ specifying O_CREAT, to defend against broken system headers.
-#ifndef MAX_OUTPUT_COLUMNS
-#define MAX_OUTPUT_COLUMNS 72
+ O_BINARY tells some runtime libraries (notably DJGPP) not to do
+ newline translation; we can handle DOS line breaks just fine
+ ourselves.
+
+ Special case: the empty string is translated to stdin. */
+
+ if (filename[0] == '\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
- if (spacer
- && pfile->deps_column > 0
- && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS)
+ }
+ else
+ file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
+
+ if (file->fd != -1 && fstat (file->fd, &file->st) == 0)
{
- deps_output (pfile, " \\\n ", 0);
- pfile->deps_column = 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;
}
- if (pfile->deps_size + size + 8 > pfile->deps_allocated_size)
+ 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))
{
- pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2;
- pfile->deps_buffer = (char *) xrealloc (pfile->deps_buffer,
- pfile->deps_allocated_size);
+ char *f = xstrdup (filename);
+ cpp_simplify_path (f);
+ file->header_name = f;
+ return file;
}
- if (spacer == ' ' && pfile->deps_column > 0)
- pfile->deps_buffer[pfile->deps_size++] = ' ';
- bcopy (string, &pfile->deps_buffer[pfile->deps_size], size);
- pfile->deps_size += size;
- pfile->deps_column += size;
- if (spacer == ':')
- pfile->deps_buffer[pfile->deps_size++] = ':';
- pfile->deps_buffer[pfile->deps_size] = 0;
+ close (file->fd);
+ file->fd = -1;
+ return NULL;
}
-static int
-file_cleanup (pbuf, pfile)
- cpp_buffer *pbuf;
- cpp_reader *pfile ATTRIBUTE_UNUSED;
+
+/* 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 (pbuf->buf)
+ if (filename[0] != '\0'
+ && pfile->cb.valid_pch != NULL)
{
- free (pbuf->buf);
- pbuf->buf = 0;
+ 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)
+ {
+ 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;
+ }
}
- return 0;
+ return open_file (pfile, filename);
}
-int
-find_include_file (pfile, fbeg, flen, fname,
- importing, search_start, foundhere)
+/* 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;
- char *fbeg;
- unsigned long flen;
- char *fname;
- int importing;
- struct file_name_list *search_start;
- struct file_name_list **foundhere;
+ struct include_file *inc;
{
- struct file_name_list *searchptr;
- int f;
-
- /* If specified file name is absolute, just open it. */
-
- if (*fbeg == '/')
- {
- strcpy (fname, fbeg);
-#ifdef VMS
- hack_vms_include_specification (fname);
+ 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))
+ {
+ /* If an error occurs, do not try to read this file again. */
+ _cpp_never_reread (inc);
+ return false;
+ }
+ /* 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 (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 = "<stdin>";
+ _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 (redundant_include_p (pfile, fname))
- return -2;
- if (importing)
- f = lookup_import (pfile, fname, NULL_PTR);
- else
- f = open_include_file (pfile, fname, NULL_PTR);
- if (f == -2)
- return -2; /* Already included this file */
- }
+
+ 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, 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
- {
- /* Search directory path, trying to open the file.
- Copy each filename tried into FNAME. */
+ {
+ /* 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 = (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);
- for (searchptr = search_start; searchptr; searchptr = searchptr->next)
+ /* 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)
{
- unsigned int l = 0;
- if (searchptr->fname)
- {
- /* The empty string in a search path is ignored.
- This makes it possible to turn off entirely
- a standard piece of the list. */
- if (searchptr->fname[0] == 0)
- continue;
-
- l = strlen (searchptr->fname);
-
- bcopy (searchptr->fname, fname, l);
- fname[l++] = '/';
- }
-
- bcopy (fbeg, &fname[l], flen);
- fname[flen+l] = '\0';
-#ifdef VMS
- hack_vms_include_specification (fname);
-#endif /* VMS */
- /* ??? There are currently 3 separate mechanisms for avoiding processing
- of redundant include files: #import, #pragma once, and
- redundant_include_p. It would be nice if they were unified. */
- if (redundant_include_p (pfile, fname))
- return -2;
- if (importing)
- f = lookup_import (pfile, fname, searchptr);
+#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
- f = open_include_file (pfile, fname, searchptr);
- if (f == -2)
- return -2; /* Already included this file */
-#ifdef EACCES
- else if (f == -1 && errno == EACCES)
- cpp_warning (pfile, "Header file %s exists, but is not readable",
- fname);
#endif
- if (f >= 0)
- break;
+ free ((PTR) inc->buffer);
+ inc->buffer = NULL;
+ }
+}
+
+/* Return 1 if the file named by FNAME has been included before in
+ any context, 0 otherwise. */
+int
+cpp_included (pfile, fname)
+ cpp_reader *pfile;
+ const char *fname;
+{
+ struct cpp_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);
}
- }
- if (f < 0)
+ /* Search directory path for the file. */
+ name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
+ for (path = pfile->quote_include; path; path = path->next)
{
- /* A file that was not found. */
- bcopy (fbeg, fname, flen);
- fname[flen] = 0;
+ 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;
- return -1;
+ nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n);
+ if (nd && nd->value)
+ return 1;
}
+ return 0;
+}
+
+/* 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, header, type)
+ cpp_reader *pfile;
+ const cpp_token *header;
+ enum include_type type;
+{
+ 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)
{
- /* Check to see if this include file is a once-only include file.
- If so, give up. */
+ cpp_error (pfile, DL_ERROR, "no include path in which to find %s",
+ fname);
+ return NO_INCLUDE_PATH;
+ }
- struct file_name_list *ptr;
+ /* Search directory path for the file. */
+ name = (char *) alloca (strlen (fname) + pfile->max_include_len + 2);
+ for (; path; path = path->next)
+ {
+ 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))
+ n = remap_filename (pfile, name, path);
+ else
+ n = name;
- for (ptr = pfile->dont_repeat_files; ptr; ptr = ptr->next)
- if (!strcmp (ptr->fname, fname))
- {
- close (f);
- return -2; /* This file was once'd. */
- }
+ file = open_file_pch (pfile, n);
+ if (file)
+ {
+ file->foundhere = path;
+ return file;
+ }
}
- /* Record file on "seen" list for #import. */
- add_import (pfile, f, fname);
+ return 0;
+}
+
+/* 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;
- *foundhere = searchptr;
- return f;
+ /* 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);
}
-/* Return nonzero if there is no need to include file NAME
- because it has already been included and it contains a conditional
- to make a repeated include do nothing. */
+/* Allow the client to change the current file. Used by the front end
+ to achieve pseudo-file names like <built-in>.
+ If REASON is LC_LEAVE, then NEW_NAME must be NULL. */
+void
+cpp_change_file (pfile, reason, new_name)
+ cpp_reader *pfile;
+ enum lc_reason reason;
+ const char *new_name;
+{
+ _cpp_do_file_change (pfile, reason, new_name, 1, 0);
+}
-static int
-redundant_include_p (pfile, name)
+/* Report on all files that might benefit from a multiple include guard.
+ Triggered by -H. */
+void
+_cpp_report_missing_guards (pfile)
cpp_reader *pfile;
- char *name;
{
- struct file_name_list *l = pfile->all_include_files;
- for (; l; l = l->next)
- if (! strcmp (name, l->fname)
- && l->control_macro
- && cpp_lookup (pfile, l->control_macro, -1, -1))
- return 1;
+ int banner = 0;
+ splay_tree_foreach (pfile->all_include_files, report_missing_guard,
+ (PTR) &banner);
+}
+
+/* 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 (f && f->cmacro == 0 && f->include_count == 1)
+ {
+ if (*bannerp == 0)
+ {
+ fputs (_("Multiple include guards may be useful for:\n"), stderr);
+ *bannerp = 1;
+ }
+ fputs (f->name, stderr);
+ putc ('\n', stderr);
+ }
return 0;
}
+/* 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_errno (pfile, CPP_OPTION (pfile, deps.style) && ! print_dep
+ ? DL_WARNING: DL_ERROR, 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 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);
-/* Maintain and search list of included files, for #import. */
+ if (type == IT_IMPORT)
+ _cpp_never_reread (inc);
+ }
-/* Hash a file name for import_hash_table. */
+ return stacked;
+}
-static int
-import_hash (f)
- char *f;
+/* 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;
{
- int val = 0;
+ struct include_file *inc = find_include_file (pfile, header, 0);
+
+ if (inc == NULL || inc == NO_INCLUDE_PATH)
+ return -1;
+
+ if (inc->fd > 0)
+ {
+ close (inc->fd);
+ inc->fd = -1;
+ }
- while (*f) val += *f++;
- return (val%IMPORT_HASH_SIZE);
+ return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime;
}
-/* Search for file FILENAME in import_hash_table.
- Return -2 if found, either a matching name or a matching inode.
- Otherwise, open the file and return a file descriptor if successful
- or -1 if unsuccessful. */
-static int
-lookup_import (pfile, filename, searchptr)
+/* 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;
- char *filename;
- struct file_name_list *searchptr;
+ const char *fname;
{
- struct import_file *i;
- int h;
- int hashval;
- struct stat sb;
- int fd;
-
- hashval = import_hash (filename);
-
- /* Attempt to find file in list of already included files */
- i = pfile->import_hash_table[hashval];
-
- while (i) {
- if (!strcmp (filename, i->name))
- return -2; /* return found */
- i = i->next;
- }
- /* Open it and try a match on inode/dev */
- fd = open_include_file (pfile, filename, searchptr);
- if (fd < 0)
- return fd;
- fstat (fd, &sb);
- for (h = 0; h < IMPORT_HASH_SIZE; h++) {
- i = pfile->import_hash_table[h];
- while (i) {
- /* Compare the inode and the device.
- Supposedly on some systems the inode is not a scalar. */
- if (!bcmp ((char *) &i->inode, (char *) &sb.st_ino, sizeof (sb.st_ino))
- && i->dev == sb.st_dev) {
- close (fd);
- return -2; /* return found */
- }
- i = i->next;
+ /* 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 (f == NULL)
+ {
+ cpp_errno (pfile, DL_ERROR, fname);
+ return false;
}
- }
- return fd; /* Not found, return open file */
+
+ return stack_include_file (pfile, f);
}
-/* Add the file FNAME, open on descriptor FD, to import_hash_table. */
+/* 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;
-static void
-add_import (pfile, fd, fname)
+ 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++;
+
+ return _cpp_execute_include (pfile, &header, IT_CMDLINE);
+}
+
+/* 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);
+}
+
+/* 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;
- int fd;
- char *fname;
+ enum include_type type;
{
- struct import_file *i;
- int hashval;
- struct stat sb;
-
- hashval = import_hash (fname);
- fstat (fd, &sb);
- i = (struct import_file *)xmalloc (sizeof (struct import_file));
- i->name = (char *)xmalloc (strlen (fname)+1);
- strcpy (i->name, fname);
- bcopy ((char *) &sb.st_ino, (char *) &i->inode, sizeof (sb.st_ino));
- i->dev = sb.st_dev;
- i->next = pfile->import_hash_table[hashval];
- pfile->import_hash_table[hashval] = i;
+ 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 (pfile->quote_ignores_source_dir)
+ return 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 = (char *) buffer->inc->name;
+ if (dlen > 1)
+ dlen--;
+ }
+ else
+ {
+ use_cwd:
+ buffer->dir.name = (char *) ".";
+ dlen = 1;
+ }
+
+ if (dlen > pfile->max_include_len)
+ pfile->max_include_len = dlen;
+
+ buffer->dir.len = dlen;
+ buffer->dir.next = 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
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;
#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;
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)
{
}
/* 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;
- char *dirname;
+ const char *dirname;
{
- register struct file_name_map_list *map_list_ptr;
+ struct file_name_map_list *map_list_ptr;
char *name;
FILE *f;
- for (map_list_ptr = CPP_OPTIONS (pfile)->map_list; map_list_ptr;
+ /* 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))
return map_list_ptr->map_list_map;
map_list_ptr = ((struct file_name_map_list *)
xmalloc (sizeof (struct file_name_map_list)));
- map_list_ptr->map_list_name = savestring (dirname);
+ 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);
strcat (name, "/");
strcat (name, FILE_NAME_MAP_FILE);
f = fopen (name, "r");
- if (!f)
- map_list_ptr->map_list_map = NULL;
- 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_hor_space[ch])
+ while ((ch = getc (f)) != EOF && is_hspace (ch))
;
to = read_filename_string (ch, f);
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;
}
fclose (f);
}
-
- map_list_ptr->map_list_next = CPP_OPTIONS (pfile)->map_list;
- CPP_OPTIONS (pfile)->map_list = map_list_ptr;
-
- return map_list_ptr->map_list_map;
-}
-
-/* Try to open include file FILENAME. SEARCHPTR is the directory
- being tried from the include file search path. This function maps
- filenames on file systems based on information read by
- read_name_map. */
-
-static int
-open_include_file (pfile, filename, searchptr)
- cpp_reader *pfile;
- char *filename;
- struct file_name_list *searchptr;
-{
- if (CPP_OPTIONS (pfile)->remap)
- {
- register struct file_name_map *map;
- register char *from;
- char *p, *dir;
-
- if (searchptr && ! searchptr->got_name_map)
- {
- searchptr->name_map = read_name_map (pfile,
- searchptr->fname
- ? searchptr->fname : ".");
- searchptr->got_name_map = 1;
- }
-
- /* First check the mapping for the directory we are using. */
- if (searchptr && searchptr->name_map)
- {
- from = filename;
- if (searchptr->fname)
- from += strlen (searchptr->fname) + 1;
- for (map = searchptr->name_map; map; map = map->map_next)
- {
- if (! strcmp (map->map_from, from))
- {
- /* Found a match. */
- return open (map->map_to, O_RDONLY, 0666);
- }
- }
- }
- /* Try to find a mapping file for the particular directory we are
- looking in. Thus #include <sys/types.h> will look up sys/types.h
- in /usr/include/header.gcc and look up types.h in
- /usr/include/sys/header.gcc. */
- p = rindex (filename, '/');
- if (! p)
- p = filename;
- if (searchptr
- && searchptr->fname
- && strlen (searchptr->fname) == (size_t) (p - filename)
- && ! strncmp (searchptr->fname, filename, p - filename))
- {
- /* FILENAME is in SEARCHPTR, which we've already checked. */
- return open (filename, O_RDONLY, 0666);
- }
+ /* 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;
- if (p == filename)
- {
- dir = ".";
- from = filename;
- }
- else
- {
- dir = (char *) alloca (p - filename + 1);
- bcopy (filename, dir, p - filename);
- dir[p - filename] = '\0';
- from = p + 1;
- }
- for (map = read_name_map (pfile, dir); map; map = map->map_next)
- if (! strcmp (map->map_from, from))
- return open (map->map_to, O_RDONLY, 0666);
- }
-
- return open (filename, O_RDONLY, 0666);
+ return map_list_ptr->map_list_map;
}
-/* Process the contents of include file FNAME, already open on descriptor F,
- with output to OP.
- SYSTEM_HEADER_P is 1 if this file resides in any one of the known
- "system" include directories (as decided by the `is_system_include'
- function above).
- DIRPTR is the link in the dir path through which this file was found,
- or 0 if the file name was absolute or via the current directory.
- Return 1 on success, 0 on failure.
-
- The caller is responsible for the cpp_push_buffer. */
-
-int
-finclude (pfile, f, fname, system_header_p, dirptr)
+/* 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;
- int f;
- char *fname;
- int system_header_p;
- struct file_name_list *dirptr;
+ char *name;
+ struct cpp_path *loc;
{
- struct stat st;
- size_t st_size;
- long i;
- int length;
- cpp_buffer *fp; /* For input stack frame */
-#if 0
- int missing_newline = 0;
-#endif
+ struct file_name_map *map;
+ const char *from, *p;
+ char *dir;
- if (fstat (f, &st) < 0)
+ if (! loc->name_map)
{
- cpp_perror_with_name (pfile, fname);
- close (f);
- cpp_pop_buffer (pfile);
- return 0;
+ /* 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;
}
- fp = CPP_BUFFER (pfile);
- fp->nominal_fname = fp->fname = fname;
-#if 0
- fp->length = 0;
-#endif
- fp->dir = dirptr;
- fp->system_header_p = system_header_p;
- fp->lineno = 1;
- fp->colno = 1;
- fp->cleanup = file_cleanup;
-
- if (S_ISREG (st.st_mode)) {
- st_size = (size_t) st.st_size;
- if (st_size != st.st_size || st_size + 2 < st_size) {
- cpp_error (pfile, "file `%s' too large", fname);
- close (f);
- return 0;
- }
- fp->buf = (U_CHAR *) xmalloc (st_size + 2);
- fp->alimit = fp->buf + st_size + 2;
- fp->cur = fp->buf;
-
- /* Read the file contents, knowing that st_size is an upper bound
- on the number of bytes we can read. */
- length = safe_read (f, fp->buf, st_size);
- fp->rlimit = fp->buf + length;
- if (length < 0) goto nope;
- }
- else if (S_ISDIR (st.st_mode)) {
- cpp_error (pfile, "directory `%s' specified in #include", fname);
- close (f);
- return 0;
- } else {
- /* Cannot count its file size before reading.
- First read the entire file into heap and
- copy them into buffer on stack. */
-
- size_t bsize = 2000;
-
- st_size = 0;
- fp->buf = (U_CHAR *) xmalloc (bsize + 2);
-
- for (;;) {
- i = safe_read (f, fp->buf + st_size, bsize - st_size);
- if (i < 0)
- goto nope; /* error! */
- st_size += i;
- if (st_size != bsize)
- break; /* End of file */
- bsize *= 2;
- fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2);
- }
- fp->cur = fp->buf;
- length = st_size;
- }
-
- if ((length > 0 && fp->buf[length - 1] != '\n')
- /* Backslash-newline at end is not good enough. */
- || (length > 1 && fp->buf[length - 2] == '\\')) {
- fp->buf[length++] = '\n';
-#if 0
- missing_newline = 1;
-#endif
- }
- fp->buf[length] = '\0';
- fp->rlimit = fp->buf + length;
-
- /* Close descriptor now, so nesting does not use lots of descriptors. */
- close (f);
+ /* This works since NAME has not been simplified yet. */
+ from = name + loc->len + 1;
- /* Must do this before calling trigraph_pcp, so that the correct file name
- will be printed in warning messages. */
+ for (map = loc->name_map; map; map = map->map_next)
+ if (!strcmp (map->map_from, from))
+ return map->map_to;
- pfile->input_stack_listing_current = 0;
+ /* Try to find a mapping file for the particular directory we are
+ looking in. Thus #include <sys/types.h> will look up sys/types.h
+ in /usr/include/header.gcc and look up types.h in
+ /usr/include/sys/header.gcc. */
+ p = strrchr (name, '/');
+ if (!p)
+ return name;
-#if 0
- if (!no_trigraphs)
- trigraph_pcp (fp);
-#endif
+ /* We know p != name as absolute paths don't call remap_filename. */
+ if (p == name)
+ cpp_error (pfile, DL_ICE, "absolute file name in remap_filename");
-#if 0
- rescan (op, 0);
+ dir = (char *) alloca (p - name + 1);
+ memcpy (dir, name, p - name);
+ dir[p - name] = '\0';
+ from = p + 1;
- if (missing_newline)
- fp->lineno--;
+ for (map = read_name_map (pfile, dir); map; map = map->map_next)
+ if (! strcmp (map->map_from, from))
+ return map->map_to;
- if (CPP_PEDANTIC (pfile) && missing_newline)
- pedwarn ("file does not end in newline");
+ return name;
+}
- indepth--;
- input_file_stack_tick++;
- free (fp->buf);
-#endif
- return 1;
+/* 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.
- nope:
+ 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;
+ cpp_path *quote, *bracket;
+ int quote_ignores_source_dir;
+{
+ pfile->quote_include = quote;
+ pfile->bracket_include = quote;
+ pfile->quote_ignores_source_dir = quote_ignores_source_dir;
+ pfile->max_include_len = 0;
- cpp_perror_with_name (pfile, fname);
- close (f);
- free (fp->buf);
- return 1;
+ for (; quote; quote = quote->next)
+ {
+ 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;
+ }
}
-/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME,
- retrying if necessary. If MAX_READ_LEN is defined, read at most
- that bytes at a time. Return a negative value if an error occurs,
- otherwise return the actual number of bytes read,
- which must be LEN unless end-of-file was reached. */
-
+/* 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
-safe_read (desc, ptr, len)
- int desc;
- char *ptr;
- int len;
+remove_component_p (path)
+ const char *path;
{
- int left, rcount, nchars;
-
- left = len;
- while (left > 0) {
- rcount = left;
-#ifdef MAX_READ_LEN
- if (rcount > MAX_READ_LEN)
- rcount = MAX_READ_LEN;
-#endif
- nchars = read (desc, ptr, rcount);
- if (nchars < 0)
- {
-#ifdef EINTR
- if (errno == EINTR)
- continue;
-#endif
- return nchars;
- }
- if (nchars == 0)
- break;
- ptr += nchars;
- left -= nchars;
- }
- return len - left;
-}
-
-#ifdef VMS
-
-/* Under VMS we need to fix up the "include" specification filename.
+ struct stat s;
+ int result;
- Rules for possible conversions
+#ifdef HAVE_LSTAT
+ result = lstat (path, &s);
+#else
+ result = stat (path, &s);
+#endif
- fullname tried paths
+ /* 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;
- 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
+ return result == 0 && S_ISDIR (s.st_mode);
+}
- The path:/name input is constructed when expanding <> includes. */
+/* Simplify a path name in place, deleting redundant components. This
+ reduces OS overhead and guarantees that equivalent paths compare
+ the same (modulo symlinks).
+ Transforms made:
+ foo/bar/../quux foo/quux
+ foo/./bar foo/bar
+ foo//bar foo/bar
+ /../quux /quux
+ //quux //quux (POSIX allows leading // as a namespace escape)
-static void
-hack_vms_include_specification (fullname)
- char *fullname;
+ 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_path (path)
+ char *path ATTRIBUTE_UNUSED;
{
- 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 = index (fullname, '/');
- if (first_slash == 0)
- return 0; /* Nothing to do!!! */
-
- /* construct device spec if none given. */
-
- if (index (fullname, ':') == 0)
- {
-
- /* If fullname has a slash, take it as device spec. */
-
- if (first_slash == fullname)
- {
- first_slash = index (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). */
+#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;
- /* 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 in cccp.c,
- 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 */
+ while (*from == '/')
+ from++;
- *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. */
-
- if (index (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 (index (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++ = ']';
- }
+ /* Add the component separator. */
+ if (to > orig_base)
+ *to++ = '/';
- /* 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, 0666);
- 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 = index (fullname, '[');
- local_ptr = index (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 */