diff options
Diffstat (limited to 'vpath.c')
-rw-r--r-- | vpath.c | 417 |
1 files changed, 417 insertions, 0 deletions
@@ -0,0 +1,417 @@ +/* Copyright (C) 1988, 1989, 1991 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. */ + p = variable_expand ("$(VPATH)"); + if (*p != '\0') + { + construct_vpath_list ("%", p); + /* VPATHS will be nil if there have been no previous `vpath' + directives and none of the given directories exists. */ + if (vpaths == 0) + general_vpath = 0; + else + { + general_vpath = vpaths; + /* It was just put into the linked list, + but we don't want it there, so we must remove it. */ + vpaths = general_vpath->next; + } + } +} + +/* 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 = vpaths; + for (path = vpaths; path != 0; lastpath = path, path = 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 == vpaths) + vpaths = path->next; + else + lastpath->next = path->next; + + /* Free its unused storage. */ + free (path->pattern); + free ((char *) path->searchpath); + free ((char *) path); + } + if (pattern != 0) + free (pattern); + return; + } + + /* Skip over any initial colons. */ + p = dirpath; + while (*p == ':') + ++p; + + /* 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 we find. */ + maxelem = 2; + while (*p != '\0') + if (*p++ == ':') + ++maxelem; + + vpath = (char **) xmalloc (maxelem * sizeof (char *)); + maxvpath = 0; + + elem = 0; + p = dirpath; + while (*p != '\0') + { + char *v; + unsigned int len; + + /* Find the next entry. */ + while (*p != '\0' && *p == ':') + ++p; + if (*p == '\0') + break; + + /* Find the end of this entry. */ + v = p; + while (*p != '\0' && *p != ':') + ++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 == '.') + continue; + + v = savestring (v, len); + if (dir_file_exists_p (v, "")) + { + vpath[elem++] = dir_name (v); + free (v); + if (len > maxvpath) + maxvpath = len; + } + else + free (v); + } + + 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); +} + +/* Search the VPATH list whose pattern matches *FILE for a directory + where the name pointed to by FILE exists. If it is found, the pointer + in FILE is set to the newly malloc'd name of the existing file and + we return 1. Otherwise we return 0. */ + +int +vpath_search (file) + char **file; +{ + 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)) + return 1; + + if (general_vpath != 0 + && selective_vpath_search (general_vpath, file)) + return 1; + + return 0; +} + + +/* Search the given VPATH list for a directory where the name pointed + to by FILE exists. If it is found, the pointer in FILE + is set to the newly malloc'd name of the existing file and we return 1. + Otherwise we return 0. */ + +static int +selective_vpath_search (path, file) + struct vpath *path; + char **file; +{ + 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) + { + 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); + + if (not_target) + /* Since *FILE is not a target, if the file is + mentioned in a makefile, we consider it existent. */ + exists = lookup_file (name) != 0; + + 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'; + + /* Make sure the directory exists and we know its contents. */ + if (name_dplen > 0 && !dir_file_exists_p (name, "")) + /* It doesn't exist. */ + continue; + + /* 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 = dir_file_exists_p (name, filename); + } + + if (exists) + { + /* We have found a file. + Store the name we found into *FILE for the caller. */ + + /* Put the slash back in NAME. */ + *n = '/'; + + *file = savestring (name, (n + 1 - name) + flen); + + return 1; + } + } + + 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' : ':'); + } + + 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' : ':'); + } +} |