Commit 8f9b4009 by Neil Booth Committed by Neil Booth

Makefile.in (LIBCPP_DEPS): Add HASHTAB_H.

	* Makefile.in (LIBCPP_DEPS): Add HASHTAB_H.
	* cppfiles.c: Completely rewritten.
	* c-incpath.c (free_path, remove_duplicates, heads, tails, add_path):
	struct cpp_path is now struct cpp_dir.
	(remove_duplicates): Don't simplify path names.
	* c-opts.c (c_common_parse_file): cpp_read_next_file renamed
	cpp_stack_file.
	* cpphash.h: Include hashtab.h.
	(_cpp_file): Declare.
	(struct cpp_buffer): struct include_file is now struct _cpp_file,
	and struct cpp_path is now struct cpp_dir.  Rename members.
	(struct cpp_reader): Similarly.  New members once_only_files,
	file_hash, file_hash_entries, quote_ignores_source_dir,
	no_search_path, saw_pragma_once.  Remove all_include_files and
	max_include_len.  Make some members bool.
	(_cpp_mark_only_only): Renamed from _cpp_never_reread.
	(_cpp_stack_file): Renamed from _cpp_read_file.
	(_cpp_stack_include): Renamed from _cpp_execute_include.
	(_cpp_init_files): Renamed from _cpp_init_includes.
	(_cpp_cleanup_files): Renamed from _cpp_cleanup_includes.
	* cppinit.c (cpp_create_reader): Initialize no_search_path.  Update.
	(cpp_read_next_file): Rename and move to cppfiles.c.
	(cpp_read_main_file): Update.
	* cpplib.c (run_directive): Update for renamed members.
	(do_include_common, _cpp_pop_buffer): Update.
	(do_import): Undeprecate #import.
	(do_pragma_once): Undeprecate.  Use _cpp_mark_file_once_only.
	* cpplib.h: Remove file_name_map_list.
	(cpp_options): Remove map_list.
	(cpp_dir): Rename from cpp_path.  New datatype for name_map.
	(cpp_set_include_chains, cpp_stack_file, cpp_included): Update.
testsuite:
	* gcc.dg/cpp/include2.c: Only expect one message.

