diff options
Diffstat (limited to 'gcc/cppfiles.c')
-rwxr-xr-x | gcc/cppfiles.c | 1065 |
1 files changed, 0 insertions, 1065 deletions
diff --git a/gcc/cppfiles.c b/gcc/cppfiles.c deleted file mode 100755 index 6f190ef..0000000 --- a/gcc/cppfiles.c +++ /dev/null @@ -1,1065 +0,0 @@ -/* Part of CPP library. (include file handling) - Copyright (C) 1986, 87, 89, 92 - 95, 98, 1999 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 - Split out of cpplib.c, Zack Weinberg, Oct 1998 - -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 -Free Software Foundation; either version 2, or (at your option) any -later version. - -This program is distributed in the hope that it will be useful, -but WITHOUT ANY WARRANTY; without even the implied warranty of -MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -GNU General Public License for more details. - -You should have received a copy of the GNU General Public License -along with this program; if not, write to the Free Software -Foundation, 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. - - In other words, you are welcome to use, share and improve this program. - You are forbidden to forbid anyone else to use, share and improve - what you give them. Help stamp out software-hoarding! */ - -#include "config.h" -#include "system.h" -#include "cpplib.h" - -/* The entry points to this file are: find_include_file, finclude, - include_hash, append_include_chain, deps_output, and file_cleanup. - file_cleanup is only called through CPP_BUFFER(pfile)->cleanup, - so it's static anyway. */ - -/* CYGNUS LOCAL - obscured headers */ -static int open_include_file_name (cpp_reader*, char *); -/* END CYGNUS LOCAL - obscured headers */ -static struct include_hash *redundant_include_p - (cpp_reader *, - struct include_hash *, - struct file_name_list *); -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 file_name_list *); -static long safe_read (int, char *, int); -static void simplify_pathname (char *); -static struct file_name_list *actual_directory (cpp_reader *, char *); - -/* Windows does not natively support inodes, and neither does MSDOS. */ -#if (defined _WIN32 && !defined CYGWIN) || defined __MSDOS__ -#define INO_T_EQ(a, b) 0 -#else -#define INO_T_EQ(a, b) ((a) == (b)) -#endif - -/* Append an entry for dir DIR to list LIST, simplifying it if - possible. SYS says whether this is a system include directory. - *** DIR is modified in place. It must be writable and permanently - allocated. LIST is a pointer to the head pointer, because we actually - *prepend* the dir, and reverse the list later (in merge_include_chains). */ -void -append_include_chain (pfile, list, dir, sysp) - cpp_reader *pfile; - struct file_name_list **list; - const char *dir; - int sysp; -{ - struct file_name_list *new; - struct stat st; - unsigned int len; - char * newdir = xstrdup (dir); - - simplify_pathname (newdir); - if (stat (newdir, &st)) - { - /* Dirs that don't exist are silently ignored. */ - if (errno != ENOENT) - cpp_perror_with_name (pfile, newdir); - return; - } - - if (!S_ISDIR (st.st_mode)) - { - cpp_message (pfile, 1, "%s: %s: Not a directory", progname, newdir); - return; - } - - len = strlen(newdir); - if (len > pfile->max_include_len) - pfile->max_include_len = len; - - new = (struct file_name_list *)xmalloc (sizeof (struct file_name_list)); - new->name = newdir; - new->nlen = len; - new->next = *list; - new->ino = st.st_ino; - new->dev = st.st_dev; - new->sysp = sysp; - new->name_map = NULL; - - *list = new; -} - -/* Merge the four include chains together in the order quote, bracket, - system, after. Remove duplicate dirs (as determined by - INO_T_EQ()). The system_include and after_include chains are never - referred to again after this function; all access is through the - bracket_include path. - - For the future: Check if the directory is empty (but - how?) and possibly preload the include hash. */ - -void -merge_include_chains (opts) - struct cpp_options *opts; -{ - struct file_name_list *prev, *next, *cur, *other; - struct file_name_list *quote, *brack, *systm, *after; - struct file_name_list *qtail, *btail, *stail, *atail; - - qtail = opts->quote_include; - btail = opts->bracket_include; - stail = opts->system_include; - atail = opts->after_include; - - /* Nreverse the four lists. */ - prev = 0; - for (cur = qtail; cur; cur = next) - { - next = cur->next; - cur->next = prev; - prev = cur; - } - quote = prev; - - prev = 0; - for (cur = btail; cur; cur = next) - { - next = cur->next; - cur->next = prev; - prev = cur; - } - brack = prev; - - prev = 0; - for (cur = stail; cur; cur = next) - { - next = cur->next; - cur->next = prev; - prev = cur; - } - systm = prev; - - prev = 0; - for (cur = atail; cur; cur = next) - { - next = cur->next; - cur->next = prev; - prev = cur; - } - after = prev; - - /* Paste together bracket, system, and after include chains. */ - if (stail) - stail->next = after; - else - systm = after; - if (btail) - btail->next = systm; - else - brack = systm; - - /* This is a bit tricky. - First we drop dupes from the quote-include list. - Then we drop dupes from the bracket-include list. - Finally, if qtail and brack are the same directory, - we cut out qtail. - - We can't just merge the lists and then uniquify them because - then we may lose directories from the <> search path that should - be there; consider -Ifoo -Ibar -I- -Ifoo -Iquux. It is however - safe to treat -Ibar -Ifoo -I- -Ifoo -Iquux as if written - -Ibar -I- -Ifoo -Iquux. */ - - for (cur = quote; cur; cur = cur->next) - { - for (other = quote; other != cur; other = other->next) - if (INO_T_EQ (cur->ino, other->ino) - && cur->dev == other->dev) - { - prev->next = cur->next; - free (cur->name); - free (cur); - cur = prev; - break; - } - prev = cur; - } - qtail = prev; - - for (cur = brack; cur; cur = cur->next) - { - for (other = brack; other != cur; other = other->next) - if (INO_T_EQ (cur->ino, other->ino) - && cur->dev == other->dev) - { - prev->next = cur->next; - free (cur->name); - free (cur); - cur = prev; - break; - } - prev = cur; - } - - if (quote) - { - if (INO_T_EQ (qtail->ino, brack->ino) && qtail->dev == brack->dev) - { - if (quote == qtail) - { - free (quote->name); - free (quote); - quote = brack; - } - else - { - cur = quote; - while (cur->next != qtail) - cur = cur->next; - cur->next = brack; - free (qtail->name); - free (qtail); - } - } - else - qtail->next = brack; - } - else - quote = brack; - - opts->quote_include = quote; - opts->bracket_include = brack; - opts->system_include = NULL; - opts->after_include = NULL; -} - -/* Look up or add an entry to the table of all includes. This table - is indexed by the name as it appears in the #include line. The - ->next_this_file chain stores all different files with the same - #include name (there are at least three ways this can happen). The - hash function could probably be improved a bit. */ - -struct include_hash * -include_hash (pfile, fname, add) - cpp_reader *pfile; - char *fname; - int add; -{ - unsigned int hash = 0; - struct include_hash *l, *m; - char *f = fname; - - while (*f) - hash += *f++; - - l = pfile->all_include_files[hash % ALL_INCLUDE_HASHSIZE]; - m = 0; - for (; l; m = l, l = l->next) - if (!strcmp (l->nshort, fname)) - return l; - - if (!add) - return 0; - - l = (struct include_hash *) xmalloc (sizeof (struct include_hash)); - l->next = NULL; - l->next_this_file = NULL; - l->foundhere = NULL; - l->buf = NULL; - l->limit = NULL; - if (m) - m->next = l; - else - pfile->all_include_files[hash % ALL_INCLUDE_HASHSIZE] = l; - - return l; -} - -/* Return 0 if the file pointed to by IHASH has never been included before, - -1 if it has been included before and need not be again, - or a pointer to an IHASH entry which is the file to be reread. - "Never before" is with respect to the position in ILIST. - - This will not detect redundancies involving odd uses of the - `current directory' rule for "" includes. They aren't quite - pathological, but I think they are rare enough not to worry about. - The simplest example is: - - top.c: - #include "a/a.h" - #include "b/b.h" - - a/a.h: - #include "../b/b.h" - - and the problem is that for `current directory' includes, - ihash->foundhere is not on any of the global include chains, - so the test below (i->foundhere == l) may be false even when - the directories are in fact the same. */ - -static struct include_hash * -redundant_include_p (pfile, ihash, ilist) - cpp_reader *pfile; - struct include_hash *ihash; - struct file_name_list *ilist; -{ - struct file_name_list *l; - struct include_hash *i; - - if (! ihash->foundhere) - return 0; - - for (i = ihash; i; i = i->next_this_file) - for (l = ilist; l; l = l->next) - if (i->foundhere == l) - /* The control_macro works like this: If it's NULL, the file - is to be included again. If it's "", the file is never to - be included again. If it's a string, the file is not to be - included again if the string is the name of a defined macro. */ - return (i->control_macro - && (i->control_macro[0] == '\0' - || cpp_lookup (pfile, i->control_macro, -1, -1))) - ? (struct include_hash *)-1 : i; - - return 0; -} - -static int -file_cleanup (pbuf, pfile) - cpp_buffer *pbuf; - cpp_reader *pfile; -{ - if (pbuf->buf) - { - free (pbuf->buf); - pbuf->buf = 0; - } - if (pfile->system_include_depth) - pfile->system_include_depth--; - return 0; -} - -/* Search for include file FNAME in the include chain starting at - SEARCH_START. Return -2 if this file doesn't need to be included - (because it was included already and it's marked idempotent), - -1 if an error occurred, or a file descriptor open on the file. - *IHASH is set to point to the include hash entry for this file, and - *BEFORE is 1 if the file was included before (but needs to be read - again). */ -int -find_include_file (pfile, fname, search_start, ihash, before) - cpp_reader *pfile; - char *fname; - struct file_name_list *search_start; - struct include_hash **ihash; - int *before; -{ - struct file_name_list *l; - struct include_hash *ih, *jh; - int f, len; - char *name; - - ih = include_hash (pfile, fname, 1); - jh = redundant_include_p (pfile, ih, - fname[0] == '/' ? ABSOLUTE_PATH : search_start); - - if (jh != 0) - { - *before = 1; - *ihash = jh; - - if (jh == (struct include_hash *)-1) - return -2; - else - { - /* CYGNUS LOCAL - obscured headers */ - return open_include_file_name (pfile, jh->name); - /* END CYGNUS LOCAL - obscured headers */ - } - } - - if (ih->foundhere) - /* A file is already known by this name, but it's not the same file. - Allocate another include_hash block and add it to the next_this_file - chain. */ - { - jh = (struct include_hash *)xmalloc (sizeof (struct include_hash)); - while (ih->next_this_file) ih = ih->next_this_file; - - ih->next_this_file = jh; - jh = ih; - ih = ih->next_this_file; - - ih->next = NULL; - ih->next_this_file = NULL; - ih->buf = NULL; - ih->limit = NULL; - } - *before = 0; - *ihash = ih; - ih->nshort = xstrdup (fname); - ih->control_macro = NULL; - - /* If the pathname is absolute, just open it. */ - if (fname[0] == '/') - { - ih->foundhere = ABSOLUTE_PATH; - ih->name = ih->nshort; - /* CYGNUS LOCAL - obscured headers */ - return open_include_file_name (pfile, ih->name); - /* END CYGNUS LOCAL - obscured headers */ - } - - /* Search directory path, trying to open the file. */ - - len = strlen (fname); - name = xmalloc (len + pfile->max_include_len + 2 + INCLUDE_LEN_FUDGE); - - for (l = search_start; l; l = l->next) - { - copy_memory (l->name, name, l->nlen); - name[l->nlen] = '/'; - strcpy (&name[l->nlen+1], fname); - simplify_pathname (name); - if (CPP_OPTIONS (pfile)->remap) - name = remap_filename (pfile, name, l); - - /* CYGNUS LOCAL - obscured headers */ - f = open_include_file_name (pfile, name); - /* END CYGNUS LOCAL - obscured headers */ -#ifdef EACCES - if (f == -1 && errno == EACCES) - { - cpp_error(pfile, "included file `%s' exists but is not readable", - name); - return -1; - } -#endif - - if (f >= 0) - { - ih->foundhere = l; - ih->name = xrealloc (name, strlen (name)+1); - return f; - } - } - - if (jh) - { - jh->next_this_file = NULL; - free (ih); - } - free (name); - *ihash = (struct include_hash *)-1; - return -1; -} - -/* The file_name_map structure holds a mapping of file names for a - particular directory. This mapping is read from the file named - FILE_NAME_MAP_FILE in that directory. Such a file can be used to - map filenames on a file system with severe filename restrictions, - such as DOS. The format of the file name map file is just a series - of lines with two tokens on each line. The first token is the name - to map, and the second token is the actual name to use. */ - -struct file_name_map -{ - struct file_name_map *map_next; - char *map_from; - char *map_to; -}; - -#define FILE_NAME_MAP_FILE "header.gcc" - -/* Read a space delimited string of unlimited length from a stdio - file. */ - -static char * -read_filename_string (ch, f) - int ch; - FILE *f; -{ - char *alloc, *set; - int len; - - len = 20; - set = alloc = xmalloc (len + 1); - if (! is_space[ch]) - { - *set++ = ch; - while ((ch = getc (f)) != EOF && ! is_space[ch]) - { - if (set - alloc == len) - { - len *= 2; - alloc = xrealloc (alloc, len + 1); - set = alloc + len / 2; - } - *set++ = ch; - } - } - *set = '\0'; - ungetc (ch, f); - return alloc; -} - -/* This structure holds a linked list of file name maps, one per directory. */ - -struct file_name_map_list -{ - struct file_name_map_list *map_list_next; - char *map_list_name; - struct file_name_map *map_list_map; -}; - -/* Read the file name map file for DIRNAME. */ - -static struct file_name_map * -read_name_map (pfile, dirname) - cpp_reader *pfile; - const char *dirname; -{ - register struct file_name_map_list *map_list_ptr; - char *name; - FILE *f; - - for (map_list_ptr = CPP_OPTIONS (pfile)->map_list; map_list_ptr; - map_list_ptr = map_list_ptr->map_list_next) - if (! strcmp (map_list_ptr->map_list_name, dirname)) - return map_list_ptr->map_list_map; - - map_list_ptr = ((struct file_name_map_list *) - xmalloc (sizeof (struct file_name_map_list))); - map_list_ptr->map_list_name = xstrdup (dirname); - - name = (char *) alloca (strlen (dirname) + strlen (FILE_NAME_MAP_FILE) + 2); - strcpy (name, dirname); - if (*dirname) - strcat (name, "/"); - strcat (name, FILE_NAME_MAP_FILE); - f = fopen (name, "r"); - if (!f) - map_list_ptr->map_list_map = (struct file_name_map *)-1; - else - { - int ch; - int dirlen = strlen (dirname); - - while ((ch = getc (f)) != EOF) - { - char *from, *to; - struct file_name_map *ptr; - - if (is_space[ch]) - continue; - from = read_filename_string (ch, f); - while ((ch = getc (f)) != EOF && is_hor_space[ch]) - ; - to = read_filename_string (ch, f); - - ptr = ((struct file_name_map *) - xmalloc (sizeof (struct file_name_map))); - ptr->map_from = from; - - /* Make the real filename absolute. */ - if (*to == '/') - ptr->map_to = to; - else - { - ptr->map_to = xmalloc (dirlen + strlen (to) + 2); - strcpy (ptr->map_to, dirname); - ptr->map_to[dirlen] = '/'; - strcpy (ptr->map_to + dirlen + 1, to); - free (to); - } - - ptr->map_next = map_list_ptr->map_list_map; - map_list_ptr->map_list_map = ptr; - - while ((ch = getc (f)) != '\n') - if (ch == EOF) - break; - } - fclose (f); - } - - map_list_ptr->map_list_next = CPP_OPTIONS (pfile)->map_list; - CPP_OPTIONS (pfile)->map_list = map_list_ptr; - - return map_list_ptr->map_list_map; -} - -/* Remap NAME based on the file_name_map (if any) for LOC. */ - -static char * -remap_filename (pfile, name, loc) - cpp_reader *pfile; - char *name; - struct file_name_list *loc; -{ - struct file_name_map *map; - const char *from, *p, *dir; - - if (! loc->name_map) - loc->name_map = read_name_map (pfile, - loc->name - ? loc->name : "."); - - if (loc->name_map == (struct file_name_map *)-1) - return name; - - from = name + strlen (loc->name) + 1; - - for (map = loc->name_map; map; map = map->map_next) - if (!strcmp (map->map_from, from)) - return map->map_to; - - /* Try to find a mapping file for the particular directory we are - looking in. Thus #include <sys/types.h> will look up sys/types.h - in /usr/include/header.gcc and look up types.h in - /usr/include/sys/header.gcc. */ - p = strrchr (name, '/'); - if (!p) - p = name; - if (loc && loc->name - && strlen (loc->name) == (size_t) (p - name) - && !strncmp (loc->name, name, p - name)) - /* FILENAME is in SEARCHPTR, which we've already checked. */ - return name; - - if (p == name) - { - dir = "."; - from = name; - } - else - { - char * newdir = (char *) alloca (p - name + 1); - copy_memory (name, newdir, p - name); - newdir[p - name] = '\0'; - dir = newdir; - from = p + 1; - } - - for (map = read_name_map (pfile, dir); map; map = map->map_next) - if (! strcmp (map->map_from, name)) - return map->map_to; - - return name; -} - -/* CYGNUS LOCAL - obscured headers */ -static int -open_include_file_name (pfile, filename) - cpp_reader *pfile; - char *filename; -{ - return open (filename, O_RDONLY, 0666); -} -/* END CYGNUS LOCAL - obscured headers */ - -/* Read the contents of FD into the buffer on the top of PFILE's stack. - IHASH points to the include hash entry for the file associated with - FD. - - The caller is responsible for the cpp_push_buffer. */ - -int -finclude (pfile, fd, ihash) - cpp_reader *pfile; - int fd; - struct include_hash *ihash; -{ - struct stat st; - size_t st_size; - long i, length; - cpp_buffer *fp; -#if 0 - int missing_newline = 0; -#endif - - if (fstat (fd, &st) < 0) - goto perror_fail; - - fp = CPP_BUFFER (pfile); - fp->nominal_fname = fp->fname = ihash->name; - fp->ihash = ihash; - fp->system_header_p = (ihash->foundhere != ABSOLUTE_PATH - && ihash->foundhere->sysp); - fp->lineno = 1; - fp->colno = 1; - fp->cleanup = file_cleanup; - - /* The ->actual_dir field is only used when ignore_srcdir is not in effect; - see do_include */ - if (!CPP_OPTIONS (pfile)->ignore_srcdir) - fp->actual_dir = actual_directory (pfile, fp->fname); - - if (S_ISREG (st.st_mode)) - { - st_size = (size_t) st.st_size; - if (st_size != st.st_size || st_size + 2 < st_size) - { - cpp_error (pfile, "file `%s' too large", ihash->name); - goto fail; - } - fp->buf = (U_CHAR *) xmalloc (st_size + 2); - fp->alimit = fp->buf + st_size + 2; - fp->cur = fp->buf; - - /* Read the file contents, knowing that st_size is an upper bound - on the number of bytes we can read. */ - length = safe_read (fd, fp->buf, st_size); - fp->rlimit = fp->buf + length; - if (length < 0) - goto perror_fail; - } - else if (S_ISDIR (st.st_mode)) - { - cpp_pop_buffer (pfile); - cpp_error (pfile, "directory `%s' specified in #include", ihash->name); - goto fail; - } - else - { - /* Cannot count its file size before reading. - First read the entire file into heap and - copy them into buffer on stack. */ - - size_t bsize = 2000; - - st_size = 0; - fp->buf = (U_CHAR *) xmalloc (bsize + 2); - - for (;;) - { - i = safe_read (fd, fp->buf + st_size, bsize - st_size); - if (i < 0) - goto perror_fail; - st_size += i; - if (st_size != bsize) - break; /* End of file */ - bsize *= 2; - fp->buf = (U_CHAR *) xrealloc (fp->buf, bsize + 2); - } - fp->cur = fp->buf; - length = st_size; - } - - /* FIXME: Broken in presence of trigraphs (consider ??/<EOF>) - and doesn't warn about a missing newline. */ - if ((length > 0 && fp->buf[length - 1] != '\n') - || (length > 1 && fp->buf[length - 2] == '\\')) - fp->buf[length++] = '\n'; - - fp->buf[length] = '\0'; - fp->rlimit = fp->buf + length; - - close (fd); - pfile->input_stack_listing_current = 0; - -#if 0 - if (!no_trigraphs) - trigraph_pcp (fp); -#endif - return 1; - - perror_fail: - cpp_pop_buffer (pfile); - cpp_error_from_errno (pfile, ihash->name); - fail: - close (fd); - return 0; -} - -static struct file_name_list * -actual_directory (pfile, fname) - cpp_reader *pfile; - char *fname; -{ - char *last_slash, *dir; - size_t dlen; - struct file_name_list *x; - - dir = xstrdup (fname); - last_slash = strrchr (dir, '/'); - if (last_slash) - { - if (last_slash == dir) - { - dlen = 1; - last_slash[1] = '\0'; - } - else - { - dlen = last_slash - dir; - *last_slash = '\0'; - } - } - else - { - dir[0] = '.'; - dir[1] = '\0'; - dlen = 1; - } - - if (dlen > pfile->max_include_len) - pfile->max_include_len = dlen; - - for (x = pfile->actual_dirs; x; x = x->alloc) - if (!strcmp (x->name, dir)) - { - free (dir); - return x; - } - - /* Not found, make a new one. */ - x = (struct file_name_list *) xmalloc (sizeof (struct file_name_list)); - x->name = dir; - x->nlen = dlen; - x->next = CPP_OPTIONS (pfile)->quote_include; - x->alloc = pfile->actual_dirs; - x->sysp = 0; - x->name_map = NULL; - - pfile->actual_dirs = x; - return x; -} - -/* Read LEN bytes at PTR from descriptor DESC, for file FILENAME, - retrying if necessary. If MAX_READ_LEN is defined, read at most - that bytes at a time. Return a negative value if an error occurs, - otherwise return the actual number of bytes read, - which must be LEN unless end-of-file was reached. */ - -static long -safe_read (desc, ptr, len) - int desc; - char *ptr; - int len; -{ - int left, rcount, nchars; - - left = len; - while (left > 0) { - rcount = left; -#ifdef MAX_READ_LEN - if (rcount > MAX_READ_LEN) - rcount = MAX_READ_LEN; -#endif - nchars = read (desc, ptr, rcount); - if (nchars < 0) - { -#ifdef EINTR - if (errno == EINTR) - continue; -#endif - return nchars; - } - if (nchars == 0) - break; - ptr += nchars; - left -= nchars; - } - return len - left; -} - -/* Add output to `deps_buffer' for the -M switch. - STRING points to the text to be output. - SPACER is ':' for targets, ' ' for dependencies, zero for text - to be inserted literally. */ - -void -deps_output (pfile, string, spacer) - cpp_reader *pfile; - char *string; - int spacer; -{ - int size; - int cr = 0; - - if (!*string) - return; - - size = strlen (string); - -#ifndef MAX_OUTPUT_COLUMNS -#define MAX_OUTPUT_COLUMNS 72 -#endif - if (pfile->deps_column > 0 - && (pfile->deps_column + size) > MAX_OUTPUT_COLUMNS) - { - size += 5; - cr = 1; - pfile->deps_column = 0; - } - - if (pfile->deps_size + size + 8 > pfile->deps_allocated_size) - { - pfile->deps_allocated_size = (pfile->deps_size + size + 50) * 2; - pfile->deps_buffer = (char *) xrealloc (pfile->deps_buffer, - pfile->deps_allocated_size); - } - - if (cr) - { - copy_memory (" \\\n ", &pfile->deps_buffer[pfile->deps_size], 5); - pfile->deps_size += 5; - } - - if (spacer == ' ' && pfile->deps_column > 0) - pfile->deps_buffer[pfile->deps_size++] = ' '; - copy_memory (string, &pfile->deps_buffer[pfile->deps_size], size); - pfile->deps_size += size; - pfile->deps_column += size; - if (spacer == ':') - pfile->deps_buffer[pfile->deps_size++] = ':'; - pfile->deps_buffer[pfile->deps_size] = 0; -} - -/* Simplify a path name in place, deleting redundant components. This - reduces OS overhead and guarantees that equivalent paths compare - the same (modulo symlinks). - - Transforms made: - foo/bar/../quux foo/quux - foo/./bar foo/bar - foo//bar foo/bar - /../quux /quux - //quux //quux (POSIX allows leading // as a namespace escape) - - Guarantees no trailing slashes. All transforms reduce the length - of the string. - */ -static void -simplify_pathname (path) - char *path; -{ - char *from, *to; - char *base; - int absolute = 0; - -#if defined _WIN32 || defined __MSDOS__ - /* Convert all backslashes to slashes. */ - for (from = path; *from; from++) - if (*from == '\\') *from = '/'; - - /* Skip over leading drive letter if present. */ - if (ISALPHA (path[0]) && path[1] == ':') - from = to = &path[2]; - else - from = to = path; -#else - from = to = path; -#endif - - /* Remove redundant initial /s. */ - if (*from == '/') - { - absolute = 1; - to++; - from++; - if (*from == '/') - { - if (*++from == '/') - /* 3 or more initial /s are equivalent to 1 /. */ - while (*++from == '/'); - else - /* On some hosts // differs from /; Posix allows this. */ - to++; - } - } - base = to; - - for (;;) - { - while (*from == '/') - from++; - - if (from[0] == '.' && from[1] == '/') - from += 2; - else if (from[0] == '.' && from[1] == '\0') - goto done; - else if (from[0] == '.' && from[1] == '.' && from[2] == '/') - { - if (base == to) - { - if (absolute) - from += 3; - else - { - *to++ = *from++; - *to++ = *from++; - *to++ = *from++; - base = to; - } - } - else - { - to -= 2; - while (to > base && *to != '/') to--; - if (*to == '/') - to++; - from += 3; - } - } - else if (from[0] == '.' && from[1] == '.' && from[2] == '\0') - { - if (base == to) - { - if (!absolute) - { - *to++ = *from++; - *to++ = *from++; - } - } - else - { - to -= 2; - while (to > base && *to != '/') to--; - if (*to == '/') - to++; - } - goto done; - } - else - /* Copy this component and trailing /, if any. */ - while ((*to++ = *from++) != '/') - { - if (!to[-1]) - { - to--; - goto done; - } - } - - } - - done: - /* Trim trailing slash */ - if (to[0] == '/' && (!absolute || to > path+1)) - to--; - - /* Change the empty string to "." so that stat() on the result - will always work. */ - if (to == path) - *to++ = '.'; - - *to = '\0'; - - return; -} |