home *** CD-ROM | disk | FTP | other *** search
- /* Implementation of pattern-matching file search paths for GNU Make.
- Copyright (C) 1988, 1989, 1991, 1992, 1993, 1994 Free Software Foundation, Inc.
- This file is part of GNU Make.
-
- GNU Make 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.
-
- GNU Make 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 GNU Make; see the file COPYING. If not, write to
- the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */
-
- #include "make.h"
- #include "file.h"
- #include "variable.h"
-
-
- /* Structure used to represent a selective VPATH searchpath. */
-
- struct vpath
- {
- struct vpath *next; /* Pointer to next struct in the linked list. */
- char *pattern; /* The pattern to match. */
- char *percent; /* Pointer into `pattern' where the `%' is. */
- unsigned int patlen;/* Length of the pattern. */
- char **searchpath; /* Null-terminated list of directories. */
- unsigned int maxlen;/* Maximum length of any entry in the list. */
- };
-
- /* Linked-list of all selective VPATHs. */
-
- static struct vpath *vpaths;
-
- /* Structure for the general VPATH given in the variable. */
-
- static struct vpath *general_vpath;
-
- static int selective_vpath_search ();
-
- /* Reverse the chain of selective VPATH lists so they
- will be searched in the order given in the makefiles
- and construct the list from the VPATH variable. */
-
- void
- build_vpath_lists ()
- {
- register struct vpath *new = 0;
- register struct vpath *old, *nexto;
- register char *p;
-
- /* Reverse the chain. */
- for (old = vpaths; old != 0; old = nexto)
- {
- nexto = old->next;
- old->next = new;
- new = old;
- }
-
- vpaths = new;
-
- /* If there is a VPATH variable with a nonnull value, construct the
- general VPATH list from it. We use variable_expand rather than just
- calling lookup_variable so that it will be recursively expanded. */
-
- {
- /* Turn off --warn-undefined-variables while we expand SHELL and IFS. */
- int save = warn_undefined_variables_flag;
- warn_undefined_variables_flag = 0;
-
- p = variable_expand ("$(strip $(VPATH))");
-
- warn_undefined_variables_flag = save;
- }
-
- if (*p != '\0')
- {
- /* Save the list of vpaths. */
- struct vpath *save_vpaths = vpaths;
-
- /* Empty `vpaths' so the new one will have no next, and `vpaths'
- will still be nil if P contains no existing directories. */
- vpaths = 0;
-
- /* Parse P. */
- construct_vpath_list ("%", p);
-
- /* Store the created path as the general path,
- and restore the old list of vpaths. */
- general_vpath = vpaths;
- vpaths = save_vpaths;
- }
- }
-
- /* Construct the VPATH listing for the pattern and searchpath given.
-
- This function is called to generate selective VPATH lists and also for
- the general VPATH list (which is in fact just a selective VPATH that
- is applied to everything). The returned pointer is either put in the
- linked list of all selective VPATH lists or in the GENERAL_VPATH
- variable.
-
- If SEARCHPATH is nil, remove all previous listings with the same
- pattern. If PATTERN is nil, remove all VPATH listings.
- Existing and readable directories that are not "." given in the
- searchpath separated by colons are loaded into the directory hash
- table if they are not there already and put in the VPATH searchpath
- for the given pattern with trailing slashes stripped off if present
- (and if the directory is not the root, "/").
- The length of the longest entry in the list is put in the structure as well.
- The new entry will be at the head of the VPATHS chain. */
-
- void
- construct_vpath_list (pattern, dirpath)
- char *pattern, *dirpath;
- {
- register unsigned int elem;
- register char *p;
- register char **vpath;
- register unsigned int maxvpath;
- unsigned int maxelem;
- char *percent;
-
- if (pattern != 0)
- {
- pattern = savestring (pattern, strlen (pattern));
- percent = find_percent (pattern);
- }
-
- if (dirpath == 0)
- {
- /* Remove matching listings. */
- register struct vpath *path, *lastpath;
-
- lastpath = 0;
- path = vpaths;
- while (path != 0)
- {
- struct vpath *next = path->next;
-
- if (pattern == 0
- || (((percent == 0 && path->percent == 0)
- || (percent - pattern == path->percent - path->pattern))
- && streq (pattern, path->pattern)))
- {
- /* Remove it from the linked list. */
- if (lastpath == 0)
- vpaths = path->next;
- else
- lastpath->next = next;
-
- /* Free its unused storage. */
- free (path->pattern);
- free ((char *) path->searchpath);
- free ((char *) path);
- }
- else
- lastpath = path;
-
- path = next;
- }
-
- if (pattern != 0)
- free (pattern);
- return;
- }
-
- /* Figure out the maximum number of VPATH entries and
- put it in MAXELEM. We start with 2, one before the
- first colon and one nil, the list terminator and
- increment our estimated number for each colon or blank we find. */
- maxelem = 2;
- p = dirpath;
- while (*p != '\0')
- if (*p++ == PATH_SEPARATOR_CHAR || isblank (*p))
- ++maxelem;
-
- vpath = (char **) xmalloc (maxelem * sizeof (char *));
- maxvpath = 0;
-
- /* Skip over any initial colons and blanks. */
- p = dirpath;
- while (*p == PATH_SEPARATOR_CHAR || isblank (*p))
- ++p;
-
- elem = 0;
- while (*p != '\0')
- {
- char *v;
- unsigned int len;
-
- /* Find the end of this entry. */
- v = p;
- #if defined (__MSDOS__) || defined (WIN32)
- /* treat x:\ as a drive, not a single-character path */
- while (*p != '\0'
- && (*p != PATH_SEPARATOR_CHAR
- || ((*(p+1) == '/' || *(p+1) == '\\') && p-v == 1))
- && !isblank (*p))
- #else
- while (*p != '\0' && *p != PATH_SEPARATOR_CHAR && !isblank (*p))
- #endif
- ++p;
-
- len = p - v;
- /* Make sure there's no trailing slash,
- but still allow "/" as a directory. */
- if (len > 1 && p[-1] == '/')
- --len;
-
- if (len > 1 || *v != '.')
- {
- v = savestring (v, len);
-
- /* Verify that the directory actually exists. */
-
- if (dir_file_exists_p (v, ""))
- {
- /* It does. Put it in the list. */
- vpath[elem++] = dir_name (v);
- free (v);
- if (len > maxvpath)
- maxvpath = len;
- }
- else
- /* The directory does not exist. Omit from the list. */
- free (v);
- }
-
- /* Skip over colons and blanks between entries. */
- while (*p == PATH_SEPARATOR_CHAR || isblank (*p))
- ++p;
- }
-
- if (elem > 0)
- {
- struct vpath *path;
- /* ELEM is now incremented one element past the last
- entry, to where the nil-pointer terminator goes.
- Usually this is maxelem - 1. If not, shrink down. */
- if (elem < (maxelem - 1))
- vpath = (char **) xrealloc ((char *) vpath,
- (elem + 1) * sizeof (char *));
-
- /* Put the nil-pointer terminator on the end of the VPATH list. */
- vpath[elem] = 0;
-
- /* Construct the vpath structure and put it into the linked list. */
- path = (struct vpath *) xmalloc (sizeof (struct vpath));
- path->searchpath = vpath;
- path->maxlen = maxvpath;
- path->next = vpaths;
- vpaths = path;
-
- /* Set up the members. */
- path->pattern = pattern;
- path->percent = percent;
- path->patlen = strlen (pattern);
- }
- else
- {
- /* There were no entries, so free whatever space we allocated. */
- free ((char *) vpath);
- if (pattern != 0)
- free (pattern);
- }
- }
-
- /* Search the VPATH list whose pattern matches *FILE for a directory
- where the name pointed to by FILE exists. If it is found, we set *FILE to
- the newly malloc'd name of the existing file, *MTIME_PTR (if MTIME_PTR is
- not NULL) to its modtime (or zero if no stat call was done), and return 1.
- Otherwise we return 0. */
-
- int
- vpath_search (file, mtime_ptr)
- char **file;
- time_t *mtime_ptr;
- {
- register struct vpath *v;
-
- /* If there are no VPATH entries or FILENAME starts at the root,
- there is nothing we can do. */
-
- if (**file == '/' || (vpaths == 0 && general_vpath == 0))
- return 0;
-
- for (v = vpaths; v != 0; v = v->next)
- if (pattern_matches (v->pattern, v->percent, *file))
- if (selective_vpath_search (v, file, mtime_ptr))
- return 1;
-
- if (general_vpath != 0
- && selective_vpath_search (general_vpath, file, mtime_ptr))
- return 1;
-
- return 0;
- }
-
-
- /* Search the given VPATH list for a directory where the name pointed
- to by FILE exists. If it is found, we set *FILE to the newly malloc'd
- name of the existing file, *MTIME_PTR (if MTIME_PTR is not NULL) to
- its modtime (or zero if no stat call was done), and we return 1.
- Otherwise we return 0. */
-
- static int
- selective_vpath_search (path, file, mtime_ptr)
- struct vpath *path;
- char **file;
- time_t *mtime_ptr;
- {
- int not_target;
- char *name, *n;
- char *filename;
- register char **vpath = path->searchpath;
- unsigned int maxvpath = path->maxlen;
- register unsigned int i;
- unsigned int flen, vlen, name_dplen;
- int exists = 0;
-
- /* Find out if *FILE is a target.
- If and only if it is NOT a target, we will accept prospective
- files that don't exist but are mentioned in a makefile. */
- {
- struct file *f = lookup_file (*file);
- not_target = f == 0 || !f->is_target;
- }
-
- flen = strlen (*file);
-
- /* Split *FILE into a directory prefix and a name-within-directory.
- NAME_DPLEN gets the length of the prefix; FILENAME gets the
- pointer to the name-within-directory and FLEN is its length. */
-
- n = rindex (*file, '/');
- name_dplen = n != 0 ? n - *file : 0;
- filename = name_dplen > 0 ? n + 1 : *file;
- if (name_dplen > 0)
- flen -= name_dplen + 1;
-
- /* Allocate enough space for the biggest VPATH entry,
- a slash, the directory prefix that came with *FILE,
- another slash (although this one may not always be
- necessary), the filename, and a null terminator. */
- name = (char *) alloca (maxvpath + 1 + name_dplen + 1 + flen + 1);
-
- /* Try each VPATH entry. */
- for (i = 0; vpath[i] != 0; ++i)
- {
- int exists_in_cache = 0;
-
- n = name;
-
- /* Put the next VPATH entry into NAME at N and increment N past it. */
- vlen = strlen (vpath[i]);
- bcopy (vpath[i], n, vlen);
- n += vlen;
-
- /* Add the directory prefix already in *FILE. */
- if (name_dplen > 0)
- {
- *n++ = '/';
- bcopy (*file, n, name_dplen);
- n += name_dplen;
- }
-
- /* Now add the name-within-directory at the end of NAME. */
- if (n != name && n[-1] != '/')
- {
- *n = '/';
- bcopy (filename, n + 1, flen + 1);
- }
- else
- bcopy (filename, n, flen + 1);
-
- /* Check if the file is mentioned in a makefile. If *FILE is not
- a target, that is enough for us to decide this file exists.
- If *FILE is a target, then the file must be mentioned in the
- makefile also as a target to be chosen.
-
- The restriction that *FILE must not be a target for a
- makefile-mentioned file to be chosen was added by an
- inadequately commented change in July 1990; I am not sure off
- hand what problem it fixes.
-
- In December 1993 I loosened of this restriction to allow a file
- to be chosen if it is mentioned as a target in a makefile. This
- seem logical. */
- {
- struct file *f = lookup_file (name);
- if (f != 0)
- exists = not_target || f->is_target;
- }
-
- if (!exists)
- {
- /* That file wasn't mentioned in the makefile.
- See if it actually exists. */
-
- /* Clobber a null into the name at the last slash.
- Now NAME is the name of the directory to look in. */
- *n = '\0';
-
- /* We know the directory is in the hash table now because either
- construct_vpath_list or the code just above put it there.
- Does the file we seek exist in it? */
- exists_in_cache = exists = dir_file_exists_p (name, filename);
- }
-
- if (exists)
- {
- /* The file is in the directory cache.
- Now check that it actually exists in the filesystem.
- The cache may be out of date. When vpath thinks a file
- exists, but stat fails for it, confusion results in the
- higher levels. */
-
- struct stat st;
-
- /* Put the slash back in NAME. */
- *n = '/';
-
- if (!exists_in_cache /* Makefile-mentioned file need not exist. */
- || safe_stat (name, &st) == 0) /* Does it really exist? */
- {
- /* We have found a file.
- Store the name we found into *FILE for the caller. */
-
- *file = savestring (name, (n + 1 - name) + flen);
-
- if (mtime_ptr != 0)
- /* Store the modtime into *MTIME_PTR for the caller.
- If we have had no need to stat the file here,
- we record a zero modtime to indicate this. */
- *mtime_ptr = exists_in_cache ? st.st_mtime : (time_t) 0;
-
- return 1;
- }
- else
- exists = 0;
- }
- }
-
- return 0;
- }
-
- /* Print the data base of VPATH search paths. */
-
- void
- print_vpath_data_base ()
- {
- register unsigned int nvpaths;
- register struct vpath *v;
-
- puts ("\n# VPATH Search Paths\n");
-
- nvpaths = 0;
- for (v = vpaths; v != 0; v = v->next)
- {
- register unsigned int i;
-
- ++nvpaths;
-
- printf ("vpath %s ", v->pattern);
-
- for (i = 0; v->searchpath[i] != 0; ++i)
- printf ("%s%c", v->searchpath[i],
- v->searchpath[i + 1] == 0 ? '\n' : PATH_SEPARATOR_CHAR);
- }
-
- if (vpaths == 0)
- puts ("# No `vpath' search paths.");
- else
- printf ("\n# %u `vpath' search paths.\n", nvpaths);
-
- if (general_vpath == 0)
- puts ("\n# No general (`VPATH' variable) search path.");
- else
- {
- register char **path = general_vpath->searchpath;
- register unsigned int i;
-
- fputs ("\n# General (`VPATH' variable) search path:\n# ", stdout);
-
- for (i = 0; path[i] != 0; ++i)
- printf ("%s%c", path[i],
- path[i + 1] == 0 ? '\n' : PATH_SEPARATOR_CHAR);
- }
- }
-