From-SVN: r69942
parent ad96995b
2003-07-29 Neil Booth <neil@daikokuya.co.uk>
* Makefile.in (LIBCPP_DEPS): Add HASHTAB_H.
* cppfiles.c: Completely rewritten.
* c-incpath.c (free_path, remove_duplicates, heads, tails, add_path):
struct cpp_path is now struct cpp_dir.
(remove_duplicates): Don't simplify path names.
* c-opts.c (c_common_parse_file): cpp_read_next_file renamed
cpp_stack_file.
* cpphash.h: Include hashtab.h.
(_cpp_file): Declare.
(struct cpp_buffer): struct include_file is now struct _cpp_file,
and struct cpp_path is now struct cpp_dir. Rename members.
(struct cpp_reader): Similarly. New members once_only_files,
file_hash, file_hash_entries, quote_ignores_source_dir,
no_search_path, saw_pragma_once. Remove all_include_files and
max_include_len. Make some members bool.
(_cpp_mark_only_only): Renamed from _cpp_never_reread.
(_cpp_stack_file): Renamed from _cpp_read_file.
(_cpp_stack_include): Renamed from _cpp_execute_include.
(_cpp_init_files): Renamed from _cpp_init_includes.
(_cpp_cleanup_files): Renamed from _cpp_cleanup_includes.
* cppinit.c (cpp_create_reader): Initialize no_search_path. Update.
(cpp_read_next_file): Rename and move to cppfiles.c.
(cpp_read_main_file): Update.
* cpplib.c (run_directive): Update for renamed members.
(do_include_common, _cpp_pop_buffer): Update.
(do_import): Undeprecate #import.
(do_pragma_once): Undeprecate. Use _cpp_mark_file_once_only.
* cpplib.h: Remove file_name_map_list.
(cpp_options): Remove map_list.
(cpp_dir): Rename from cpp_path. New datatype for name_map.
(cpp_set_include_chains, cpp_stack_file, cpp_included): Update.
2003-07-29 Phil Edwards <pme@gcc.gnu.org> 2003-07-29 Phil Edwards <pme@gcc.gnu.org>
* Makefile.in: Make stamp-objdir safe for parallel builds. * Makefile.in: Make stamp-objdir safe for parallel builds.
......
...@@ -2348,7 +2348,7 @@ LIBCPP_OBJS = cpplib.o cpplex.o cppmacro.o cppexp.o cppfiles.o cpptrad.o \ ...@@ -2348,7 +2348,7 @@ LIBCPP_OBJS = cpplib.o cpplex.o cppmacro.o cppexp.o cppfiles.o cpptrad.o \
hashtable.o line-map.o mkdeps.o cpppch.o hashtable.o line-map.o mkdeps.o cpppch.o
LIBCPP_DEPS = $(CPPLIB_H) cpphash.h line-map.h hashtable.h intl.h \ LIBCPP_DEPS = $(CPPLIB_H) cpphash.h line-map.h hashtable.h intl.h \
$(OBSTACK_H) $(CONFIG_H) $(SYSTEM_H) $(HASHTAB_H) $(OBSTACK_H) $(CONFIG_H) $(SYSTEM_H)
# Most of the other archives built/used by this makefile are for # Most of the other archives built/used by this makefile are for
# targets. This one is strictly for the host. # targets. This one is strictly for the host.
......
...@@ -45,21 +45,21 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ...@@ -45,21 +45,21 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
static void add_env_var_paths (const char *, int); static void add_env_var_paths (const char *, int);
static void add_standard_paths (const char *, const char *, int); static void add_standard_paths (const char *, const char *, int);
static void free_path (struct cpp_path *, int); static void free_path (struct cpp_dir *, int);
static void merge_include_chains (cpp_reader *, int); static void merge_include_chains (cpp_reader *, int);
static struct cpp_path *remove_duplicates (cpp_reader *, struct cpp_path *, static struct cpp_dir *remove_duplicates (cpp_reader *, struct cpp_dir *,
struct cpp_path *, struct cpp_dir *,
struct cpp_path *, int); struct cpp_dir *, int);
/* Include chains heads and tails. */ /* Include chains heads and tails. */
static struct cpp_path *heads[4]; static struct cpp_dir *heads[4];
static struct cpp_path *tails[4]; static struct cpp_dir *tails[4];
static bool quote_ignores_source_dir; static bool quote_ignores_source_dir;
enum { REASON_QUIET = 0, REASON_NOENT, REASON_DUP, REASON_DUP_SYS }; enum { REASON_QUIET = 0, REASON_NOENT, REASON_DUP, REASON_DUP_SYS };
/* Free an element of the include chain, possibly giving a reason. */ /* Free an element of the include chain, possibly giving a reason. */
static void static void
free_path (struct cpp_path *path, int reason) free_path (struct cpp_dir *path, int reason)
{ {
switch (reason) switch (reason)
{ {
...@@ -169,12 +169,12 @@ add_standard_paths (const char *sysroot, const char *iprefix, int cxx_stdinc) ...@@ -169,12 +169,12 @@ add_standard_paths (const char *sysroot, const char *iprefix, int cxx_stdinc)
JOIN, unless it duplicates JOIN in which case the last path is JOIN, unless it duplicates JOIN in which case the last path is
removed. Return the head of the resulting chain. Any of HEAD, removed. Return the head of the resulting chain. Any of HEAD,
JOIN and SYSTEM can be NULL. */ JOIN and SYSTEM can be NULL. */
static struct cpp_path * static struct cpp_dir *
remove_duplicates (cpp_reader *pfile, struct cpp_path *head, remove_duplicates (cpp_reader *pfile, struct cpp_dir *head,
struct cpp_path *system, struct cpp_path *join, struct cpp_dir *system, struct cpp_dir *join,
int verbose) int verbose)
{ {
struct cpp_path **pcur, *tmp, *cur; struct cpp_dir **pcur, *tmp, *cur;
struct stat st; struct stat st;
for (pcur = &head; *pcur; ) for (pcur = &head; *pcur; )
...@@ -182,7 +182,6 @@ remove_duplicates (cpp_reader *pfile, struct cpp_path *head, ...@@ -182,7 +182,6 @@ remove_duplicates (cpp_reader *pfile, struct cpp_path *head,
int reason = REASON_QUIET; int reason = REASON_QUIET;
cur = *pcur; cur = *pcur;
cpp_simplify_path (cur->name);
if (stat (cur->name, &st)) if (stat (cur->name, &st))
{ {
...@@ -269,7 +268,7 @@ merge_include_chains (cpp_reader *pfile, int verbose) ...@@ -269,7 +268,7 @@ merge_include_chains (cpp_reader *pfile, int verbose)
/* If verbose, print the list of dirs to search. */ /* If verbose, print the list of dirs to search. */
if (verbose) if (verbose)
{ {
struct cpp_path *p; struct cpp_dir *p;
fprintf (stderr, _("#include \"...\" search starts here:\n")); fprintf (stderr, _("#include \"...\" search starts here:\n"));
for (p = heads[QUOTE];; p = p->next) for (p = heads[QUOTE];; p = p->next)
...@@ -304,9 +303,9 @@ split_quote_chain (void) ...@@ -304,9 +303,9 @@ split_quote_chain (void)
void void
add_path (char *path, int chain, int cxx_aware) add_path (char *path, int chain, int cxx_aware)
{ {
struct cpp_path *p; struct cpp_dir *p;
p = xmalloc (sizeof (struct cpp_path)); p = xmalloc (sizeof (struct cpp_dir));
p->next = NULL; p->next = NULL;
p->name = path; p->name = path;
if (chain == SYSTEM || chain == AFTER) if (chain == SYSTEM || chain == AFTER)
......
...@@ -1206,7 +1206,7 @@ c_common_parse_file (int set_yydebug ATTRIBUTE_UNUSED) ...@@ -1206,7 +1206,7 @@ c_common_parse_file (int set_yydebug ATTRIBUTE_UNUSED)
/* Reset cpplib's macros and start a new file. */ /* Reset cpplib's macros and start a new file. */
cpp_undef_all (parse_in); cpp_undef_all (parse_in);
cpp_read_next_file (parse_in, in_fnames[file_index]); cpp_stack_file (parse_in, in_fnames[file_index]);
} }
finish_options(in_fnames[file_index]); finish_options(in_fnames[file_index]);
......
/* Part of CPP library. (include file handling) /* Part of CPP library. File handling.
Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998, Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc. 1999, 2000, 2001, 2002, 2003 Free Software Foundation, Inc.
Written by Per Bothner, 1994. Written by Per Bothner, 1994.
Based on CCCP program by Paul Rubin, June 1986 Based on CCCP program by Paul Rubin, June 1986
Adapted to ANSI C, Richard Stallman, Jan 1987 Adapted to ANSI C, Richard Stallman, Jan 1987
Split out of cpplib.c, Zack Weinberg, Oct 1998 Split out of cpplib.c, Zack Weinberg, Oct 1998
Reimplemented, Neil Booth, Jul 2003
This program is free software; you can redistribute it and/or modify it This program is free software; you can redistribute it and/or modify it
under the terms of the GNU General Public License as published by the under the terms of the GNU General Public License as published by the
...@@ -22,423 +23,398 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ...@@ -22,423 +23,398 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#include "config.h" #include "config.h"
#include "system.h" #include "system.h"
#include <dirent.h>
#include "cpplib.h" #include "cpplib.h"
#include "cpphash.h" #include "cpphash.h"
#include "intl.h" #include "intl.h"
#include "mkdeps.h" #include "mkdeps.h"
#include "splay-tree.h" #include <dirent.h>
#ifndef O_BINARY /* Variable length record files on VMS will have a stat size that includes
# define O_BINARY 0 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_RELIABLE(ST) ((ST).st_fab_rfm != FAB_C_VAR)
#else
# define STAT_SIZE_RELIABLE(ST) true
#endif
#ifdef __DJGPP__
/* For DJGPP redirected input is opened in text mode. */
# define set_stdin_to_binary_mode() \
if (! isatty (0)) setmode (0, O_BINARY)
#else
# define set_stdin_to_binary_mode() /* Nothing */
#endif #endif
/* If errno is inspected immediately after a system call fails, it will be #ifndef O_BINARY
nonzero, and no error number will ever be zero. */ # define O_BINARY 0
#ifndef ENOENT
# define ENOENT 0
#endif #endif
#ifndef ENOTDIR #ifndef ENOTDIR
# define ENOTDIR 0 # define ENOTDIR 0
#endif #endif
/* This structure is used for the table of all includes. */ /* This structure represents a file searched for by CPP, whether it
struct include_file { exists or not. An instance may be pointed to by more than one
const char *name; /* actual path name of file */ file_hash_entry; at present no reference count is kept. */
const char *header_name; /* the original header found */ typedef struct _cpp_file _cpp_file;
const cpp_hashnode *cmacro; /* macro, if any, preventing reinclusion. */ struct _cpp_file
const struct cpp_path *foundhere; {
/* location in search path where file was /* Filename as given to #include or command line switch. */
found, for #include_next and sysp. */ const char *name;
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 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 /* The full path used to find the file. */
record control characters that won't be included in the read size. */ const char *path;
#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 /* The full path of the pch file. */
included again. If it's NEVER_REREAD, the file is never to be const char *pchname;
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 (cpp_reader *, const char *);
static char *read_filename_string (int, FILE *);
static char *remap_filename (cpp_reader *, char *, struct cpp_path *);
static struct cpp_path *search_from (cpp_reader *, enum include_type);
static struct include_file *find_include_file (cpp_reader *, const char *,
int, enum include_type);
static struct include_file *open_file (cpp_reader *, const char *);
static struct include_file *validate_pch (cpp_reader *, const char *,
const char *);
static struct include_file *open_file_pch (cpp_reader *, const char *);
static int read_include_file (cpp_reader *, struct include_file *);
static bool stack_include_file (cpp_reader *, struct include_file *);
static void purge_cache (struct include_file *);
static void destroy_node (splay_tree_value);
static int report_missing_guard (splay_tree_node, void *);
static splay_tree_node find_or_create_entry (cpp_reader *, const char *);
static void handle_missing_header (cpp_reader *, const char *, int);
static int remove_component_p (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 (cpp_reader *pfile)
{
pfile->all_include_files
= splay_tree_new ((splay_tree_compare_fn) strcmp,
(splay_tree_delete_key_fn) free,
destroy_node);
}
/* Tear down the splay tree. */ /* The file's path with the basename stripped, malloced. NULL if it
void hasn't been calculated yet. */
_cpp_cleanup_includes (cpp_reader *pfile) const char *dir_name;
{
splay_tree_delete (pfile->all_include_files);
}
/* Free a node. The path string is automatically freed. */ /* Chain through #import-ed files or those containing #pragma once. */
static void struct _cpp_file *once_only_next;
destroy_node (splay_tree_value v)
{
struct include_file *f = (struct include_file *) v;
if (f) /* The contents of NAME after calling read_file(). */
{ const uchar *buffer;
purge_cache (f);
free (f);
}
}
/* Mark a file to not be reread (e.g. #import, read failure). */ /* The macro, if any, preventing re-inclusion. */
void const cpp_hashnode *cmacro;
_cpp_never_reread (struct include_file *file)
{
file->cmacro = NEVER_REREAD;
}
/* Lookup a filename, which is simplified after making a copy, and /* The directory in the search path where FILE was found. Used for
create an entry if none exists. */ #include_next and determining whether a header is a system
static splay_tree_node header. Is NULL if the file was given as an absolute path, or
find_or_create_entry (cpp_reader *pfile, const char *fname) opened with read_file. */
{ cpp_dir *dir;
splay_tree_node node;
struct include_file *file;
char *name = xstrdup (fname);
int saved_errno;
cpp_simplify_path (name);
saved_errno = errno;
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 = saved_errno;
node = splay_tree_insert (pfile->all_include_files,
(splay_tree_key) file->name,
(splay_tree_value) file);
}
return node; /* As filled in by stat(2) for the file. */
} struct stat st;
/* Enter a file name in the splay tree, for the sake of cpp_included. */ /* File descriptor. Invalid if -1, otherwise open. */
void int fd;
_cpp_fake_include (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, /* Zero if this file was successfully opened and stat()-ed,
create one with a non-NULL value (regardless of success in opening otherwise errno obtained from failure. */
the file). If the file doesn't exist or is inaccessible, this int err_no;
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 (cpp_reader *pfile, const char *filename)
{
splay_tree_node nd = find_or_create_entry (pfile, filename);
struct include_file *file = (struct include_file *) nd->value;
if (file->err_no) /* Number of times the file has been stacked for preprocessing. */
{ unsigned short stack_count;
/* Ugh. handle_missing_header () needs errno to be set. */
errno = file->err_no;
return 0;
}
/* Don't reopen an idempotent file. */ /* If opened with #import. */
if (DO_NOT_REREAD (file)) bool import;
return file;
/* Don't reopen one which is already loaded. */ /* If contains #pragma once. */
if (0 && file->buffer != NULL) bool pragma_once;
return file;
/* If read() failed before. */
bool dont_read;
/* If this file is the main file. */
bool main_file;
/* If BUFFER above contains the true contents of the file. */
bool buffer_valid;
/* 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. */
uchar pch;
};
/* A singly-linked list for all searches for a given file name, with
its head pointed to by a slot in FILE_HASH. The file name is what
appeared between the quotes in a #include directive; it can be
determined implicity from the hash table location or explicitly
from FILE->fname.
FILE is a structure containing details about the file that was
found with that search, or details of how the search failed.
START_DIR is the starting location of the search in the include
chain. The current directories for "" includes are also hashed in
the hash table. Files that are looked up without using a search
path, such as absolute filenames and file names from the command
line share a special starting directory so they don't get confused
with normal include-chain lookups in the cache.
If START_DIR is NULL then the entry is for a directory, not a file,
and the directory is in DIR. Since the starting point in a file
lookup chain is never NULL, this means that simple pointer
comparisons against START_DIR can be made to determine cache hits
in file lookups.
*/
struct file_hash_entry
{
struct file_hash_entry *next;
cpp_dir *start_dir;
union
{
_cpp_file *file;
cpp_dir *dir;
} u;
};
/* We used to open files in nonblocking mode, but that caused more static bool open_file (_cpp_file *file);
problems than it solved. Do take care not to acquire a static bool pch_open_file (cpp_reader *pfile, _cpp_file *file);
controlling terminal by mistake (this can't happen on sane static bool open_file_in_dir (cpp_reader *pfile, _cpp_file *file);
systems, but paranoia is a virtue). static _cpp_file *find_file (cpp_reader *, const char *fname,
cpp_dir *start_dir, bool fake);
static bool read_file_guts (cpp_reader *pfile, _cpp_file *file);
static bool read_file (cpp_reader *pfile, _cpp_file *file);
static bool stack_file (cpp_reader *, _cpp_file *file, bool import);
static bool once_only_file_p (cpp_reader *, _cpp_file *file, bool import);
static struct cpp_dir *search_path_head (cpp_reader *, const char *fname,
int angle_brackets, enum include_type);
static const char *dir_name_of_file (_cpp_file *file);
static void open_file_failed (cpp_reader *pfile, _cpp_file *file);
static struct file_hash_entry *search_cache (struct file_hash_entry *head,
const cpp_dir *start_dir);
static _cpp_file *make_cpp_file (cpp_reader *, cpp_dir *, const char *fname);
static cpp_dir *make_cpp_dir (cpp_reader *, const char *dir_name, int sysp);
static void allocate_file_hash_entries (cpp_reader *pfile);
static struct file_hash_entry *new_file_hash_entry (cpp_reader *pfile);
static int report_missing_guard (void **slot, void *b);
static int hash_string_eq (const void *p, const void *q);
static char *read_filename_string (int ch, FILE *f);
static void read_name_map (cpp_dir *dir);
static char *remap_filename (cpp_reader *pfile, _cpp_file *file);
static char *append_file_to_dir (const char *fname, cpp_dir *dir);
static bool validate_pch (cpp_reader *, _cpp_file *file, const char *pchname);
static bool include_pch_p (_cpp_file *file);
/* Given a filename in FILE->PATH, with the empty string interpreted
as <stdin>, open it.
On success FILE contains an open file descriptor and stat
information for the file. On failure the file descriptor is -1 and
the appropriate errno is also stored in FILE. Returns TRUE iff
successful.
We used to open files in nonblocking mode, but that caused more
problems than it solved. Do take care not to acquire a controlling
terminal by mistake (this can't happen on sane systems, but
paranoia is a virtue).
Use the three-argument form of open even though we aren't Use the three-argument form of open even though we aren't
specifying O_CREAT, to defend against broken system headers. specifying O_CREAT, to defend against broken system headers.
O_BINARY tells some runtime libraries (notably DJGPP) not to do O_BINARY tells some runtime libraries (notably DJGPP) not to do
newline translation; we can handle DOS line breaks just fine newline translation; we can handle DOS line breaks just fine
ourselves. ourselves. */
static bool
Special case: the empty string is translated to stdin. */ open_file (_cpp_file *file)
{
if (filename[0] == '\0') if (file->path[0] == '\0')
{ {
file->fd = 0; file->fd = 0;
#ifdef __DJGPP__ set_stdin_to_binary_mode ();
/* 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 else
file->fd = open (file->name, O_RDONLY | O_NOCTTY | O_BINARY, 0666); file->fd = open (file->path, O_RDONLY | O_NOCTTY | O_BINARY, 0666);
if (file->fd != -1 && fstat (file->fd, &file->st) == 0) if (file->fd != -1)
{
if (fstat (file->fd, &file->st) == 0)
{ {
if (!S_ISDIR (file->st.st_mode)) if (!S_ISDIR (file->st.st_mode))
return file; {
file->err_no = 0;
return true;
}
/* If it's a directory, we return null and continue the search /* Ignore a directory and continue the search. The file we're
as the file we're looking for may appear elsewhere in the looking for may be elsewhere in the search path. */
search path. */
errno = ENOENT; errno = ENOENT;
}
close (file->fd); close (file->fd);
file->fd = -1; file->fd = -1;
} }
file->err_no = errno; file->err_no = errno;
return 0;
}
static struct include_file * return false;
validate_pch (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 (CPP_OPTION (pfile, print_include_names))
{
unsigned int i;
for (i = 1; i < pfile->line_maps.depth; i++)
putc ('.', stderr);
fprintf (stderr, "%c %s\n", INCLUDE_PCH_P (file) ? '!' : 'x', pchname);
}
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;
} }
/* Temporary PCH intercept of opening a file. */
/* Like open_file, but also look for a precompiled header if (a) one exists static bool
and (b) it is valid. */ pch_open_file (cpp_reader *pfile, _cpp_file *file)
static struct include_file *
open_file_pch (cpp_reader *pfile, const char *filename)
{ {
if (filename[0] != '\0' static const char extension[] = ".gch";
&& pfile->cb.valid_pch != NULL) const char *path = file->path;
{ size_t len, flen;
size_t namelen = strlen (filename); char *pchname;
char *pchname = alloca (namelen + 5);
struct include_file *file = NULL;
struct stat st; struct stat st;
bool valid = false;
/* No PCH on <stdin> or if not requested. */
if (file->name[0] == '\0' || !pfile->cb.valid_pch)
return false;
memcpy (pchname, filename, namelen); flen = strlen (path);
memcpy (pchname + namelen, ".gch", 5); len = flen + sizeof (extension);
cpp_simplify_path (pchname); pchname = xmalloc (len);
memcpy (pchname, path, flen);
memcpy (pchname + flen, extension, sizeof (extension));
if (stat (pchname, &st) == 0 && S_ISDIR (st.st_mode)) if (stat (pchname, &st) == 0)
{ {
DIR * thedir; DIR *pchdir;
struct dirent *d; struct dirent *d;
size_t subname_len = namelen + 64; size_t dlen, plen = len;
char *subname = xmalloc (subname_len);
thedir = opendir (pchname); if (!S_ISDIR (st.st_mode))
if (thedir == NULL) valid = validate_pch (pfile, file, pchname);
return NULL; else if ((pchdir = opendir (pchname)) != NULL)
memcpy (subname, pchname, namelen + 4);
subname[namelen+4] = '/';
while ((d = readdir (thedir)) != NULL)
{ {
if (strlen (d->d_name) + namelen + 7 > subname_len) pchname[plen - 1] = '/';
while ((d = readdir (pchdir)) != NULL)
{ {
subname_len = strlen (d->d_name) + namelen + 64; dlen = strlen (d->d_name) + 1;
subname = xrealloc (subname, subname_len); if (dlen + plen > len)
{
len += dlen + 64;
pchname = xrealloc (pchname, len);
} }
strcpy (subname + namelen + 5, d->d_name); memcpy (pchname + plen, d->d_name, dlen);
file = validate_pch (pfile, filename, subname); valid = validate_pch (pfile, file, pchname);
if (file) if (valid)
break; break;
} }
closedir (thedir); closedir (pchdir);
free (subname);
} }
else
file = validate_pch (pfile, filename, pchname);
if (file)
return file;
} }
return open_file (pfile, filename); if (valid)
file->pchname = pchname;
else
free (pchname);
return valid;
} }
/* Place the file referenced by INC into a new buffer on the buffer /* Try to open the path FILE->name appended to FILE->dir. This is
stack, unless there are errors, or the file is not re-included where remap and PCH intercept the file lookup process. */
because of e.g. multiple-include guards. Returns true if a buffer
is stacked. */
static bool static bool
stack_include_file (cpp_reader *pfile, struct include_file *inc) open_file_in_dir (cpp_reader *pfile, _cpp_file *file)
{ {
cpp_buffer *fp; char *path;
int sysp;
const char *filename; if (CPP_OPTION (pfile, remap) && (path = remap_filename (pfile, file)))
;
else
path = append_file_to_dir (file->name, file->dir);
file->path = path;
if (pch_open_file (pfile, file))
return true;
if (DO_NOT_REREAD (inc)) if (open_file (file))
return true;
free (path);
file->path = NULL;
return false; return false;
}
sysp = MAX ((pfile->map ? pfile->map->sysp : 0), /* Given a filename FNAME search for such a file in the include path
(inc->foundhere ? inc->foundhere->sysp : 0)); starting from START_DIR. If FNAME is the empty string it is
interpreted as STDIN if START_DIR is PFILE->no_seach_path.
If the file is not found in the file cache fall back to the O/S and
add the result to our cache.
If the file was not found in the filesystem, or there was an error
opening it, then ERR_NO is non-zero and FD is -1. If the file was
found, then ERR_NO is zero and FD could be -1 or an open file
descriptor. FD can be -1 if the file was found in the cache and
had previously been closed. To open it again pass the return value
to open_file().
*/
static _cpp_file *
find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, bool fake)
{
struct file_hash_entry *entry, **hash_slot;
_cpp_file *file;
/* Add the file to the dependencies on its first inclusion. */ /* Ensure we get no confusion between cached files and directories. */
if (CPP_OPTION (pfile, deps.style) > !!sysp && !inc->include_count) if (start_dir == NULL)
cpp_error (pfile, DL_ICE, "NULL directory in find_file");
hash_slot = (struct file_hash_entry **)
htab_find_slot (pfile->file_hash, fname, INSERT);
/* First check the cache before we resort to memory allocation. */
entry = search_cache (*hash_slot, start_dir);
if (entry)
return entry->u.file;
file = make_cpp_file (pfile, start_dir, fname);
/* Try each path in the include chain. */
for (; !fake ;)
{ {
if (pfile->buffer || CPP_OPTION (pfile, deps.ignore_main_file) == 0) if (open_file_in_dir (pfile, file))
deps_add_dep (pfile->deps, inc->name); break;
}
/* PCH files get dealt with immediately. */ if (file->err_no != ENOENT || (file->dir = file->dir->next) == NULL)
if (INCLUDE_PCH_P (inc))
{ {
pfile->cb.read_pch (pfile, inc->name, inc->fd, inc->header_name); open_file_failed (pfile, file);
close (inc->fd); break;
inc->fd = -1;
return false;
} }
/* Not in cache? */ /* Only check the cache for the starting location (done above)
if (1 || ! inc->buffer) and the quote and bracket chain heads because there are no
{ other possible starting points for searches. */
if (read_include_file (pfile, inc)) if (file->dir != pfile->bracket_include
&& file->dir != pfile->quote_include)
continue;
entry = search_cache (*hash_slot, file->dir);
if (entry)
{ {
/* If an error occurs, do not try to read this file again. */ /* Cache for START_DIR too, sharing the _cpp_file structure. */
_cpp_never_reread (inc); free ((char *) file->name);
return false; free (file);
file = entry->u.file;
break;
} }
/* 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) /* Store this new result in the hash table. */
/* We don't want MI guard advice for the main file. */ entry = new_file_hash_entry (pfile);
inc->include_count++; entry->next = *hash_slot;
entry->start_dir = start_dir;
/* Push a buffer. */ entry->u.file = file;
fp = cpp_push_buffer (pfile, inc->buffer, inc->st.st_size, *hash_slot = entry;
/* from_stage3 */ CPP_OPTION (pfile, preprocessed), 0);
fp->inc = inc;
/* 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; return file;
} }
/* Read the file referenced by INC into the file cache. /* Read a file into FILE->buffer, returning true on success.
If fd points to a plain file, we might be able to mmap it; we can If FILE->fd is something weird, like a block device, we don't want
definitely allocate the buffer all at once. If fd is a pipe or to read it at all. Don't even try to figure out what something is,
terminal, we can't do either. If fd is something weird, like a except for plain files and block devices, since there is no
block device, we don't want to read it at all. reliable portable way of doing this.
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. */ FIXME: Flush file cache and try again if we run out of memory. */
static int static bool
read_include_file (cpp_reader *pfile, struct include_file *inc) read_file_guts (cpp_reader *pfile, _cpp_file *file)
{ {
ssize_t size, offset, count; ssize_t size, total, count;
uchar *buf; uchar *buf;
bool regular;
if (S_ISBLK (file->st.st_mode))
{
cpp_error (pfile, DL_ERROR, "%s is a block device", file->name);
return false;
}
if (S_ISREG (inc->st.st_mode)) regular = S_ISREG (file->st.st_mode);
if (regular)
{ {
/* off_t might have a wider range than ssize_t - in other words, /* 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 the max size of a file might be bigger than the address
...@@ -448,191 +424,463 @@ read_include_file (cpp_reader *pfile, struct include_file *inc) ...@@ -448,191 +424,463 @@ read_include_file (cpp_reader *pfile, struct include_file *inc)
SSIZE_MAX to be much smaller than the actual range of the SSIZE_MAX to be much smaller than the actual range of the
type. Use INTTYPE_MAXIMUM unconditionally to ensure this type. Use INTTYPE_MAXIMUM unconditionally to ensure this
does not bite us. */ does not bite us. */
if (inc->st.st_size > INTTYPE_MAXIMUM (ssize_t)) if (file->st.st_size > INTTYPE_MAXIMUM (ssize_t))
{ {
cpp_error (pfile, DL_ERROR, "%s is too large", inc->name); cpp_error (pfile, DL_ERROR, "%s is too large", file->name);
goto fail; return false;
} }
size = inc->st.st_size;
{ size = file->st.st_size;
buf = 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 \n-terminated. */
buf[size] = '\n';
}
}
else if (S_ISBLK (inc->st.st_mode))
{
cpp_error (pfile, DL_ERROR, "%s is a block device", inc->name);
goto fail;
} }
else else
{ /* 8 kilobytes is a sensible starting size. It ought to be bigger
/* 8 kilobytes is a sensible starting size. It ought to be than the kernel pipe buffer, and it's definitely bigger than
bigger than the kernel pipe buffer, and it's definitely the majority of C source files. */
bigger than the majority of C source files. */
size = 8 * 1024; size = 8 * 1024;
buf = xmalloc (size + 1); buf = xmalloc (size + 1);
offset = 0; total = 0;
while ((count = read (inc->fd, buf + offset, size - offset)) > 0) while ((count = read (file->fd, buf + total, size - total)) > 0)
{ {
offset += count; total += count;
if (offset == size)
if (total == size)
{ {
if (regular)
break;
size *= 2; size *= 2;
buf = xrealloc (buf, size + 1); buf = xrealloc (buf, size + 1);
} }
} }
if (count < 0) if (count < 0)
goto perror_fail; {
cpp_errno (pfile, DL_ERROR, file->name);
return false;
}
if (offset + 1 < size) if (regular && total != size && STAT_SIZE_RELIABLE (file->st))
buf = xrealloc (buf, offset + 1); cpp_error (pfile, DL_WARNING, "%s is shorter than expected", file->name);
/* Shrink buffer if we allocated substantially too much. */
if (total + 4096 < size)
buf = xrealloc (buf, total + 1);
/* The lexer requires that the buffer be \n-terminated. */ /* The lexer requires that the buffer be \n-terminated. */
buf[offset] = '\n'; buf[total] = '\n';
inc->st.st_size = offset;
file->buffer = buf;
file->st.st_size = total;
file->buffer_valid = true;
return true;
}
/* Convenience wrapper around read_file_guts that opens the file if
necessary and closes the file desciptor after reading. FILE must
have been passed through find_file() at some stage. */
static bool
read_file (cpp_reader *pfile, _cpp_file *file)
{
/* Skip if the file had a header guard and the macro is defined. */
if (file->cmacro && file->cmacro->type == NT_MACRO)
return false;
/* PCH files get dealt with immediately. */
if (include_pch_p (file))
{
pfile->cb.read_pch (pfile, file->path, file->fd, file->pchname);
close (file->fd);
file->fd = -1;
return false;
} }
inc->buffer = buf; /* If we already have its contents in memory, succeed immediately. */
return 0; if (file->buffer_valid)
return true;
perror_fail: /* If an earlier read failed for some reason don't try again. */
cpp_errno (pfile, DL_ERROR, inc->name); if (file->dont_read || file->err_no)
fail: return false;
return 1;
if (file->fd == -1 && !open_file (file))
{
open_file_failed (pfile, file);
return false;
}
file->dont_read = !read_file_guts (pfile, file);
close (file->fd);
file->fd = -1;
return !file->dont_read;
} }
/* Drop INC's buffer from memory. */ /* Place the file referenced by FILE into a new buffer on the buffer
static void stack if possible. IMPORT is true if this stacking attempt is
purge_cache (struct include_file *inc) because of a #import directive. Returns true if a buffer is
stacked. */
static bool
stack_file (cpp_reader *pfile, _cpp_file *file, bool import)
{ {
if (inc->buffer) cpp_buffer *buffer;
int sysp;
const char *fname;
if (once_only_file_p (pfile, file, import))
return false;
sysp = MAX ((pfile->map ? pfile->map->sysp : 0),
(file->dir ? file->dir->sysp : 0));
/* Add the file to the dependencies on its first inclusion. */
if (CPP_OPTION (pfile, deps.style) > !!sysp && !file->stack_count)
{ {
free ((void *) inc->buffer); if (!file->main_file || !CPP_OPTION (pfile, deps.ignore_main_file))
inc->buffer = NULL; deps_add_dep (pfile->deps, file->name);
} }
if (!read_file (pfile, file))
return false;
/* Clear buffer_valid since _cpp_clean_line messes it up. */
file->buffer_valid = false;
file->stack_count++;
/* Stack the buffer. */
buffer = cpp_push_buffer (pfile, file->buffer, file->st.st_size,
CPP_OPTION (pfile, preprocessed), 0);
buffer->file = file;
/* Initialize controlling macro state. */
pfile->mi_valid = true;
pfile->mi_cmacro = 0;
/* Generate the call back. */
fname = file->name;
if (*fname == '\0')
fname = "<stdin>";
_cpp_do_file_change (pfile, LC_ENTER, fname, 1, sysp);
return true;
} }
/* Return 1 if the file named by FNAME has been included before in /* Returns TRUE if FILE has been previously read and should not be
any context, 0 otherwise. */ read again. */
int static bool
cpp_included (cpp_reader *pfile, const char *fname) once_only_file_p (cpp_reader *pfile, _cpp_file *file, bool import)
{ {
struct cpp_path *path; _cpp_file *f;
char *name, *n;
splay_tree_node nd;
if (IS_ABSOLUTE_PATH (fname)) /* Nothing to check if this isn't #import and there haven't been any
#pragma_once directives. */
if (!import && !pfile->saw_pragma_once)
return false;
/* Did this file contain #pragma once? */
if (file->pragma_once)
return true;
/* Are we #import-ing a previously #import-ed file? */
if (import)
{ {
/* Just look it up. */ if (file->import)
nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) fname); return true;
return (nd && nd->value); _cpp_mark_file_once_only (pfile, file, true);
} }
/* Search directory path for the file. */ /* Read the file contents now. stack_file would do it later, and
name = alloca (strlen (fname) + pfile->max_include_len + 2); we're smart enough to not do it twice, so this is no loss. */
for (path = pfile->quote_include; path; path = path->next) if (!read_file (pfile, file))
return false;
/* We may have #imported it under a different name, though. Look
for likely candidates and compare file contents to be sure. */
for (f = pfile->once_only_files; f; f = f->once_only_next)
{ {
memcpy (name, path->name, path->len); if (f == file)
name[path->len] = '/'; continue;
strcpy (&name[path->len + 1], fname);
if (CPP_OPTION (pfile, remap))
n = remap_filename (pfile, name, path);
else
n = name;
nd = splay_tree_lookup (pfile->all_include_files, (splay_tree_key) n); if (!f->pragma_once && !(f->import && import))
if (nd && nd->value) continue;
return 1;
if (f->err_no == 0
&& f->st.st_mtime == file->st.st_mtime
&& f->st.st_size == file->st.st_size
&& read_file (pfile, f)
/* Size might have changed in read_file(). */
&& f->st.st_size == file->st.st_size
&& !memcmp (f->buffer, file->buffer, f->st.st_size))
return true;
} }
return 0;
return false;
} }
/* Search for FNAME. Return 0 if there is no such file (or it's /* Mark FILE to be included once only. IMPORT is true if because of
un-openable), in which case an error code will be in errno. If #import, otherwise it is assumed to be #pragma once. */
there is no include path to use it returns NO_INCLUDE_PATH, void
otherwise an include_file structure. If this request originates _cpp_mark_file_once_only (cpp_reader *pfile, _cpp_file *file, bool import)
from a directive of TYPE #include_next, set INCLUDE_NEXT to true. */
static struct include_file *
find_include_file (cpp_reader *pfile, const char *fname, int angle_brackets,
enum include_type type)
{ {
struct cpp_path *path; if (import)
struct include_file *file; file->import = true;
char *name, *n; else
{
pfile->saw_pragma_once = true;
file->pragma_once = true;
}
if (*fname == '\0') /* Put it on the once-only list if it's not on there already (an
earlier #include with a #pragma once might have put it on there
already). */
if (file->once_only_next == NULL)
{ {
cpp_error (pfile, DL_ERROR, "empty file name"); file->once_only_next = pfile->once_only_files;
return NO_INCLUDE_PATH; pfile->once_only_files = file;
} }
}
/* Return the directory from which searching for FNAME should start,
condiering the directive TYPE and ANGLE_BRACKETS. If there is
nothing left in the path, returns NULL. */
static struct cpp_dir *
search_path_head (cpp_reader *pfile, const char *fname, int angle_brackets,
enum include_type type)
{
cpp_dir *dir;
_cpp_file *file;
if (IS_ABSOLUTE_PATH (fname)) if (IS_ABSOLUTE_PATH (fname))
return open_file_pch (pfile, fname); return &pfile->no_search_path;
file = pfile->buffer->file;
/* For #include_next, skip in the search path past the dir in which /* 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 the current file was found, but if it was found via an absolute
path use the normal search logic. */ path use the normal search logic. */
if (type == IT_INCLUDE_NEXT && pfile->buffer->inc->foundhere) if (type == IT_INCLUDE_NEXT && file->dir)
path = pfile->buffer->inc->foundhere->next; dir = file->dir->next;
else if (angle_brackets) else if (angle_brackets)
path = pfile->bracket_include; dir = pfile->bracket_include;
else if (type == IT_CMDLINE)
/* -include and -imacros use the #include "" chain with the
preprocessor's cwd prepended. */
return make_cpp_dir (pfile, "./", false);
else if (pfile->quote_ignores_source_dir)
dir = pfile->quote_include;
else else
path = search_from (pfile, type); return make_cpp_dir (pfile, dir_name_of_file (file), pfile->map->sysp);
if (dir == NULL)
cpp_error (pfile, DL_ERROR,
"no include path in which to search for %s", fname);
if (path == NULL) return dir;
}
/* Strip the basename from the file's path. It ends with a slash if
of non-zero length. Note that this procedure also works for
<stdin>, which is represented by the empty string. */
static const char *
dir_name_of_file (_cpp_file *file)
{
if (!file->dir_name)
{ {
cpp_error (pfile, DL_ERROR, "no include path in which to find %s", size_t len = lbasename (file->path) - file->path;
fname); char *dir_name = xmalloc (len + 1);
return NO_INCLUDE_PATH;
memcpy (dir_name, file->path, len);
dir_name[len] = '\0';
file->dir_name = dir_name;
} }
/* Search directory path for the file. */ return file->dir_name;
name = alloca (strlen (fname) + pfile->max_include_len + 2); }
for (; path; path = path->next)
/* Push an input buffer with the contents of FNAME, the empty string
for standard input. Return true if a buffer was stacked. */
bool
cpp_stack_file (cpp_reader *pfile, const char *fname)
{
struct cpp_dir *dir = &pfile->no_search_path;
return stack_file (pfile, find_file (pfile, fname, dir, false), false);
}
/* 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_stack_include (cpp_reader *pfile, const char *fname, int angle_brackets,
enum include_type type)
{
struct cpp_dir *dir;
dir = search_path_head (pfile, fname, angle_brackets, type);
if (!dir)
return false;
return stack_file (pfile, find_file (pfile, fname, dir, false),
type == IT_IMPORT);
}
/* Could not open FILE. The complication is dependency output. */
static void
open_file_failed (cpp_reader *pfile, _cpp_file *file)
{
int sysp = pfile->map ? pfile->map->sysp: 0;
bool print_dep = CPP_OPTION (pfile, deps.style) > !!sysp;
errno = file->err_no;
if (print_dep && CPP_OPTION (pfile, deps.missing_files) && errno == ENOENT)
deps_add_dep (pfile->deps, file->name);
else
{ {
int len = path->len; /* If we are outputting dependencies but not for this file then
memcpy (name, path->name, len); don't error because we can still produce correct output. */
/* Don't turn / into // or // into ///; // may be a namespace if (CPP_OPTION (pfile, deps.style) && ! print_dep)
escape. */ cpp_errno (pfile, DL_WARNING, file->name);
if (name[len-1] == '/')
len--;
name[len] = '/';
strcpy (&name[len + 1], fname);
if (CPP_OPTION (pfile, remap))
n = remap_filename (pfile, name, path);
else else
n = name; cpp_errno (pfile, DL_ERROR, file->name);
}
}
/* Search in the chain beginning at HEAD for a file whose search path
started at START_DIR != NULL. */
static struct file_hash_entry *
search_cache (struct file_hash_entry *head, const cpp_dir *start_dir)
{
while (head && head->start_dir != start_dir)
head = head->next;
return head;
}
/* Allocate a new _cpp_file structure. */
static _cpp_file *
make_cpp_file (cpp_reader *pfile, cpp_dir *dir, const char *fname)
{
_cpp_file *file;
file = xcalloc (1, sizeof (_cpp_file));
file->main_file = !pfile->buffer;
file->fd = -1;
file->dir = dir;
file->name = xstrdup (fname);
file = open_file_pch (pfile, n);
if (file)
{
file->foundhere = path;
return file; return file;
} }
}
return 0; /* A hash of directory names. The directory names are the path names
of files which contain a #include "", the included file name is
appended to this directories.
To avoid duplicate entries we follow the convention that all
non-empty directory names should end in a '/'. DIR_NAME must be
stored in permanently allocated memory. */
static cpp_dir *
make_cpp_dir (cpp_reader *pfile, const char *dir_name, int sysp)
{
struct file_hash_entry *entry, **hash_slot;
cpp_dir *dir;
hash_slot = (struct file_hash_entry **)
htab_find_slot (pfile->file_hash, dir_name, INSERT);
/* Have we already hashed this directory? */
for (entry = *hash_slot; entry; entry = entry->next)
if (entry->start_dir == NULL)
return entry->u.dir;
dir = xcalloc (1, sizeof (cpp_dir));
dir->next = pfile->quote_include;
dir->name = (char *) dir_name;
dir->len = strlen (dir_name);
dir->sysp = sysp;
/* Store this new result in the hash table. */
entry = new_file_hash_entry (pfile);
entry->next = *hash_slot;
entry->start_dir = NULL;
entry->u.dir = dir;
*hash_slot = entry;
return dir;
}
/* Create a new block of memory for file hash entries. */
static void
allocate_file_hash_entries (cpp_reader *pfile)
{
pfile->file_hash_entries_used = 0;
pfile->file_hash_entries_allocated = 127;
pfile->file_hash_entries = xmalloc
(pfile->file_hash_entries_allocated * sizeof (struct file_hash_entry));
}
/* Return a new file hash entry. */
static struct file_hash_entry *
new_file_hash_entry (cpp_reader *pfile)
{
if (pfile->file_hash_entries_used == pfile->file_hash_entries_allocated)
allocate_file_hash_entries (pfile);
return &pfile->file_hash_entries[pfile->file_hash_entries_used++];
}
/* Returns TRUE if a file FNAME has ever been successfully opened.
This routine is not intended to correctly handle filenames aliased
by links or redundant . or .. traversals etc. */
bool
cpp_included (cpp_reader *pfile, const char *fname)
{
struct file_hash_entry *entry;
entry = htab_find (pfile->file_hash, fname);
while (entry && (entry->start_dir == NULL || entry->u.file->err_no))
entry = entry->next;
return entry != NULL;
}
/* Compare a string Q against a file hash entry P. */
static int
hash_string_eq (const void *p, const void *q)
{
struct file_hash_entry *entry = (struct file_hash_entry *) p;
const char *fname = (const char *) q;
const char *hname;
if (entry->start_dir)
hname = entry->u.file->name;
else
hname = entry->u.dir->name;
return strcmp (hname, fname) == 0;
}
/* Initialize everything in this source file. */
void
_cpp_init_files (cpp_reader *pfile)
{
pfile->file_hash = htab_create_alloc (127, htab_hash_string, hash_string_eq,
NULL, xcalloc, free);
allocate_file_hash_entries (pfile);
}
/* Finalize everything in this source file. */
void
_cpp_cleanup_files (cpp_reader *pfile)
{
htab_delete (pfile->file_hash);
}
/* Enter a file name in the hash for the sake of cpp_included. */
void
_cpp_fake_include (cpp_reader *pfile, const char *fname)
{
find_file (pfile, fname, pfile->buffer->file->dir, true);
} }
/* Not everyone who wants to set system-header-ness on a buffer can /* Not everyone who wants to set system-header-ness on a buffer can
...@@ -660,79 +908,44 @@ cpp_change_file (cpp_reader *pfile, enum lc_reason reason, ...@@ -660,79 +908,44 @@ cpp_change_file (cpp_reader *pfile, enum lc_reason reason,
_cpp_do_file_change (pfile, reason, new_name, 1, 0); _cpp_do_file_change (pfile, reason, new_name, 1, 0);
} }
/* Report on all files that might benefit from a multiple include guard. /* Callback function for htab_traverse. */
Triggered by -H. */
void
_cpp_report_missing_guards (cpp_reader *pfile)
{
int banner = 0;
splay_tree_foreach (pfile->all_include_files, report_missing_guard, &banner);
}
/* Callback function for splay_tree_foreach(). */
static int static int
report_missing_guard (splay_tree_node n, void *b) report_missing_guard (void **slot, void *b)
{ {
struct include_file *f = (struct include_file *) n->value; struct file_hash_entry *entry = (struct file_hash_entry *) *slot;
int *bannerp = (int *) b; int *bannerp = (int *) b;
if (f && f->cmacro == 0 && f->include_count == 1) /* Skip directories. */
if (entry->start_dir != NULL)
{
_cpp_file *file = entry->u.file;
/* We don't want MI guard advice for the main file. */
if (file->cmacro == NULL && file->stack_count == 1 && !file->main_file)
{ {
if (*bannerp == 0) if (*bannerp == 0)
{ {
fputs (_("Multiple include guards may be useful for:\n"), stderr); fputs (_("Multiple include guards may be useful for:\n"),
stderr);
*bannerp = 1; *bannerp = 1;
} }
fputs (f->name, stderr);
fputs (entry->u.file->path, stderr);
putc ('\n', stderr); putc ('\n', stderr);
} }
return 0; }
}
/* Create a dependency for file FNAME, or issue an error message as return 0;
appropriate. ANGLE_BRACKETS is nonzero if the file was bracketed
like <..>. */
static void
handle_missing_header (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), /* Report on all files that might benefit from a multiple include guard.
including HEADER, and the command line -imacros and -include. Triggered by -H. */
Returns true if a buffer was stacked. */ void
bool _cpp_report_missing_guards (cpp_reader *pfile)
_cpp_execute_include (cpp_reader *pfile, const char *fname, int angle_brackets,
enum include_type type)
{ {
bool stacked = false; int banner = 0;
struct include_file *inc;
inc = find_include_file (pfile, fname, angle_brackets, type);
if (inc == 0)
handle_missing_header (pfile, fname, angle_brackets);
else if (inc != NO_INCLUDE_PATH)
{
stacked = stack_include_file (pfile, inc);
if (type == IT_IMPORT)
_cpp_never_reread (inc);
}
return stacked; htab_traverse (pfile->file_hash, report_missing_guard, &banner);
} }
/* Locate HEADER, and determine whether it is newer than the current /* Locate HEADER, and determine whether it is newer than the current
...@@ -742,134 +955,96 @@ int ...@@ -742,134 +955,96 @@ int
_cpp_compare_file_date (cpp_reader *pfile, const char *fname, _cpp_compare_file_date (cpp_reader *pfile, const char *fname,
int angle_brackets) int angle_brackets)
{ {
struct include_file *inc; _cpp_file *file;
struct cpp_dir *dir;
inc = find_include_file (pfile, fname, angle_brackets, IT_INCLUDE); dir = search_path_head (pfile, fname, angle_brackets, IT_INCLUDE);
if (inc == NULL || inc == NO_INCLUDE_PATH) if (!dir)
return -1; return -1;
if (inc->fd > 0) file = find_file (pfile, fname, dir, false);
{ if (file->err_no)
close (inc->fd); return -1;
inc->fd = -1;
}
return inc->st.st_mtime > pfile->buffer->inc->st.st_mtime;
}
/* 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 (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 (f == NULL) if (file->fd != -1)
{ {
cpp_errno (pfile, DL_ERROR, fname); close (file->fd);
return false; file->fd = -1;
} }
return stack_include_file (pfile, f); return file->st.st_mtime > pfile->buffer->file->st.st_mtime;
} }
/* Pushes the given file onto the buffer stack. Returns nonzero if /* Pushes the given file onto the buffer stack. Returns nonzero if
successful. */ successful. */
bool bool
cpp_push_include (cpp_reader *pfile, const char *filename) cpp_push_include (cpp_reader *pfile, const char *fname)
{ {
/* Make the command line directive take up a line. */ /* Make the command line directive take up a line. */
pfile->line++; pfile->line++;
return _cpp_execute_include (pfile, filename, false, IT_CMDLINE); return _cpp_stack_include (pfile, fname, false, IT_CMDLINE);
} }
/* Do appropriate cleanup when a file INC's buffer is popped off the /* Do appropriate cleanup when a file INC's buffer is popped off the
input stack. */ input stack. */
void void
_cpp_pop_file_buffer (cpp_reader *pfile, struct include_file *inc) _cpp_pop_file_buffer (cpp_reader *pfile, _cpp_file *file)
{ {
/* Record the inclusion-preventing macro, which could be NULL /* Record the inclusion-preventing macro, which could be NULL
meaning no controlling macro. */ meaning no controlling macro. */
if (pfile->mi_valid && inc->cmacro == NULL) if (pfile->mi_valid && file->cmacro == NULL)
inc->cmacro = pfile->mi_cmacro; file->cmacro = pfile->mi_cmacro;
/* Invalidate control macros in the #including file. */ /* Invalidate control macros in the #including file. */
pfile->mi_valid = false; pfile->mi_valid = false;
purge_cache (inc); if (file->buffer)
{
free ((void *) file->buffer);
file->buffer = NULL;
}
} }
/* Returns the first place in the include chain to start searching for /* Set the include chain for "" to QUOTE, for <> to BRACKET. If
"" includes. This involves stripping away the basename of the QUOTE_IGNORES_SOURCE_DIR, then "" includes do not look in the
current file, unless -I- was specified. directory of the including file.
If we're handling -include or -imacros, use the "" chain, but with If BRACKET does not lie in the QUOTE chain, it is set to QUOTE. */
the preprocessor's cwd prepended. */ void
static struct cpp_path * cpp_set_include_chains (cpp_reader *pfile, cpp_dir *quote, cpp_dir *bracket,
search_from (cpp_reader *pfile, enum include_type type) int quote_ignores_source_dir)
{ {
cpp_buffer *buffer = pfile->buffer; pfile->quote_include = quote;
unsigned int dlen; pfile->bracket_include = quote;
pfile->quote_ignores_source_dir = quote_ignores_source_dir;
/* 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) for (; quote; quote = quote->next)
{
/* 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: quote->name_map = NULL;
buffer->dir.name = (char *) "."; quote->len = strlen (quote->name);
dlen = 1; if (quote == bracket)
} pfile->bracket_include = bracket;
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 /* Append the file name to the directory to create the path, but don't
particular directory. This mapping is read from the file named turn / into // or // into ///; // may be a namespace escape. */
FILE_NAME_MAP_FILE in that directory. Such a file can be used to static char *
map filenames on a file system with severe filename restrictions, append_file_to_dir (const char *fname, cpp_dir *dir)
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 size_t dlen, flen;
to map, and the second token is the actual name to use. */ char *path;
struct file_name_map {
struct file_name_map *map_next; dlen = dir->len;
char *map_from; flen = strlen (fname);
char *map_to; path = xmalloc (dlen + 1 + flen + 1);
}; memcpy (path, dir->name, dlen);
if (dlen && path[dlen - 1] != '/')
#define FILE_NAME_MAP_FILE "header.gcc" path[dlen++] = '/';
memcpy (&path[dlen], fname, flen + 1);
return path;
}
/* Read a space delimited string of unlimited length from a stdio /* Read a space delimited string of unlimited length from a stdio
file F. */ file F. */
...@@ -900,40 +1075,25 @@ read_filename_string (int ch, FILE *f) ...@@ -900,40 +1075,25 @@ read_filename_string (int ch, FILE *f)
return alloc; return alloc;
} }
/* This structure holds a linked list of file name maps, one per directory. */ /* Read the file name map file for DIR. */
struct file_name_map_list { static void
struct file_name_map_list *map_list_next; read_name_map (cpp_dir *dir)
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 (cpp_reader *pfile, const char *dirname)
{ {
struct file_name_map_list *map_list_ptr; static const char FILE_NAME_MAP_FILE[] = "header.gcc";
char *name; char *name;
FILE *f; FILE *f;
size_t len, count = 0, room = 9;
/* Check the cache of directories, and mappings in their remap file. */
for (map_list_ptr = CPP_OPTION (pfile, map_list); map_list_ptr; len = dir->len;
map_list_ptr = map_list_ptr->map_list_next) name = alloca (len + sizeof (FILE_NAME_MAP_FILE) + 1);
if (! strcmp (map_list_ptr->map_list_name, dirname)) memcpy (name, dir->name, len);
return map_list_ptr->map_list_map; if (len && name[len - 1] != '/')
name[len++] = '/';
map_list_ptr = xmalloc (sizeof (struct file_name_map_list)); strcpy (name + len, FILE_NAME_MAP_FILE);
map_list_ptr->map_list_name = xstrdup (dirname);
/* The end of the list ends in NULL. */
map_list_ptr->map_list_map = NULL;
name = 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"); f = fopen (name, "r");
dir->name_map = xmalloc (room * sizeof (char *));
/* Silently return NULL if we cannot open. */ /* Silently return NULL if we cannot open. */
if (f) if (f)
{ {
...@@ -941,269 +1101,118 @@ read_name_map (cpp_reader *pfile, const char *dirname) ...@@ -941,269 +1101,118 @@ read_name_map (cpp_reader *pfile, const char *dirname)
while ((ch = getc (f)) != EOF) while ((ch = getc (f)) != EOF)
{ {
char *from, *to; char *to;
struct file_name_map *ptr;
if (is_space (ch)) if (is_space (ch))
continue; continue;
from = read_filename_string (ch, f);
if (count + 2 > room)
{
room += 8;
dir->name_map = xrealloc (dir->name_map, room * sizeof (char *));
}
dir->name_map[count] = 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);
ptr = xmalloc (sizeof (struct file_name_map)); to = read_filename_string (ch, f);
ptr->map_from = from;
/* Make the real filename absolute. */
if (IS_ABSOLUTE_PATH (to)) if (IS_ABSOLUTE_PATH (to))
ptr->map_to = to; dir->name_map[count + 1] = to;
else else
{ {
ptr->map_to = concat (dirname, "/", to, NULL); dir->name_map[count + 1] = append_file_to_dir (to, dir);
free (to); free (to);
} }
ptr->map_next = map_list_ptr->map_list_map; count += 2;
map_list_ptr->map_list_map = ptr;
while ((ch = getc (f)) != '\n') while ((ch = getc (f)) != '\n')
if (ch == EOF) if (ch == EOF)
break; break;
} }
fclose (f); fclose (f);
} }
/* Add this information to the cache. */ /* Terminate the list of maps. */
map_list_ptr->map_list_next = CPP_OPTION (pfile, map_list); dir->name_map[count] = NULL;
CPP_OPTION (pfile, map_list) = map_list_ptr;
return map_list_ptr->map_list_map;
} }
/* Remap an unsimplified path NAME based on the file_name_map (if any) /* Remap a FILE's name based on the file_name_map, if any, for
for LOC. */ FILE->dir. If the file name has any directory separators,
recursively check those directories too. */
static char * static char *
remap_filename (cpp_reader *pfile, char *name, struct cpp_path *loc) remap_filename (cpp_reader *pfile, _cpp_file *file)
{ {
struct file_name_map *map; const char *fname, *p;
const char *from, *p; char *new_dir;
char *dir; cpp_dir *dir;
size_t index, len;
if (! loc->name_map)
{
/* 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;
for (map = loc->name_map; map; map = map->map_next)
if (!strcmp (map->map_from, from))
return map->map_to;
/* Try to find a mapping file for the particular directory we are dir = file->dir;
looking in. Thus #include <sys/types.h> will look up sys/types.h fname = file->name;
in /usr/include/header.gcc and look up types.h in
/usr/include/sys/header.gcc. */
p = strrchr (name, '/');
if (!p)
return name;
/* We know p != name as absolute paths don't call remap_filename. */ for (;;)
if (p == name) {
cpp_error (pfile, DL_ICE, "absolute file name in remap_filename"); if (!dir->name_map)
read_name_map (dir);
dir = 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, from))
return map->map_to;
return name; for (index = 0; dir->name_map[index]; index += 2)
} if (!strcmp (dir->name_map[index], fname))
return xstrdup (dir->name_map[index + 1]);
/* Set the include chain for "" to QUOTE, for <> to BRACKET. If p = strchr (fname, '/');
QUOTE_IGNORES_SOURCE_DIR, then "" includes do not look in the if (!p || p == fname)
directory of the including file. return NULL;
If BRACKET does not lie in the QUOTE chain, it is set to QUOTE. */ len = dir->len + (p - fname + 1);
void new_dir = xmalloc (len + 1);
cpp_set_include_chains (cpp_reader *pfile, cpp_path *quote, cpp_path *bracket, memcpy (new_dir, dir->name, dir->len);
int quote_ignores_source_dir) memcpy (new_dir + dir->len, fname, p - fname + 1);
{ new_dir[len] = '\0';
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 = make_cpp_dir (pfile, new_dir, dir->sysp);
{ fname = p + 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, /* Return true if FILE is usable by PCH. */
when it is followed by a ".." component. We use lstat to avoid static bool
symlinks if we have it. If not, we can still catch errors with include_pch_p (_cpp_file *file)
stat (). */
static int
remove_component_p (const char *path)
{ {
struct stat s; return file->pch & 1;
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;
return result == 0 && S_ISDIR (s.st_mode);
} }
/* Simplify a path name in place, deleting redundant components. This /* Returns true if PCHNAME is a valid PCH file for FILE. */
reduces OS overhead and guarantees that equivalent paths compare static bool
the same (modulo symlinks). validate_pch (cpp_reader *pfile, _cpp_file *file, const char *pchname)
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)
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 (char *path ATTRIBUTE_UNUSED)
{ {
#ifndef VMS const char *saved_path = file->path;
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;
#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 leading /s. */ file->path = pchname;
if (*from == '/') if (open_file (file))
{ {
absolute = 1; if ((file->pch & 2) == 0)
to++; file->pch = pfile->cb.valid_pch (pfile, pchname, file->fd);
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 = orig_base = to;
for (;;)
{
int move_base = 0;
while (*from == '/')
from++;
if (*from == '\0')
break;
if (*from == '.') if (!include_pch_p (file))
{ {
if (from[1] == '\0') close (file->fd);
break; file->fd = -1;
if (from[1] == '/')
{
from += 2;
continue;
}
else if (from[1] == '.' && (from[2] == '/' || from[2] == '\0'))
{
/* 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 (CPP_OPTION (pfile, print_include_names))
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 != '/') unsigned int i;
to--; for (i = 1; i < pfile->line_maps.depth; i++)
from += 2; putc ('.', stderr);
continue; fprintf (stderr, "%c %s\n",
} include_pch_p (file) ? '!' : 'x', pchname);
}
move_base = 1;
}
} }
/* Add the component separator. */
if (to > orig_base)
*to++ = '/';
/* Copy this component until the trailing null or '/'. */
while (*from != '\0' && *from != '/')
*to++ = *from++;
if (move_base)
base = to;
} }
else
file->pch = 2;
/* Change the empty string to "." so that it is not treated as stdin. file->path = saved_path;
Null terminate. */ return include_pch_p (file);
if (to == path)
*to++ = '.';
*to = '\0';
#else /* VMS */
errno = 0;
#endif /* !VMS */
} }
...@@ -24,6 +24,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */ ...@@ -24,6 +24,7 @@ Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. */
#define GCC_CPPHASH_H #define GCC_CPPHASH_H
#include "hashtable.h" #include "hashtable.h"
#include "hashtab.h"
#ifdef HAVE_ICONV #ifdef HAVE_ICONV
#include <iconv.h> #include <iconv.h>
...@@ -36,6 +37,7 @@ struct directive; /* Deliberately incomplete. */ ...@@ -36,6 +37,7 @@ struct directive; /* Deliberately incomplete. */
struct pending_option; struct pending_option;
struct op; struct op;
struct strbuf; struct strbuf;
struct _cpp_file;
typedef bool (*convert_f) (iconv_t, const unsigned char *, size_t, typedef bool (*convert_f) (iconv_t, const unsigned char *, size_t,
struct strbuf *); struct strbuf *);
...@@ -281,9 +283,9 @@ struct cpp_buffer ...@@ -281,9 +283,9 @@ struct cpp_buffer
struct cpp_buffer *prev; struct cpp_buffer *prev;
/* Pointer into the include table; non-NULL if this is a file /* Pointer into the file table; non-NULL if this is a file buffer.
buffer. Used for include_next and to record control macros. */ Used for include_next and to record control macros. */
struct include_file *inc; struct _cpp_file *file;
/* Value of if_stack at start of this file. /* Value of if_stack at start of this file.
Used to prohibit unmatched #endif (etc) in an include file. */ Used to prohibit unmatched #endif (etc) in an include file. */
...@@ -314,7 +316,7 @@ struct cpp_buffer ...@@ -314,7 +316,7 @@ struct cpp_buffer
/* The directory of the this buffer's file. Its NAME member is not /* The directory of the this buffer's file. Its NAME member is not
allocated, so we don't need to worry about freeing it. */ allocated, so we don't need to worry about freeing it. */
struct cpp_path dir; struct cpp_dir dir;
/* Used for buffer overlays by cpptrad.c. */ /* Used for buffer overlays by cpptrad.c. */
const uchar *saved_cur, *saved_rlimit; const uchar *saved_cur, *saved_rlimit;
...@@ -355,8 +357,24 @@ struct cpp_reader ...@@ -355,8 +357,24 @@ struct cpp_reader
const struct directive *directive; const struct directive *directive;
/* Search paths for include files. */ /* Search paths for include files. */
struct cpp_path *quote_include; /* "" */ struct cpp_dir *quote_include; /* "" */
struct cpp_path *bracket_include; /* <> */ struct cpp_dir *bracket_include; /* <> */
struct cpp_dir no_search_path; /* No path. */
/* Chain of files that were #import-ed or contain #pragma once. */
struct _cpp_file *once_only_files;
/* File and directory hash table. */
htab_t file_hash;
struct file_hash_entry *file_hash_entries;
unsigned int file_hash_entries_allocated, file_hash_entries_used;
/* Nonzero means don't look for #include "foo" the source-file
directory. */
bool quote_ignores_source_dir;
/* Non-zero if any file has contained #pragma once. */
bool saw_pragma_once;
/* Multiple include optimization. */ /* Multiple include optimization. */
const cpp_hashnode *mi_cmacro; const cpp_hashnode *mi_cmacro;
...@@ -386,13 +404,6 @@ struct cpp_reader ...@@ -386,13 +404,6 @@ struct cpp_reader
wide execution character set. */ wide execution character set. */
struct cset_converter wide_cset_desc; struct cset_converter wide_cset_desc;
/* Tree of other included files. See cppfiles.c. */
struct splay_tree_s *all_include_files;
/* Current maximum length of directory names in the search path
for include files. (Altered as we get more of them.) */
unsigned int max_include_len;
/* Date and time text. Calculated together if either is requested. */ /* Date and time text. Calculated together if either is requested. */
const uchar *date; const uchar *date;
const uchar *time; const uchar *time;
...@@ -432,12 +443,8 @@ struct cpp_reader ...@@ -432,12 +443,8 @@ struct cpp_reader
preprocessor. */ preprocessor. */
struct spec_nodes spec_nodes; struct spec_nodes spec_nodes;
/* Nonzero means don't look for #include "foo" the source-file
directory. */
unsigned char quote_ignores_source_dir;
/* Whether cpplib owns the hashtable. */ /* Whether cpplib owns the hashtable. */
unsigned char our_hashtable; bool our_hashtable;
/* Traditional preprocessing output buffer (a logical line). */ /* Traditional preprocessing output buffer (a logical line). */
struct struct
...@@ -509,16 +516,16 @@ extern void _cpp_init_hashtable (cpp_reader *, hash_table *); ...@@ -509,16 +516,16 @@ extern void _cpp_init_hashtable (cpp_reader *, hash_table *);
extern void _cpp_destroy_hashtable (cpp_reader *); extern void _cpp_destroy_hashtable (cpp_reader *);
/* In cppfiles.c */ /* In cppfiles.c */
extern void _cpp_mark_file_once_only (cpp_reader *, struct _cpp_file *, bool);
extern void _cpp_fake_include (cpp_reader *, const char *); extern void _cpp_fake_include (cpp_reader *, const char *);
extern void _cpp_never_reread (struct include_file *); extern bool _cpp_stack_file (cpp_reader *, const char *);
extern bool _cpp_read_file (cpp_reader *, const char *); extern bool _cpp_stack_include (cpp_reader *, const char *, int,
extern bool _cpp_execute_include (cpp_reader *, const char *, int,
enum include_type); enum include_type);
extern int _cpp_compare_file_date (cpp_reader *, const char *, int); extern int _cpp_compare_file_date (cpp_reader *, const char *, int);
extern void _cpp_report_missing_guards (cpp_reader *); extern void _cpp_report_missing_guards (cpp_reader *);
extern void _cpp_init_includes (cpp_reader *); extern void _cpp_init_files (cpp_reader *);
extern void _cpp_cleanup_includes (cpp_reader *); extern void _cpp_cleanup_files (cpp_reader *);
extern void _cpp_pop_file_buffer (cpp_reader *, struct include_file *); extern void _cpp_pop_file_buffer (cpp_reader *, struct _cpp_file *);
/* In cppexp.c */ /* In cppexp.c */
extern bool _cpp_parse_expr (cpp_reader *); extern bool _cpp_parse_expr (cpp_reader *);
......
...@@ -161,6 +161,12 @@ cpp_create_reader (enum c_lang lang, hash_table *table) ...@@ -161,6 +161,12 @@ cpp_create_reader (enum c_lang lang, hash_table *table)
CPP_OPTION (pfile, narrow_charset) = 0; CPP_OPTION (pfile, narrow_charset) = 0;
CPP_OPTION (pfile, wide_charset) = 0; CPP_OPTION (pfile, wide_charset) = 0;
/* A fake empty "directory" used as the starting point for files
looked up without a search path. Name cannot be '/' because we
don't want to prepend anything at all to filenames using it. All
other entries are correct zero-initialized. */
pfile->no_search_path.name = (char *) "";
/* Initialize the line map. Start at logical line 1, so we can use /* Initialize the line map. Start at logical line 1, so we can use
a line number of zero for special states. */ a line number of zero for special states. */
linemap_init (&pfile->line_maps); linemap_init (&pfile->line_maps);
...@@ -196,7 +202,7 @@ cpp_create_reader (enum c_lang lang, hash_table *table) ...@@ -196,7 +202,7 @@ cpp_create_reader (enum c_lang lang, hash_table *table)
(void *(*) (long)) xmalloc, (void *(*) (long)) xmalloc,
(void (*) (void *)) free); (void (*) (void *)) free);
_cpp_init_includes (pfile); _cpp_init_files (pfile);
_cpp_init_hashtable (pfile, table); _cpp_init_hashtable (pfile, table);
...@@ -231,7 +237,7 @@ cpp_destroy (cpp_reader *pfile) ...@@ -231,7 +237,7 @@ cpp_destroy (cpp_reader *pfile)
obstack_free (&pfile->buffer_ob, 0); obstack_free (&pfile->buffer_ob, 0);
_cpp_destroy_hashtable (pfile); _cpp_destroy_hashtable (pfile);
_cpp_cleanup_includes (pfile); _cpp_cleanup_files (pfile);
_cpp_destroy_iconv (pfile); _cpp_destroy_iconv (pfile);
_cpp_free_buff (pfile->a_buff); _cpp_free_buff (pfile->a_buff);
...@@ -427,15 +433,6 @@ cpp_add_dependency_target (cpp_reader *pfile, const char *target, int quote) ...@@ -427,15 +433,6 @@ cpp_add_dependency_target (cpp_reader *pfile, const char *target, int quote)
deps_add_target (pfile->deps, target, quote); deps_add_target (pfile->deps, target, quote);
} }
/* This sets up for processing input from the file FNAME.
It returns false on error. */
bool
cpp_read_next_file (cpp_reader *pfile, const char *fname)
{
/* Open the main input file. */
return _cpp_read_file (pfile, fname);
}
/* This is called after options have been parsed, and partially /* This is called after options have been parsed, and partially
processed. Setup for processing input from the file named FNAME, processed. Setup for processing input from the file named FNAME,
or stdin if it is the empty string. Return the original filename or stdin if it is the empty string. Return the original filename
...@@ -461,7 +458,7 @@ cpp_read_main_file (cpp_reader *pfile, const char *fname) ...@@ -461,7 +458,7 @@ cpp_read_main_file (cpp_reader *pfile, const char *fname)
} }
pfile->line = 1; pfile->line = 1;
if (!cpp_read_next_file (pfile, fname)) if (!cpp_stack_file (pfile, fname))
return NULL; return NULL;
/* Set this here so the client can change the option if it wishes, /* Set this here so the client can change the option if it wishes,
......
...@@ -441,7 +441,7 @@ run_directive (cpp_reader *pfile, int dir_no, const char *buf, size_t count) ...@@ -441,7 +441,7 @@ run_directive (cpp_reader *pfile, int dir_no, const char *buf, size_t count)
/* from_stage3 */ true, 1); /* from_stage3 */ true, 1);
/* Disgusting hack. */ /* Disgusting hack. */
if (dir_no == T_PRAGMA) if (dir_no == T_PRAGMA)
pfile->buffer->inc = pfile->buffer->prev->inc; pfile->buffer->file = pfile->buffer->prev->file;
start_directive (pfile); start_directive (pfile);
/* This is a short-term fix to prevent a leading '#' being /* This is a short-term fix to prevent a leading '#' being
...@@ -454,7 +454,7 @@ run_directive (cpp_reader *pfile, int dir_no, const char *buf, size_t count) ...@@ -454,7 +454,7 @@ run_directive (cpp_reader *pfile, int dir_no, const char *buf, size_t count)
pfile->directive->handler (pfile); pfile->directive->handler (pfile);
end_directive (pfile, 1); end_directive (pfile, 1);
if (dir_no == T_PRAGMA) if (dir_no == T_PRAGMA)
pfile->buffer->inc = NULL; pfile->buffer->file = NULL;
_cpp_pop_buffer (pfile); _cpp_pop_buffer (pfile);
} }
...@@ -684,7 +684,7 @@ do_include_common (cpp_reader *pfile, enum include_type type) ...@@ -684,7 +684,7 @@ do_include_common (cpp_reader *pfile, enum include_type type)
pfile->cb.include (pfile, pfile->directive_line, pfile->cb.include (pfile, pfile->directive_line,
pfile->directive->name, fname, angle_brackets); pfile->directive->name, fname, angle_brackets);
_cpp_execute_include (pfile, fname, angle_brackets, type); _cpp_stack_include (pfile, fname, angle_brackets, type);
} }
free ((void *) fname); free ((void *) fname);
...@@ -699,13 +699,6 @@ do_include (cpp_reader *pfile) ...@@ -699,13 +699,6 @@ do_include (cpp_reader *pfile)
static void static void
do_import (cpp_reader *pfile) do_import (cpp_reader *pfile)
{ {
if (CPP_OPTION (pfile, warn_import))
{
CPP_OPTION (pfile, warn_import) = 0;
cpp_error (pfile, DL_WARNING,
"#import is obsolete, use an #ifndef wrapper in the header file");
}
do_include_common (pfile, IT_IMPORT); do_include_common (pfile, IT_IMPORT);
} }
...@@ -1170,15 +1163,11 @@ do_pragma (cpp_reader *pfile) ...@@ -1170,15 +1163,11 @@ do_pragma (cpp_reader *pfile)
static void static void
do_pragma_once (cpp_reader *pfile) do_pragma_once (cpp_reader *pfile)
{ {
if (CPP_OPTION (pfile, warn_deprecated))
cpp_error (pfile, DL_WARNING, "#pragma once is obsolete");
if (pfile->buffer->prev == NULL) if (pfile->buffer->prev == NULL)
cpp_error (pfile, DL_WARNING, "#pragma once in main file"); cpp_error (pfile, DL_WARNING, "#pragma once in main file");
else
_cpp_never_reread (pfile->buffer->inc);
check_eol (pfile); check_eol (pfile);
_cpp_mark_file_once_only (pfile, pfile->buffer->file, false);
} }
/* Handle #pragma GCC poison, to poison one or more identifiers so /* Handle #pragma GCC poison, to poison one or more identifiers so
...@@ -1944,7 +1933,7 @@ void ...@@ -1944,7 +1933,7 @@ void
_cpp_pop_buffer (cpp_reader *pfile) _cpp_pop_buffer (cpp_reader *pfile)
{ {
cpp_buffer *buffer = pfile->buffer; cpp_buffer *buffer = pfile->buffer;
struct include_file *inc = buffer->inc; struct _cpp_file *inc = buffer->file;
struct if_stack *ifs; struct if_stack *ifs;
/* Walk back up the conditional stack till we reach its level at /* Walk back up the conditional stack till we reach its level at
......
...@@ -39,10 +39,9 @@ typedef struct cpp_string cpp_string; ...@@ -39,10 +39,9 @@ typedef struct cpp_string cpp_string;
typedef struct cpp_hashnode cpp_hashnode; typedef struct cpp_hashnode cpp_hashnode;
typedef struct cpp_macro cpp_macro; typedef struct cpp_macro cpp_macro;
typedef struct cpp_callbacks cpp_callbacks; typedef struct cpp_callbacks cpp_callbacks;
typedef struct cpp_path cpp_path; typedef struct cpp_dir cpp_dir;
struct answer; struct answer;
struct file_name_map_list;
/* The first three groups, apart from '=', can appear in preprocessor /* The first three groups, apart from '=', can appear in preprocessor
expressions (+= and -= are used to indicate unary + and - resp.). expressions (+= and -= are used to indicate unary + and - resp.).
...@@ -214,10 +213,6 @@ struct cpp_options ...@@ -214,10 +213,6 @@ struct cpp_options
/* Characters between tab stops. */ /* Characters between tab stops. */
unsigned int tabstop; unsigned int tabstop;
/* Map between header names and file names, used only on DOS where
file names are limited in length. */
struct file_name_map_list *map_list;
/* The language we're preprocessing. */ /* The language we're preprocessing. */
enum c_lang lang; enum c_lang lang;
...@@ -397,12 +392,12 @@ struct cpp_callbacks ...@@ -397,12 +392,12 @@ struct cpp_callbacks
}; };
/* Chain of directories to look for include files in. */ /* Chain of directories to look for include files in. */
struct cpp_path struct cpp_dir
{ {
/* NULL-terminated singly-linked list. */ /* NULL-terminated singly-linked list. */
struct cpp_path *next; struct cpp_dir *next;
/* NAME need not be NUL-terminated once inside cpplib. */ /* NAME of the directory, NUL-terminated. */
char *name; char *name;
unsigned int len; unsigned int len;
...@@ -410,9 +405,9 @@ struct cpp_path ...@@ -410,9 +405,9 @@ struct cpp_path
"C" guards for C++. */ "C" guards for C++. */
unsigned char sysp; unsigned char sysp;
/* Mapping of file names for this directory for MS-DOS and /* Mapping of file names for this directory for MS-DOS and related
related platforms. */ platforms. A NULL-terminated array of (from, to) pairs. */
struct file_name_map *name_map; const char **name_map;
/* The C front end uses these to recognize duplicated /* The C front end uses these to recognize duplicated
directories in the search path. */ directories in the search path. */
...@@ -516,7 +511,7 @@ extern void cpp_set_lang (cpp_reader *, enum c_lang); ...@@ -516,7 +511,7 @@ extern void cpp_set_lang (cpp_reader *, enum c_lang);
extern void cpp_add_dependency_target (cpp_reader *, const char *, int); extern void cpp_add_dependency_target (cpp_reader *, const char *, int);
/* Set the include paths. */ /* Set the include paths. */
extern void cpp_set_include_chains (cpp_reader *, cpp_path *, cpp_path *, int); extern void cpp_set_include_chains (cpp_reader *, cpp_dir *, cpp_dir *, int);
/* Call these to get pointers to the options and callback structures /* Call these to get pointers to the options and callback structures
for a given reader. These pointers are good until you call for a given reader. These pointers are good until you call
...@@ -535,9 +530,9 @@ extern void cpp_set_callbacks (cpp_reader *, cpp_callbacks *); ...@@ -535,9 +530,9 @@ extern void cpp_set_callbacks (cpp_reader *, cpp_callbacks *);
too. If there was an error opening the file, it returns NULL. */ too. If there was an error opening the file, it returns NULL. */
extern const char *cpp_read_main_file (cpp_reader *, const char *); extern const char *cpp_read_main_file (cpp_reader *, const char *);
/* This continues processing to a new file. It will return false if /* Stacks a new file. It will return false if there was an error
there was an error opening the file. */ opening the file. */
extern bool cpp_read_next_file (cpp_reader *, const char *); extern bool cpp_stack_file (cpp_reader *, const char *);
/* Set up built-ins like __FILE__. */ /* Set up built-ins like __FILE__. */
extern void cpp_init_builtins (cpp_reader *, int); extern void cpp_init_builtins (cpp_reader *, int);
...@@ -715,7 +710,7 @@ extern unsigned char *cpp_quote_string (unsigned char *, const unsigned char *, ...@@ -715,7 +710,7 @@ extern unsigned char *cpp_quote_string (unsigned char *, const unsigned char *,
unsigned int); unsigned int);
/* In cppfiles.c */ /* In cppfiles.c */
extern int cpp_included (cpp_reader *, const char *); extern bool cpp_included (cpp_reader *, const char *);
extern void cpp_make_system_header (cpp_reader *, int, int); extern void cpp_make_system_header (cpp_reader *, int, int);
extern void cpp_simplify_path (char *); extern void cpp_simplify_path (char *);
extern bool cpp_push_include (cpp_reader *, const char *); extern bool cpp_push_include (cpp_reader *, const char *);
......
2003-07-29 Neil Booth <neil@daikokuya.co.uk>
* gcc.dg/cpp/include2.c: Only expect one message.
2003-07-29 Nathan Sidwell <nathan@codesourcery.com> 2003-07-29 Nathan Sidwell <nathan@codesourcery.com>
PR c++/9447 PR c++/9447
......
...@@ -10,7 +10,7 @@ ...@@ -10,7 +10,7 @@
#include <silly\>> /* { dg-warning "extra tokens" "" } */ #include <silly\>> /* { dg-warning "extra tokens" "" } */
#include "silly\"" /* { dg-warning "extra tokens" "" } */ #include "silly\"" /* { dg-warning "extra tokens" "" } */
/* These first 2 errors are No such file or directory. However, this /* These error is No such file or directory, just once. However, this
message is locale-dependent, so don't test for it. */ message is locale-dependent, so don't test for it. */
/* { dg-error "silly" "" { target *-*-* } 10 } */ /* { dg-error "silly" "" { target *-*-* } 10 } */
/* { dg-error "silly" "" { target *-*-* } 11 } */
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment