1 |
|
|
/* $OpenBSD: dir.c,v 1.68 2016/10/21 16:12:38 espie Exp $ */ |
2 |
|
|
/* $NetBSD: dir.c,v 1.14 1997/03/29 16:51:26 christos Exp $ */ |
3 |
|
|
|
4 |
|
|
/* |
5 |
|
|
* Copyright (c) 1999 Marc Espie. |
6 |
|
|
* |
7 |
|
|
* Extensive code changes for the OpenBSD project. |
8 |
|
|
* |
9 |
|
|
* Redistribution and use in source and binary forms, with or without |
10 |
|
|
* modification, are permitted provided that the following conditions |
11 |
|
|
* are met: |
12 |
|
|
* 1. Redistributions of source code must retain the above copyright |
13 |
|
|
* notice, this list of conditions and the following disclaimer. |
14 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
15 |
|
|
* notice, this list of conditions and the following disclaimer in the |
16 |
|
|
* documentation and/or other materials provided with the distribution. |
17 |
|
|
* |
18 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE OPENBSD PROJECT AND CONTRIBUTORS |
19 |
|
|
* ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
20 |
|
|
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR |
21 |
|
|
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE OPENBSD |
22 |
|
|
* PROJECT OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, |
23 |
|
|
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT |
24 |
|
|
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, |
25 |
|
|
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY |
26 |
|
|
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT |
27 |
|
|
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE |
28 |
|
|
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. |
29 |
|
|
*/ |
30 |
|
|
/* |
31 |
|
|
* Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
32 |
|
|
* Copyright (c) 1988, 1989 by Adam de Boor |
33 |
|
|
* Copyright (c) 1989 by Berkeley Softworks |
34 |
|
|
* All rights reserved. |
35 |
|
|
* |
36 |
|
|
* This code is derived from software contributed to Berkeley by |
37 |
|
|
* Adam de Boor. |
38 |
|
|
* |
39 |
|
|
* Redistribution and use in source and binary forms, with or without |
40 |
|
|
* modification, are permitted provided that the following conditions |
41 |
|
|
* are met: |
42 |
|
|
* 1. Redistributions of source code must retain the above copyright |
43 |
|
|
* notice, this list of conditions and the following disclaimer. |
44 |
|
|
* 2. Redistributions in binary form must reproduce the above copyright |
45 |
|
|
* notice, this list of conditions and the following disclaimer in the |
46 |
|
|
* documentation and/or other materials provided with the distribution. |
47 |
|
|
* 3. Neither the name of the University nor the names of its contributors |
48 |
|
|
* may be used to endorse or promote products derived from this software |
49 |
|
|
* without specific prior written permission. |
50 |
|
|
* |
51 |
|
|
* THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
52 |
|
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
53 |
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
54 |
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
55 |
|
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
56 |
|
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
57 |
|
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
58 |
|
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
59 |
|
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
60 |
|
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
61 |
|
|
* SUCH DAMAGE. |
62 |
|
|
*/ |
63 |
|
|
|
64 |
|
|
#include <sys/stat.h> |
65 |
|
|
#include <dirent.h> |
66 |
|
|
#include <limits.h> |
67 |
|
|
#include <stddef.h> |
68 |
|
|
#include <stdint.h> |
69 |
|
|
#include <stdio.h> |
70 |
|
|
#include <stdlib.h> |
71 |
|
|
#include <string.h> |
72 |
|
|
#include <ohash.h> |
73 |
|
|
#include "config.h" |
74 |
|
|
#include "defines.h" |
75 |
|
|
#include "dir.h" |
76 |
|
|
#include "lst.h" |
77 |
|
|
#include "memory.h" |
78 |
|
|
#include "buf.h" |
79 |
|
|
#include "gnode.h" |
80 |
|
|
#include "arch.h" |
81 |
|
|
#include "targ.h" |
82 |
|
|
#include "error.h" |
83 |
|
|
#include "str.h" |
84 |
|
|
#include "timestamp.h" |
85 |
|
|
|
86 |
|
|
|
87 |
|
|
/* A search path consists of a Lst of PathEntry structures. A Path |
88 |
|
|
* structure has in it the name of the directory and a hash table of all |
89 |
|
|
* the files in the directory. This is used to cut down on the number of |
90 |
|
|
* system calls necessary to find implicit dependents and their like. |
91 |
|
|
* Since these searches are made before any actions are taken, we need not |
92 |
|
|
* worry about the directory changing due to creation commands. If this |
93 |
|
|
* hampers the style of some makefiles, they must be changed. |
94 |
|
|
* |
95 |
|
|
* A list of all previously-read directories is kept in the |
96 |
|
|
* knownDirectories cache. |
97 |
|
|
* |
98 |
|
|
* The need for the caching of whole directories is brought about by |
99 |
|
|
* the multi-level transformation code in suff.c, which tends to search |
100 |
|
|
* for far more files than regular make does. In the initial |
101 |
|
|
* implementation, the amount of time spent performing "stat" calls was |
102 |
|
|
* truly astronomical. The problem with hashing at the start is, |
103 |
|
|
* of course, that pmake doesn't then detect changes to these directories |
104 |
|
|
* during the course of the make. Three possibilities suggest themselves: |
105 |
|
|
* |
106 |
|
|
* 1) just use stat to test for a file's existence. As mentioned |
107 |
|
|
* above, this is very inefficient due to the number of checks |
108 |
|
|
* engendered by the multi-level transformation code. |
109 |
|
|
* 2) use readdir() and company to search the directories, keeping |
110 |
|
|
* them open between checks. I have tried this and while it |
111 |
|
|
* didn't slow down the process too much, it could severely |
112 |
|
|
* affect the amount of parallelism available as each directory |
113 |
|
|
* open would take another file descriptor out of play for |
114 |
|
|
* handling I/O for another job. Given that it is only recently |
115 |
|
|
* that UNIX OS's have taken to allowing more than 20 or 32 |
116 |
|
|
* file descriptors for a process, this doesn't seem acceptable |
117 |
|
|
* to me. |
118 |
|
|
* 3) record the mtime of the directory in the PathEntry structure and |
119 |
|
|
* verify the directory hasn't changed since the contents were |
120 |
|
|
* hashed. This will catch the creation or deletion of files, |
121 |
|
|
* but not the updating of files. However, since it is the |
122 |
|
|
* creation and deletion that is the problem, this could be |
123 |
|
|
* a good thing to do. Unfortunately, if the directory (say ".") |
124 |
|
|
* were fairly large and changed fairly frequently, the constant |
125 |
|
|
* rehashing could seriously degrade performance. It might be |
126 |
|
|
* good in such cases to keep track of the number of rehashes |
127 |
|
|
* and if the number goes over a (small) limit, resort to using |
128 |
|
|
* stat in its place. |
129 |
|
|
* |
130 |
|
|
* An additional thing to consider is that pmake is used primarily |
131 |
|
|
* to create C programs and until recently pcc-based compilers refused |
132 |
|
|
* to allow you to specify where the resulting object file should be |
133 |
|
|
* placed. This forced all objects to be created in the current |
134 |
|
|
* directory. This isn't meant as a full excuse, just an explanation of |
135 |
|
|
* some of the reasons for the caching used here. |
136 |
|
|
* |
137 |
|
|
* One more note: the location of a target's file is only performed |
138 |
|
|
* on the downward traversal of the graph and then only for terminal |
139 |
|
|
* nodes in the graph. This could be construed as wrong in some cases, |
140 |
|
|
* but prevents inadvertent modification of files when the "installed" |
141 |
|
|
* directory for a file is provided in the search path. |
142 |
|
|
* |
143 |
|
|
* Another data structure maintained by this module is an mtime |
144 |
|
|
* cache used when the searching of cached directories fails to find |
145 |
|
|
* a file. In the past, Dir_FindFile would simply perform an access() |
146 |
|
|
* call in such a case to determine if the file could be found using |
147 |
|
|
* just the name given. When this hit, however, all that was gained |
148 |
|
|
* was the knowledge that the file existed. Given that an access() is |
149 |
|
|
* essentially a stat() without the copyout() call, and that the same |
150 |
|
|
* filesystem overhead would have to be incurred in Dir_MTime, it made |
151 |
|
|
* sense to replace the access() with a stat() and record the mtime |
152 |
|
|
* in a cache for when Dir_MTime was actually called. */ |
153 |
|
|
|
154 |
|
|
|
155 |
|
|
/* several data structures exist to handle caching of directory stuff. |
156 |
|
|
* |
157 |
|
|
* There is a global hash of directory names (knownDirectories), and each |
158 |
|
|
* read directory is kept there as one PathEntry instance. Such a structure |
159 |
|
|
* only contains the file names. |
160 |
|
|
* |
161 |
|
|
* There is a global hash of timestamps (modification times), so care must |
162 |
|
|
* be taken of giving the right file names to that structure. |
163 |
|
|
* |
164 |
|
|
* XXX A set of similar structure should exist at the Target level to properly |
165 |
|
|
* take care of VPATH issues. |
166 |
|
|
*/ |
167 |
|
|
|
168 |
|
|
|
169 |
|
|
/* each directory is cached into a PathEntry structure. */ |
170 |
|
|
struct PathEntry { |
171 |
|
|
int refCount; /* ref-counted, can participate to |
172 |
|
|
* several paths */ |
173 |
|
|
struct ohash files; /* hash of name of files in the directory */ |
174 |
|
|
char name[1]; /* directory name */ |
175 |
|
|
}; |
176 |
|
|
|
177 |
|
|
/* PathEntry kept on knownDirectories */ |
178 |
|
|
static struct ohash_info dir_info = { |
179 |
|
|
offsetof(struct PathEntry, name), NULL, hash_calloc, hash_free, |
180 |
|
|
element_alloc |
181 |
|
|
}; |
182 |
|
|
|
183 |
|
|
static struct ohash knownDirectories; /* cache all open directories */ |
184 |
|
|
|
185 |
|
|
|
186 |
|
|
/* file names kept in a path entry */ |
187 |
|
|
static struct ohash_info file_info = { |
188 |
|
|
0, NULL, hash_calloc, hash_free, element_alloc |
189 |
|
|
}; |
190 |
|
|
|
191 |
|
|
|
192 |
|
|
/* Global structure used to cache mtimes. XXX We don't cache an mtime |
193 |
|
|
* before a caller actually looks up for the given time, because of the |
194 |
|
|
* possibility a caller might update the file and invalidate the cache |
195 |
|
|
* entry, and we don't look up in this cache except as a last resort. |
196 |
|
|
*/ |
197 |
|
|
struct file_stamp { |
198 |
|
|
struct timespec mtime; /* time stamp... */ |
199 |
|
|
char name[1]; /* ...for that file. */ |
200 |
|
|
}; |
201 |
|
|
|
202 |
|
|
static struct ohash mtimes; |
203 |
|
|
|
204 |
|
|
|
205 |
|
|
static struct ohash_info stamp_info = { |
206 |
|
|
offsetof(struct file_stamp, name), NULL, hash_calloc, hash_free, |
207 |
|
|
element_alloc |
208 |
|
|
}; |
209 |
|
|
|
210 |
|
|
|
211 |
|
|
|
212 |
|
|
static LIST theDefaultPath; /* main search path */ |
213 |
|
|
Lst defaultPath= &theDefaultPath; |
214 |
|
|
struct PathEntry *dot; /* contents of current directory */ |
215 |
|
|
|
216 |
|
|
|
217 |
|
|
|
218 |
|
|
/* add_file(path, name): add a file name to a path hash structure. */ |
219 |
|
|
static void add_file(struct PathEntry *, const char *); |
220 |
|
|
/* n = find_file_hashi(p, name, end, hv): retrieve name in a path hash |
221 |
|
|
* structure. */ |
222 |
|
|
static char *find_file_hashi(struct PathEntry *, const char *, const char *, |
223 |
|
|
uint32_t); |
224 |
|
|
|
225 |
|
|
/* stamp = find_stampi(name, end): look for (name, end) in the global |
226 |
|
|
* cache. */ |
227 |
|
|
static struct file_stamp *find_stampi(const char *, const char *); |
228 |
|
|
/* record_stamp(name, timestamp): record timestamp for name in the global |
229 |
|
|
* cache. */ |
230 |
|
|
static void record_stamp(const char *, struct timespec); |
231 |
|
|
|
232 |
|
|
static bool read_directory(struct PathEntry *); |
233 |
|
|
/* p = DirReaddiri(name, end): read an actual directory, caching results |
234 |
|
|
* as we go. */ |
235 |
|
|
static struct PathEntry *create_PathEntry(const char *, const char *); |
236 |
|
|
/* Debugging: show a dir name in a path. */ |
237 |
|
|
static void DirPrintDir(void *); |
238 |
|
|
|
239 |
|
|
/*** |
240 |
|
|
*** timestamp handling |
241 |
|
|
***/ |
242 |
|
|
|
243 |
|
|
static void |
244 |
|
|
record_stamp(const char *file, struct timespec t) |
245 |
|
|
{ |
246 |
|
|
unsigned int slot; |
247 |
|
290678 |
const char *end = NULL; |
248 |
|
|
struct file_stamp *n; |
249 |
|
|
|
250 |
|
145339 |
slot = ohash_qlookupi(&mtimes, file, &end); |
251 |
|
145339 |
n = ohash_find(&mtimes, slot); |
252 |
✗✓ |
145339 |
if (n) |
253 |
|
|
n->mtime = t; |
254 |
|
|
else { |
255 |
|
145339 |
n = ohash_create_entry(&stamp_info, file, &end); |
256 |
|
145339 |
n->mtime = t; |
257 |
|
145339 |
ohash_insert(&mtimes, slot, n); |
258 |
|
|
} |
259 |
|
145339 |
} |
260 |
|
|
|
261 |
|
|
static struct file_stamp * |
262 |
|
|
find_stampi(const char *file, const char *efile) |
263 |
|
|
{ |
264 |
|
2001006 |
return ohash_find(&mtimes, ohash_qlookupi(&mtimes, file, &efile)); |
265 |
|
|
} |
266 |
|
|
|
267 |
|
|
/*** |
268 |
|
|
*** PathEntry handling |
269 |
|
|
***/ |
270 |
|
|
|
271 |
|
|
static void |
272 |
|
|
add_file(struct PathEntry *p, const char *file) |
273 |
|
|
{ |
274 |
|
|
unsigned int slot; |
275 |
|
7429940 |
const char *end = NULL; |
276 |
|
|
char *n; |
277 |
|
3714970 |
struct ohash *h = &p->files; |
278 |
|
|
|
279 |
|
3714970 |
slot = ohash_qlookupi(h, file, &end); |
280 |
|
3714970 |
n = ohash_find(h, slot); |
281 |
✓✗ |
3714970 |
if (n == NULL) { |
282 |
|
3714970 |
n = ohash_create_entry(&file_info, file, &end); |
283 |
|
3714970 |
ohash_insert(h, slot, n); |
284 |
|
3714970 |
} |
285 |
|
3714970 |
} |
286 |
|
|
|
287 |
|
|
static char * |
288 |
|
|
find_file_hashi(struct PathEntry *p, const char *file, const char *efile, |
289 |
|
|
uint32_t hv) |
290 |
|
|
{ |
291 |
|
7453898 |
struct ohash *h = &p->files; |
292 |
|
|
|
293 |
|
3726949 |
return ohash_find(h, ohash_lookup_interval(h, file, efile, hv)); |
294 |
|
|
} |
295 |
|
|
|
296 |
|
|
static bool |
297 |
|
|
read_directory(struct PathEntry *p) |
298 |
|
|
{ |
299 |
|
|
DIR *d; |
300 |
|
|
struct dirent *dp; |
301 |
|
|
|
302 |
✗✓ |
70298 |
if (DEBUG(DIR)) { |
303 |
|
|
printf("Caching %s...", p->name); |
304 |
|
|
fflush(stdout); |
305 |
|
|
} |
306 |
|
|
|
307 |
✓✓ |
35149 |
if ((d = opendir(p->name)) == NULL) |
308 |
|
42 |
return false; |
309 |
|
|
|
310 |
|
35107 |
ohash_init(&p->files, 4, &file_info); |
311 |
|
|
|
312 |
✓✓ |
7570368 |
while ((dp = readdir(d)) != NULL) { |
313 |
✓✓✓✗
|
3820291 |
if (dp->d_name[0] == '.' && |
314 |
✓✓ |
70228 |
(dp->d_name[1] == '\0' || |
315 |
✓✓ |
70228 |
(dp->d_name[1] == '.' && dp->d_name[2] == '\0'))) |
316 |
|
70214 |
continue; |
317 |
|
3714970 |
add_file(p, dp->d_name); |
318 |
|
|
} |
319 |
|
35107 |
(void)closedir(d); |
320 |
✗✓ |
35107 |
if (DEBUG(DIR)) |
321 |
|
|
printf("done\n"); |
322 |
|
35107 |
return true; |
323 |
|
35149 |
} |
324 |
|
|
|
325 |
|
|
/* Read a directory, either from the disk, or from the cache. */ |
326 |
|
|
static struct PathEntry * |
327 |
|
|
create_PathEntry(const char *name, const char *ename) |
328 |
|
|
{ |
329 |
|
|
struct PathEntry *p; |
330 |
|
|
unsigned int slot; |
331 |
|
|
|
332 |
|
35149 |
slot = ohash_qlookupi(&knownDirectories, name, &ename); |
333 |
|
35149 |
p = ohash_find(&knownDirectories, slot); |
334 |
|
|
|
335 |
✓✗ |
35149 |
if (p == NULL) { |
336 |
|
35149 |
p = ohash_create_entry(&dir_info, name, &ename); |
337 |
|
35149 |
p->refCount = 0; |
338 |
✓✓ |
35149 |
if (!read_directory(p)) { |
339 |
|
42 |
free(p); |
340 |
|
42 |
return NULL; |
341 |
|
|
} |
342 |
|
35107 |
ohash_insert(&knownDirectories, slot, p); |
343 |
|
35107 |
} |
344 |
|
35107 |
p->refCount++; |
345 |
|
35107 |
return p; |
346 |
|
35149 |
} |
347 |
|
|
|
348 |
|
|
char * |
349 |
|
|
PathEntry_name(struct PathEntry *p) |
350 |
|
|
{ |
351 |
|
5102 |
return p->name; |
352 |
|
|
} |
353 |
|
|
|
354 |
|
|
/* Side Effects: cache the current directory */ |
355 |
|
|
void |
356 |
|
|
Dir_Init(void) |
357 |
|
|
{ |
358 |
|
|
char *dotname = "."; |
359 |
|
|
|
360 |
|
|
Static_Lst_Init(defaultPath); |
361 |
|
33706 |
ohash_init(&knownDirectories, 4, &dir_info); |
362 |
|
16853 |
ohash_init(&mtimes, 4, &stamp_info); |
363 |
|
|
|
364 |
|
|
|
365 |
|
16853 |
dot = create_PathEntry(dotname, dotname+1); |
366 |
|
|
|
367 |
✗✓ |
16853 |
if (!dot) |
368 |
|
|
Fatal("Can't access current directory"); |
369 |
|
16853 |
} |
370 |
|
|
|
371 |
|
|
/*- |
372 |
|
|
*----------------------------------------------------------------------- |
373 |
|
|
* Dir_MatchFilesi -- |
374 |
|
|
* Given a pattern and a PathEntry structure, see if any files |
375 |
|
|
* match the pattern and add their names to the 'expansions' list if |
376 |
|
|
* any do. This is incomplete -- it doesn't take care of patterns like |
377 |
|
|
* src / *src / *.c properly (just *.c on any of the directories), but it |
378 |
|
|
* will do for now. |
379 |
|
|
*----------------------------------------------------------------------- |
380 |
|
|
*/ |
381 |
|
|
void |
382 |
|
|
Dir_MatchFilesi(const char *word, const char *eword, struct PathEntry *p, |
383 |
|
|
Lst expansions) |
384 |
|
|
{ |
385 |
|
67416 |
unsigned int search; /* Index into the directory's table */ |
386 |
|
|
const char *entry; /* Current entry in the table */ |
387 |
|
|
|
388 |
✓✓ |
7331068 |
for (entry = ohash_first(&p->files, &search); entry != NULL; |
389 |
|
3631826 |
entry = ohash_next(&p->files, &search)) { |
390 |
|
|
/* See if the file matches the given pattern. We follow the UNIX |
391 |
|
|
* convention that dot files will only be found if the pattern |
392 |
|
|
* begins with a dot (the hashing scheme doesn't hash . or .., |
393 |
|
|
* so they won't match `.*'. */ |
394 |
✓✗✓✓
|
7263652 |
if (*word != '.' && *entry == '.') |
395 |
|
|
continue; |
396 |
✓✓ |
3631815 |
if (Str_Matchi(entry, strchr(entry, '\0'), word, eword)) |
397 |
|
17273 |
Lst_AtEnd(expansions, |
398 |
✓✓ |
51819 |
p == dot ? estrdup(entry) : |
399 |
|
17271 |
Str_concat(p->name, entry, '/')); |
400 |
|
|
} |
401 |
|
33708 |
} |
402 |
|
|
|
403 |
|
|
/*- |
404 |
|
|
* Side Effects: |
405 |
|
|
* If the file is found in a directory which is not on the path |
406 |
|
|
* already (either 'name' is absolute or it is a relative path |
407 |
|
|
* [ dir1/.../dirn/file ] which exists below one of the directories |
408 |
|
|
* already on the search path), its directory is added to the end |
409 |
|
|
* of the path on the assumption that there will be more files in |
410 |
|
|
* that directory later on. |
411 |
|
|
*/ |
412 |
|
|
char * |
413 |
|
|
Dir_FindFileComplexi(const char *name, const char *ename, Lst path, |
414 |
|
|
bool checkCurdirFirst) |
415 |
|
|
{ |
416 |
|
|
struct PathEntry *p; /* current path member */ |
417 |
|
|
char *p1; /* pointer into p->name */ |
418 |
|
|
const char *p2; /* pointer into name */ |
419 |
|
|
LstNode ln; /* a list element */ |
420 |
|
|
char *file; /* the current filename to check */ |
421 |
|
|
char *temp; /* index into file */ |
422 |
|
|
const char *basename; |
423 |
|
|
bool hasSlash; |
424 |
|
2660313 |
struct stat stb;/* Buffer for stat, if necessary */ |
425 |
|
|
struct file_stamp *entry; |
426 |
|
|
/* Entry for mtimes table */ |
427 |
|
|
uint32_t hv; /* hash value for last component in file name */ |
428 |
|
|
char *q; /* Str_dupi(name, ename) */ |
429 |
|
|
|
430 |
|
|
/* Find the final component of the name and note whether name has a |
431 |
|
|
* slash in it */ |
432 |
|
2660313 |
basename = Str_rchri(name, ename, '/'); |
433 |
✓✓ |
2660313 |
if (basename) { |
434 |
|
|
hasSlash = true; |
435 |
|
2035316 |
basename++; |
436 |
|
2035316 |
} else { |
437 |
|
|
hasSlash = false; |
438 |
|
|
basename = name; |
439 |
|
|
} |
440 |
|
|
|
441 |
|
2660313 |
hv = ohash_interval(basename, &ename); |
442 |
|
|
|
443 |
✗✓ |
2660313 |
if (DEBUG(DIR)) |
444 |
|
|
printf("Searching for %s...", name); |
445 |
|
|
/* Unless checkCurDirFirst is false, we always look for |
446 |
|
|
* the file in the current directory before anywhere else |
447 |
|
|
* and we always return exactly what the caller specified. */ |
448 |
✓✓✓✓
|
3126926 |
if (checkCurdirFirst && |
449 |
✓✓✓✓ ✓✗ |
4537141 |
(!hasSlash || (basename - name == 2 && *name == '.')) && |
450 |
|
466613 |
find_file_hashi(dot, basename, ename, hv) != NULL) { |
451 |
✗✓ |
42285 |
if (DEBUG(DIR)) |
452 |
|
|
printf("in '.'\n"); |
453 |
|
42285 |
return Str_dupi(name, ename); |
454 |
|
|
} |
455 |
|
|
|
456 |
|
|
/* Then, we look through all the directories on path, seeking one |
457 |
|
|
* containing the final component of name and whose final |
458 |
|
|
* component(s) match name's initial component(s). |
459 |
|
|
* If found, we concatenate the directory name and the |
460 |
|
|
* final component and return the resulting string. */ |
461 |
✓✓ |
11407626 |
for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) { |
462 |
|
3260336 |
p = Lst_Datum(ln); |
463 |
✗✓ |
3260336 |
if (DEBUG(DIR)) |
464 |
|
|
printf("%s...", p->name); |
465 |
✓✓ |
3260336 |
if (find_file_hashi(p, basename, ename, hv) != NULL) { |
466 |
✗✓ |
144181 |
if (DEBUG(DIR)) |
467 |
|
|
printf("here..."); |
468 |
✓✓ |
144181 |
if (hasSlash) { |
469 |
|
|
/* If the name had a slash, its initial |
470 |
|
|
* components and p's final components must |
471 |
|
|
* match. This is false if a mismatch is |
472 |
|
|
* encountered before all of the initial |
473 |
|
|
* components have been checked (p2 > name at |
474 |
|
|
* the end of the loop), or we matched only |
475 |
|
|
* part of one of the components of p along |
476 |
|
|
* with all the rest of them (*p1 != '/'). */ |
477 |
|
3901 |
p1 = p->name + strlen(p->name) - 1; |
478 |
|
3901 |
p2 = basename - 2; |
479 |
✓✓✓✗ ✓✓ |
522832 |
while (p2 >= name && p1 >= p->name && |
480 |
|
129113 |
*p1 == *p2) { |
481 |
|
128402 |
p1--; |
482 |
|
128402 |
p2--; |
483 |
|
|
} |
484 |
✓✓✗✗
|
3901 |
if (p2 >= name || |
485 |
✗✓ |
3190 |
(p1 >= p->name && *p1 != '/')) { |
486 |
✗✓ |
711 |
if (DEBUG(DIR)) |
487 |
|
|
printf("component mismatch -- continuing..."); |
488 |
|
|
continue; |
489 |
|
|
} |
490 |
|
|
} |
491 |
|
286940 |
file = Str_concati(p->name, strchr(p->name, '\0'), basename, |
492 |
|
143470 |
ename, '/'); |
493 |
✗✓ |
143470 |
if (DEBUG(DIR)) |
494 |
|
|
printf("returning %s\n", file); |
495 |
|
143470 |
return file; |
496 |
✓✓ |
3116155 |
} else if (hasSlash) { |
497 |
|
|
/* If the file has a leading path component and that |
498 |
|
|
* component exactly matches the entire name of the |
499 |
|
|
* current search directory, we assume the file |
500 |
|
|
* doesn't exist and return NULL. */ |
501 |
✓✓✓✓
|
17387180 |
for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; |
502 |
|
5267804 |
p1++, p2++) |
503 |
|
|
continue; |
504 |
✓✓✓✓
|
574500 |
if (*p1 == '\0' && p2 == basename - 1) { |
505 |
✗✓ |
31081 |
if (DEBUG(DIR)) |
506 |
|
|
printf("has to be here but isn't -- returning NULL\n"); |
507 |
|
31081 |
return NULL; |
508 |
|
|
} |
509 |
|
|
} |
510 |
|
|
} |
511 |
|
|
|
512 |
|
|
/* We didn't find the file on any existing member of the path. |
513 |
|
|
* If the name doesn't contain a slash, end of story. |
514 |
|
|
* If it does contain a slash, however, it could be in a subdirectory |
515 |
|
|
* of one of the members of the search path. (eg., for path=/usr/include |
516 |
|
|
* and name=sys/types.h, the above search fails to turn up types.h |
517 |
|
|
* in /usr/include, even though /usr/include/sys/types.h exists). |
518 |
|
|
* |
519 |
|
|
* We only perform this look-up for non-absolute file names. |
520 |
|
|
* |
521 |
|
|
* Whenever we score a hit, we assume there will be more matches from |
522 |
|
|
* that directory, and append all but the last component of the |
523 |
|
|
* resulting name onto the search path. */ |
524 |
✓✓ |
2443477 |
if (!hasSlash) { |
525 |
✗✓ |
442471 |
if (DEBUG(DIR)) |
526 |
|
|
printf("failed.\n"); |
527 |
|
442471 |
return NULL; |
528 |
|
|
} |
529 |
|
|
|
530 |
✓✓ |
2001006 |
if (*name != '/') { |
531 |
|
|
bool checkedDot = false; |
532 |
|
|
|
533 |
✗✓ |
8242 |
if (DEBUG(DIR)) |
534 |
|
|
printf("failed. Trying subdirectories..."); |
535 |
✓✓ |
16624 |
for (ln = Lst_First(path); ln != NULL; ln = Lst_Adv(ln)) { |
536 |
|
70 |
p = Lst_Datum(ln); |
537 |
✓✗ |
70 |
if (p != dot) |
538 |
|
140 |
file = Str_concati(p->name, |
539 |
|
70 |
strchr(p->name, '\0'), name, ename, '/'); |
540 |
|
|
else { |
541 |
|
|
/* Checking in dot -- DON'T put a leading |
542 |
|
|
* ./ on the thing. */ |
543 |
|
|
file = Str_dupi(name, ename); |
544 |
|
|
checkedDot = true; |
545 |
|
|
} |
546 |
✗✓ |
70 |
if (DEBUG(DIR)) |
547 |
|
|
printf("checking %s...", file); |
548 |
|
|
|
549 |
✗✓ |
70 |
if (stat(file, &stb) == 0) { |
550 |
|
|
struct timespec mtime; |
551 |
|
|
|
552 |
|
|
ts_set_from_stat(stb, mtime); |
553 |
|
|
if (DEBUG(DIR)) |
554 |
|
|
printf("got it.\n"); |
555 |
|
|
|
556 |
|
|
/* We've found another directory to search. |
557 |
|
|
* We know there is a slash in 'file'. We |
558 |
|
|
* call Dir_AddDiri to add the new directory |
559 |
|
|
* onto the existing search path. Once that's |
560 |
|
|
* done, we return the file name, knowing that |
561 |
|
|
* should a file in this directory ever be |
562 |
|
|
* referenced again in such a manner, we will |
563 |
|
|
* find it without having to do numerous |
564 |
|
|
* access calls. */ |
565 |
|
|
temp = strrchr(file, '/'); |
566 |
|
|
Dir_AddDiri(path, file, temp); |
567 |
|
|
|
568 |
|
|
/* Save the modification time so if it's |
569 |
|
|
* needed, we don't have to fetch it again. */ |
570 |
|
|
if (DEBUG(DIR)) |
571 |
|
|
printf("Caching %s for %s\n", |
572 |
|
|
time_to_string(&mtime), file); |
573 |
|
|
record_stamp(file, mtime); |
574 |
|
|
return file; |
575 |
|
|
} else |
576 |
|
70 |
free(file); |
577 |
|
|
} |
578 |
|
|
|
579 |
✗✓ |
8242 |
if (DEBUG(DIR)) |
580 |
|
|
printf("failed. "); |
581 |
|
|
|
582 |
✗✓ |
8242 |
if (checkedDot) { |
583 |
|
|
/* Already checked by the given name, since . was in |
584 |
|
|
* the path, so no point in proceeding... */ |
585 |
|
|
if (DEBUG(DIR)) |
586 |
|
|
printf("Checked . already, returning NULL\n"); |
587 |
|
|
return NULL; |
588 |
|
|
} |
589 |
✓✗ |
8242 |
} |
590 |
|
|
|
591 |
|
|
/* Didn't find it that way, either. Last resort: look for the file |
592 |
|
|
* in the global mtime cache, then on the disk. |
593 |
|
|
* If this doesn't succeed, we finally return a NULL pointer. |
594 |
|
|
* |
595 |
|
|
* We cannot add this directory onto the search path because |
596 |
|
|
* of this amusing case: |
597 |
|
|
* $(INSTALLDIR)/$(FILE): $(FILE) |
598 |
|
|
* |
599 |
|
|
* $(FILE) exists in $(INSTALLDIR) but not in the current one. |
600 |
|
|
* When searching for $(FILE), we will find it in $(INSTALLDIR) |
601 |
|
|
* b/c we added it here. This is not good... */ |
602 |
|
2001006 |
q = Str_dupi(name, ename); |
603 |
✗✓ |
2001006 |
if (DEBUG(DIR)) |
604 |
|
|
printf("Looking for \"%s\"...", q); |
605 |
|
|
|
606 |
|
2001006 |
entry = find_stampi(name, ename); |
607 |
✓✓ |
2001006 |
if (entry != NULL) { |
608 |
✗✓ |
57727 |
if (DEBUG(DIR)) |
609 |
|
|
printf("got it (in mtime cache)\n"); |
610 |
|
57727 |
return q; |
611 |
✓✓ |
1943279 |
} else if (stat(q, &stb) == 0) { |
612 |
|
145339 |
struct timespec mtime; |
613 |
|
|
|
614 |
✗✓✗✗
|
145339 |
ts_set_from_stat(stb, mtime); |
615 |
✗✓ |
145339 |
if (DEBUG(DIR)) |
616 |
|
|
printf("Caching %s for %s\n", time_to_string(&mtime), |
617 |
|
|
q); |
618 |
|
145339 |
record_stamp(q, mtime); |
619 |
|
|
return q; |
620 |
|
145339 |
} else { |
621 |
✗✓ |
1797940 |
if (DEBUG(DIR)) |
622 |
|
|
printf("failed. Returning NULL\n"); |
623 |
|
1797940 |
free(q); |
624 |
|
1797940 |
return NULL; |
625 |
|
|
} |
626 |
|
2660313 |
} |
627 |
|
|
|
628 |
|
|
void |
629 |
|
|
Dir_AddDiri(Lst path, const char *name, const char *ename) |
630 |
|
|
{ |
631 |
|
|
struct PathEntry *p; |
632 |
|
|
|
633 |
|
36592 |
p = create_PathEntry(name, ename); |
634 |
✓✓ |
18296 |
if (p == NULL) |
635 |
|
42 |
return; |
636 |
✓✗ |
18254 |
if (p->refCount == 1) |
637 |
|
18254 |
Lst_AtEnd(path, p); |
638 |
|
|
else if (!Lst_AddNew(path, p)) |
639 |
|
|
return; |
640 |
|
36550 |
} |
641 |
|
|
|
642 |
|
|
void * |
643 |
|
|
Dir_CopyDir(void *p) |
644 |
|
|
{ |
645 |
|
63302 |
struct PathEntry *q = p; |
646 |
|
31651 |
q->refCount++; |
647 |
|
31651 |
return p; |
648 |
|
|
} |
649 |
|
|
|
650 |
|
|
void |
651 |
|
|
Dir_Destroy(void *pp) |
652 |
|
|
{ |
653 |
|
|
struct PathEntry *p = pp; |
654 |
|
|
|
655 |
|
|
if (--p->refCount == 0) { |
656 |
|
|
ohash_remove(&knownDirectories, |
657 |
|
|
ohash_qlookup(&knownDirectories, p->name)); |
658 |
|
|
free_hash(&p->files); |
659 |
|
|
free(p); |
660 |
|
|
} |
661 |
|
|
} |
662 |
|
|
|
663 |
|
|
/*- |
664 |
|
|
*----------------------------------------------------------------------- |
665 |
|
|
* Dir_Concat -- |
666 |
|
|
* Concatenate two paths, adding the second to the end of the first. |
667 |
|
|
* Makes sure to avoid duplicates. |
668 |
|
|
* |
669 |
|
|
* Side Effects: |
670 |
|
|
* Reference counts for added dirs are upped. |
671 |
|
|
*----------------------------------------------------------------------- |
672 |
|
|
*/ |
673 |
|
|
void |
674 |
|
|
Dir_Concat(Lst path1, Lst path2) |
675 |
|
|
{ |
676 |
|
|
LstNode ln; |
677 |
|
|
struct PathEntry *p; |
678 |
|
|
|
679 |
✗✓ |
50559 |
for (ln = Lst_First(path2); ln != NULL; ln = Lst_Adv(ln)) { |
680 |
|
|
p = Lst_Datum(ln); |
681 |
|
|
if (Lst_AddNew(path1, p)) |
682 |
|
|
p->refCount++; |
683 |
|
|
} |
684 |
|
16853 |
} |
685 |
|
|
|
686 |
|
|
static void |
687 |
|
|
DirPrintDir(void *p) |
688 |
|
|
{ |
689 |
|
|
const struct PathEntry *q = p; |
690 |
|
|
printf("%s ", q->name); |
691 |
|
|
} |
692 |
|
|
|
693 |
|
|
void |
694 |
|
|
Dir_PrintPath(Lst path) |
695 |
|
|
{ |
696 |
|
|
Lst_Every(path, DirPrintDir); |
697 |
|
|
} |
698 |
|
|
|
699 |
|
|
struct timespec |
700 |
|
|
Dir_MTime(GNode *gn) |
701 |
|
|
{ |
702 |
|
|
char *fullName; |
703 |
|
563404 |
struct stat stb; |
704 |
|
|
struct file_stamp *entry; |
705 |
|
|
unsigned int slot; |
706 |
|
|
struct timespec mtime; |
707 |
|
|
|
708 |
✓✓ |
281702 |
if (gn->type & OP_PHONY) |
709 |
|
53524 |
return gn->mtime; |
710 |
|
|
|
711 |
✗✓ |
228178 |
if (gn->type & OP_ARCHV) |
712 |
|
|
return Arch_MTime(gn); |
713 |
|
|
|
714 |
✓✓ |
228178 |
if (gn->path == NULL) { |
715 |
|
7665 |
fullName = Dir_FindFile(gn->name, defaultPath); |
716 |
✓✓ |
7665 |
if (fullName == NULL) |
717 |
|
5126 |
fullName = estrdup(gn->name); |
718 |
|
|
} else |
719 |
|
|
fullName = gn->path; |
720 |
|
|
|
721 |
|
228178 |
slot = ohash_qlookup(&mtimes, fullName); |
722 |
|
228178 |
entry = ohash_find(&mtimes, slot); |
723 |
✓✓ |
228178 |
if (entry != NULL) { |
724 |
|
|
/* Only do this once -- the second time folks are checking to |
725 |
|
|
* see if the file was actually updated, so we need to |
726 |
|
|
* actually go to the file system. */ |
727 |
✗✓ |
113603 |
if (DEBUG(DIR)) |
728 |
|
|
printf("Using cached time %s for %s\n", |
729 |
|
|
time_to_string(&entry->mtime), fullName); |
730 |
|
113603 |
mtime = entry->mtime; |
731 |
|
113603 |
free(entry); |
732 |
|
113603 |
ohash_remove(&mtimes, slot); |
733 |
✓✓ |
228178 |
} else if (stat(fullName, &stb) == 0) |
734 |
✗✓✗✗
|
69814 |
ts_set_from_stat(stb, mtime); |
735 |
|
|
else { |
736 |
✗✓ |
44761 |
if (gn->type & OP_MEMBER) { |
737 |
|
|
if (fullName != gn->path) |
738 |
|
|
free(fullName); |
739 |
|
|
return Arch_MemMTime(gn); |
740 |
|
|
} else |
741 |
|
|
ts_set_out_of_date(mtime); |
742 |
|
|
} |
743 |
✓✗✓✓
|
456356 |
if (fullName && gn->path == NULL) |
744 |
|
7665 |
gn->path = fullName; |
745 |
|
|
|
746 |
|
228178 |
gn->mtime = mtime; |
747 |
|
228178 |
return gn->mtime; |
748 |
|
281702 |
} |
749 |
|
|
|