/* Part of CPP library. File handling.
Copyright (C) 1986, 1987, 1989, 1992, 1993, 1994, 1995, 1998,
- 1999, 2000, 2001, 2002, 2003, 2004 Free Software Foundation, Inc.
+ 1999, 2000, 2001, 2002, 2003, 2004, 2005, 2006, 2007
+ 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. */
+Foundation, 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */
#include "config.h"
#include "system.h"
#include "cpplib.h"
#include "internal.h"
#include "mkdeps.h"
+#include "obstack.h"
#include "hashtab.h"
#include "md5.h"
#include <dirent.h>
#endif
#ifdef __DJGPP__
+#include <io.h>
/* For DJGPP redirected input is opened in text mode. */
# define set_stdin_to_binary_mode() \
if (! isatty (0)) setmode (0, O_BINARY)
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 void open_file_failed (cpp_reader *pfile, _cpp_file *file, int);
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 void destroy_cpp_file (_cpp_file *);
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 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 int pchf_adder (void **slot, void *data);
static int pchf_save_compare (const void *e1, const void *e2);
static int pchf_compare (const void *d_p, const void *e_p);
static bool check_file_against_entries (cpp_reader *, _cpp_file *, bool);
close (file->fd);
file->fd = -1;
}
+#if defined(_WIN32) && !defined(__CYGWIN__)
+ else if (errno == EACCES)
+ {
+ /* On most UNIX systems, open succeeds on a directory. Above,
+ we check if we have opened a directory and if so, set errno
+ to ENOENT. However, on Windows, opening a directory
+ fails with EACCES. We want to return ENOENT in that
+ case too. */
+ if (stat (file->path, &file->st) == 0
+ && S_ISDIR (file->st.st_mode))
+ errno = ENOENT;
+ else
+ /* The call to stat may have reset errno. */
+ errno = EACCES;
+ }
+#endif
else if (errno == ENOTDIR)
errno = ENOENT;
flen = strlen (path);
len = flen + sizeof (extension);
- pchname = xmalloc (len);
+ pchname = XNEWVEC (char, len);
memcpy (pchname, path, flen);
memcpy (pchname + flen, extension, sizeof (extension));
if (dlen + plen > len)
{
len += dlen + 64;
- pchname = xrealloc (pchname, len);
+ pchname = XRESIZEVEC (char, pchname, len);
}
memcpy (pchname + plen, d->d_name, dlen);
valid = validate_pch (pfile, file, pchname);
if (path)
{
+ hashval_t hv = htab_hash_string (path);
+ char *copy;
+ void **pp;
+
+ if (htab_find_with_hash (pfile->nonexistent_file_hash, path, hv) != NULL)
+ {
+ file->err_no = ENOENT;
+ return false;
+ }
+
file->path = path;
if (pch_open_file (pfile, file, invalid_pch))
return true;
if (file->err_no != ENOENT)
{
- open_file_failed (pfile, file);
+ open_file_failed (pfile, file, 0);
return true;
}
+ /* We copy the path name onto an obstack partly so that we don't
+ leak the memory, but mostly so that we don't fragment the
+ heap. */
+ copy = obstack_copy0 (&pfile->nonexistent_file_ob, path,
+ strlen (path));
free (path);
+ pp = htab_find_slot_with_hash (pfile->nonexistent_file_hash,
+ copy, hv, INSERT);
+ *pp = copy;
+
file->path = file->name;
}
else
to open_file().
*/
_cpp_file *
-_cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, bool fake)
+_cpp_find_file (cpp_reader *pfile, const char *fname, cpp_dir *start_dir, bool fake, int angle_brackets)
{
struct file_hash_entry *entry, **hash_slot;
_cpp_file *file;
bool invalid_pch = false;
+ bool saw_bracket_include = false;
+ bool saw_quote_include = false;
+ struct cpp_dir *found_in_cache = NULL;
/* Ensure we get no confusion between cached files and directories. */
if (start_dir == NULL)
/* Try each path in the include chain. */
for (; !fake ;)
{
- if (file->dir == pfile->quote_include
- || file->dir == pfile->bracket_include)
- {
- entry = search_cache (*hash_slot, file->dir);
- if (entry)
- {
- /* Found the same file again. Record it as reachable
- from this position, too. */
- free ((char *) file->name);
- free (file);
- file = entry->u.file;
- goto found;
- }
- }
-
if (find_file_in_dir (pfile, file, &invalid_pch))
break;
if (file->dir == NULL)
{
if (search_path_exhausted (pfile, fname, file))
- return file;
+ {
+ /* Although this file must not go in the cache, because
+ the file found might depend on things (like the current file)
+ that aren't represented in the cache, it still has to go in
+ the list of all files so that #import works. */
+ file->next_file = pfile->all_files;
+ pfile->all_files = file;
+ return file;
+ }
- open_file_failed (pfile, file);
+ open_file_failed (pfile, file, angle_brackets);
if (invalid_pch)
{
cpp_error (pfile, CPP_DL_ERROR,
}
break;
}
- }
-
- /* This is a new file; put it in the list. */
- file->next_file = pfile->all_files;
- pfile->all_files = file;
- /* If this file was found in the directory-of-the-current-file,
- check whether that directory is reachable via one of the normal
- search paths. If so, we must record this entry as being
- reachable that way, otherwise we will mistakenly reprocess this
- file if it is included later from the normal search path. */
- if (file->dir && start_dir->next == pfile->quote_include)
- {
- cpp_dir *d;
- cpp_dir *proper_start_dir = pfile->quote_include;
+ /* Only check the cache for the starting location (done above)
+ and the quote and bracket chain heads because there are no
+ other possible starting points for searches. */
+ if (file->dir == pfile->bracket_include)
+ saw_bracket_include = true;
+ else if (file->dir == pfile->quote_include)
+ saw_quote_include = true;
+ else
+ continue;
- for (d = proper_start_dir;; d = d->next)
+ entry = search_cache (*hash_slot, file->dir);
+ if (entry)
{
- if (d == pfile->bracket_include)
- proper_start_dir = d;
- if (d == 0)
- {
- proper_start_dir = 0;
- break;
- }
- /* file->dir->name will have a trailing slash. */
- if (!strncmp (d->name, file->dir->name, file->dir->len - 1))
- break;
+ found_in_cache = file->dir;
+ break;
}
- if (proper_start_dir)
- start_dir = proper_start_dir;
}
- found:
+ if (entry)
+ {
+ /* Cache for START_DIR too, sharing the _cpp_file structure. */
+ free ((char *) file->name);
+ free (file);
+ file = entry->u.file;
+ }
+ else
+ {
+ /* This is a new file; put it in the list. */
+ file->next_file = pfile->all_files;
+ pfile->all_files = file;
+ }
+
/* Store this new result in the hash table. */
entry = new_file_hash_entry (pfile);
entry->next = *hash_slot;
entry->u.file = file;
*hash_slot = entry;
+ /* If we passed the quote or bracket chain heads, cache them also.
+ This speeds up processing if there are lots of -I options. */
+ if (saw_bracket_include
+ && pfile->bracket_include != start_dir
+ && found_in_cache != pfile->bracket_include)
+ {
+ entry = new_file_hash_entry (pfile);
+ entry->next = *hash_slot;
+ entry->start_dir = pfile->bracket_include;
+ entry->u.file = file;
+ *hash_slot = entry;
+ }
+ if (saw_quote_include
+ && pfile->quote_include != start_dir
+ && found_in_cache != pfile->quote_include)
+ {
+ entry = new_file_hash_entry (pfile);
+ entry->next = *hash_slot;
+ entry->start_dir = pfile->quote_include;
+ entry->u.file = file;
+ *hash_slot = entry;
+ }
+
return file;
}
the majority of C source files. */
size = 8 * 1024;
- buf = xmalloc (size + 1);
+ buf = XNEWVEC (uchar, size + 1);
total = 0;
while ((count = read (file->fd, buf + total, size - total)) > 0)
{
if (regular)
break;
size *= 2;
- buf = xrealloc (buf, size + 1);
+ buf = XRESIZEVEC (uchar, buf, size + 1);
}
}
if (file->fd == -1 && !open_file (file))
{
- open_file_failed (pfile, file);
+ open_file_failed (pfile, file, 0);
return false;
}
if ((import || f->once_only)
&& 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))
- break;
+ && f->st.st_size == file->st.st_size)
+ {
+ _cpp_file *ref_file;
+ bool same_file_p = false;
+
+ if (f->buffer && !f->buffer_valid)
+ {
+ /* We already have a buffer but it is not valid, because
+ the file is still stacked. Make a new one. */
+ ref_file = make_cpp_file (pfile, f->dir, f->name);
+ ref_file->path = f->path;
+ }
+ else
+ /* The file is not stacked anymore. We can reuse it. */
+ ref_file = f;
+
+ same_file_p = read_file (pfile, ref_file)
+ /* Size might have changed in read_file(). */
+ && ref_file->st.st_size == file->st.st_size
+ && !memcmp (ref_file->buffer,
+ file->buffer,
+ file->st.st_size);
+
+ if (f->buffer && !f->buffer_valid)
+ {
+ ref_file->path = 0;
+ destroy_cpp_file (ref_file);
+ }
+
+ if (same_file_p)
+ break;
+ }
}
return f == NULL;
/* Stack the buffer. */
buffer = cpp_push_buffer (pfile, file->buffer, file->st.st_size,
- CPP_OPTION (pfile, preprocessed));
+ CPP_OPTION (pfile, preprocessed)
+ && !CPP_OPTION (pfile, directives_only));
buffer->file = file;
buffer->sysp = sysp;
if (!file->dir_name)
{
size_t len = lbasename (file->path) - file->path;
- char *dir_name = xmalloc (len + 1);
+ char *dir_name = XNEWVEC (char, len + 1);
memcpy (dir_name, file->path, len);
dir_name[len] = '\0';
if (!dir)
return false;
- file = _cpp_find_file (pfile, fname, dir, false);
+ file = _cpp_find_file (pfile, fname, dir, false, angle_brackets);
/* Compensate for the increment in linemap_add. In the case of a
normal #include, we're currently at the start of the line
/* Could not open FILE. The complication is dependency output. */
static void
-open_file_failed (cpp_reader *pfile, _cpp_file *file)
+open_file_failed (cpp_reader *pfile, _cpp_file *file, int angle_brackets)
{
int sysp = pfile->line_table->highest_line > 1 && pfile->buffer ? pfile->buffer->sysp : 0;
- bool print_dep = CPP_OPTION (pfile, deps.style) > !!sysp;
+ bool print_dep = CPP_OPTION (pfile, deps.style) > (angle_brackets || !!sysp);
errno = file->err_no;
if (print_dep && CPP_OPTION (pfile, deps.missing_files) && errno == ENOENT)
static struct file_hash_entry *
search_cache (struct file_hash_entry *head, const cpp_dir *start_dir)
{
- struct file_hash_entry *p;
+ while (head && head->start_dir != start_dir)
+ head = head->next;
- /* Look for a file that was found from a search starting at the
- given location. */
- for (p = head; p; p = p->next)
- if (p->start_dir == start_dir)
- return p;
- return 0;
+ return head;
}
/* Allocate a new _cpp_file structure. */
{
_cpp_file *file;
- file = xcalloc (1, sizeof (_cpp_file));
+ file = XCNEW (_cpp_file);
file->main_file = !pfile->buffer;
file->fd = -1;
file->dir = dir;
return file;
}
+/* Release a _cpp_file structure. */
+static void
+destroy_cpp_file (_cpp_file *file)
+{
+ if (file->buffer)
+ free ((void *) file->buffer);
+ free ((void *) file->name);
+ free (file);
+}
+
/* 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.
cpp_dir *dir;
hash_slot = (struct file_hash_entry **)
- htab_find_slot_with_hash (pfile->file_hash, dir_name,
+ htab_find_slot_with_hash (pfile->dir_hash, dir_name,
htab_hash_string (dir_name),
INSERT);
if (entry->start_dir == NULL)
return entry->u.dir;
- dir = xcalloc (1, sizeof (cpp_dir));
+ dir = XCNEW (cpp_dir);
dir->next = pfile->quote_include;
dir->name = (char *) dir_name;
dir->len = strlen (dir_name);
{
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));
+ pfile->file_hash_entries = XNEWVEC (struct file_hash_entry,
+ pfile->file_hash_entries_allocated);
}
/* Return a new file hash entry. */
{
struct file_hash_entry *entry;
- entry = htab_find_with_hash (pfile->file_hash, fname,
- htab_hash_string (fname));
+ entry = (struct file_hash_entry *)
+ htab_find_with_hash (pfile->file_hash, fname, htab_hash_string (fname));
while (entry && (entry->start_dir == NULL || entry->u.file->err_no))
entry = entry->next;
return strcmp (hname, fname) == 0;
}
+/* Compare entries in the nonexistent file hash table. These are just
+ strings. */
+static int
+nonexistent_file_hash_eq (const void *p, const void *q)
+{
+ return strcmp (p, q) == 0;
+}
+
/* Initialize everything in this source file. */
void
_cpp_init_files (cpp_reader *pfile)
{
pfile->file_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,
NULL, xcalloc, free);
+ pfile->dir_hash = htab_create_alloc (127, file_hash_hash, file_hash_eq,
+ NULL, xcalloc, free);
allocate_file_hash_entries (pfile);
+ pfile->nonexistent_file_hash = htab_create_alloc (127, htab_hash_string,
+ nonexistent_file_hash_eq,
+ NULL, xcalloc, free);
+ _obstack_begin (&pfile->nonexistent_file_ob, 0, 0,
+ (void *(*) (long)) xmalloc,
+ (void (*) (void *)) free);
}
/* Finalize everything in this source file. */
_cpp_cleanup_files (cpp_reader *pfile)
{
htab_delete (pfile->file_hash);
+ htab_delete (pfile->dir_hash);
+ htab_delete (pfile->nonexistent_file_hash);
+ obstack_free (&pfile->nonexistent_file_ob, 0);
}
/* Enter a file name in the hash for the sake of cpp_included. */
void
_cpp_fake_include (cpp_reader *pfile, const char *fname)
{
- _cpp_find_file (pfile, fname, pfile->buffer->file->dir, true);
+ _cpp_find_file (pfile, fname, pfile->buffer->file->dir, true, 0);
}
/* Not everyone who wants to set system-header-ness on a buffer can
if (!dir)
return -1;
- file = _cpp_find_file (pfile, fname, dir, false);
+ file = _cpp_find_file (pfile, fname, dir, false, angle_brackets);
if (file->err_no)
return -1;
{
free ((void *) file->buffer);
file->buffer = NULL;
+ file->buffer_valid = false;
}
}
+/* Inteface to file statistics record in _cpp_file structure. */
+struct stat *
+_cpp_get_file_stat (_cpp_file *file)
+{
+ return &file->st;
+}
+
/* 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.
dlen = dir->len;
flen = strlen (fname);
- path = xmalloc (dlen + 1 + flen + 1);
+ path = XNEWVEC (char, dlen + 1 + flen + 1);
memcpy (path, dir->name, dlen);
if (dlen && path[dlen - 1] != '/')
path[dlen++] = '/';
int len;
len = 20;
- set = alloc = xmalloc (len + 1);
+ set = alloc = XNEWVEC (char, len + 1);
if (! is_space (ch))
{
*set++ = ch;
if (set - alloc == len)
{
len *= 2;
- alloc = xrealloc (alloc, len + 1);
+ alloc = XRESIZEVEC (char, alloc, len + 1);
set = alloc + len / 2;
}
*set++ = ch;
size_t len, count = 0, room = 9;
len = dir->len;
- name = alloca (len + sizeof (FILE_NAME_MAP_FILE) + 1);
+ name = (char *) alloca (len + sizeof (FILE_NAME_MAP_FILE) + 1);
memcpy (name, dir->name, len);
if (len && name[len - 1] != '/')
name[len++] = '/';
strcpy (name + len, FILE_NAME_MAP_FILE);
f = fopen (name, "r");
- dir->name_map = xmalloc (room * sizeof (char *));
+ dir->name_map = XNEWVEC (const char *, room);
/* Silently return NULL if we cannot open. */
if (f)
if (count + 2 > room)
{
room += 8;
- dir->name_map = xrealloc (dir->name_map, room * sizeof (char *));
+ dir->name_map = XRESIZEVEC (const char *, dir->name_map, room);
}
dir->name_map[count] = read_filename_string (ch, f);
return NULL;
len = dir->len + (p - fname + 1);
- new_dir = xmalloc (len + 1);
+ new_dir = XNEWVEC (char, len + 1);
memcpy (new_dir, dir->name, dir->len);
memcpy (new_dir + dir->len, fname, p - fname + 1);
new_dir[len] = '\0';
that's OK. The code does rely on having entries with the same size
next to each other. */
+struct pchf_entry {
+ /* The size of this file. This is used to save running a MD5 checksum
+ if the sizes don't match. */
+ off_t size;
+ /* The MD5 checksum of this file. */
+ unsigned char sum[16];
+ /* Is this file to be included only once? */
+ bool once_only;
+};
+
struct pchf_data {
/* Number of pchf_entry structures. */
size_t count;
the structure if we're processing a regular #include. */
bool have_once_only;
- struct pchf_entry {
- /* The size of this file. This is used to save running a MD5 checksum
- if the sizes don't match. */
- off_t size;
- /* The MD5 checksum of this file. */
- unsigned char sum[16];
- /* Is this file to be included only once? */
- bool once_only;
- } entries[1];
+ struct pchf_entry entries[1];
};
static struct pchf_data *pchf;
-/* Data for pchf_addr. */
-struct pchf_adder_info
+/* A qsort ordering function for pchf_entry structures. */
+
+static int
+pchf_save_compare (const void *e1, const void *e2)
{
- cpp_reader *pfile;
- struct pchf_data *d;
-};
+ return memcmp (e1, e2, sizeof (struct pchf_entry));
+}
-/* A hash traversal function to add entries into DATA->D. */
+/* Create and write to F a pchf_data structure. */
-static int
-pchf_adder (void **slot, void *data)
+bool
+_cpp_save_file_entries (cpp_reader *pfile, FILE *fp)
{
- struct file_hash_entry *h = (struct file_hash_entry *) *slot;
- struct pchf_adder_info *i = (struct pchf_adder_info *) data;
+ size_t count = 0;
+ struct pchf_data *result;
+ size_t result_size;
+ _cpp_file *f;
+
+ for (f = pfile->all_files; f; f = f->next_file)
+ ++count;
+
+ result_size = (sizeof (struct pchf_data)
+ + sizeof (struct pchf_entry) * (count - 1));
+ result = XCNEWVAR (struct pchf_data, result_size);
+
+ result->count = 0;
+ result->have_once_only = false;
- if (h->start_dir != NULL && h->u.file->stack_count != 0)
+ for (f = pfile->all_files; f; f = f->next_file)
{
- struct pchf_data *d = i->d;
- _cpp_file *f = h->u.file;
- size_t count = d->count++;
+ size_t count;
/* This should probably never happen, since if a read error occurred
the PCH file shouldn't be written... */
if (f->dont_read || f->err_no)
- return 1;
+ continue;
+
+ if (f->stack_count == 0)
+ continue;
+
+ count = result->count++;
- d->entries[count].once_only = f->once_only;
+ result->entries[count].once_only = f->once_only;
/* |= is avoided in the next line because of an HP C compiler bug */
- d->have_once_only = d->have_once_only | f->once_only;
+ result->have_once_only = result->have_once_only | f->once_only;
if (f->buffer_valid)
- md5_buffer ((const char *)f->buffer,
- f->st.st_size, d->entries[count].sum);
+ md5_buffer ((const char *)f->buffer,
+ f->st.st_size, result->entries[count].sum);
else
{
FILE *ff;
if (!open_file (f))
{
- open_file_failed (i->pfile, f);
- return 0;
+ open_file_failed (pfile, f, 0);
+ return false;
}
ff = fdopen (f->fd, "rb");
- md5_stream (ff, d->entries[count].sum);
+ md5_stream (ff, result->entries[count].sum);
fclose (ff);
f->fd = oldfd;
}
- d->entries[count].size = f->st.st_size;
+ result->entries[count].size = f->st.st_size;
}
- return 1;
-}
-
-/* A qsort ordering function for pchf_entry structures. */
-
-static int
-pchf_save_compare (const void *e1, const void *e2)
-{
- return memcmp (e1, e2, sizeof (struct pchf_entry));
-}
-
-/* Create and write to F a pchf_data structure. */
-
-bool
-_cpp_save_file_entries (cpp_reader *pfile, FILE *f)
-{
- size_t count = 0;
- struct pchf_data *result;
- size_t result_size;
- struct pchf_adder_info pai;
-
- count = htab_elements (pfile->file_hash);
- result_size = (sizeof (struct pchf_data)
- + sizeof (struct pchf_entry) * (count - 1));
- result = xcalloc (result_size, 1);
-
- result->count = 0;
- result->have_once_only = false;
-
- pai.pfile = pfile;
- pai.d = result;
- htab_traverse (pfile->file_hash, pchf_adder, &pai);
result_size = (sizeof (struct pchf_data)
+ sizeof (struct pchf_entry) * (result->count - 1));
qsort (result->entries, result->count, sizeof (struct pchf_entry),
pchf_save_compare);
- return fwrite (result, result_size, 1, f) == 1;
+ return fwrite (result, result_size, 1, fp) == 1;
}
/* Read the pchf_data structure from F. */
!= 1)
return false;
- pchf = xmalloc (sizeof (struct pchf_data)
+ pchf = XNEWVAR (struct pchf_data, sizeof (struct pchf_data)
+ sizeof (struct pchf_entry) * (d.count - 1));
memcpy (pchf, &d, sizeof (struct pchf_data) - sizeof (struct pchf_entry));
if (fread (pchf->entries, sizeof (struct pchf_entry), d.count, f)