| @@ -1,14 +1,14 @@ | | | @@ -1,14 +1,14 @@ |
1 | /* $NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $ */ | | 1 | /* $NetBSD: dir.c,v 1.283 2023/09/21 20:30:59 rillig Exp $ */ |
2 | | | 2 | |
3 | /* | | 3 | /* |
4 | * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. | | 4 | * Copyright (c) 1988, 1989, 1990 The Regents of the University of California. |
5 | * All rights reserved. | | 5 | * All rights reserved. |
6 | * | | 6 | * |
7 | * This code is derived from software contributed to Berkeley by | | 7 | * This code is derived from software contributed to Berkeley by |
8 | * Adam de Boor. | | 8 | * Adam de Boor. |
9 | * | | 9 | * |
10 | * Redistribution and use in source and binary forms, with or without | | 10 | * Redistribution and use in source and binary forms, with or without |
11 | * modification, are permitted provided that the following conditions | | 11 | * modification, are permitted provided that the following conditions |
12 | * are met: | | 12 | * are met: |
13 | * 1. Redistributions of source code must retain the above copyright | | 13 | * 1. Redistributions of source code must retain the above copyright |
14 | * notice, this list of conditions and the following disclaimer. | | 14 | * notice, this list of conditions and the following disclaimer. |
| @@ -73,156 +73,145 @@ | | | @@ -73,156 +73,145 @@ |
73 | * Directory searching using wildcards and/or normal names. | | 73 | * Directory searching using wildcards and/or normal names. |
74 | * Used both for source wildcarding in the makefile and for finding | | 74 | * Used both for source wildcarding in the makefile and for finding |
75 | * implicit sources. | | 75 | * implicit sources. |
76 | * | | 76 | * |
77 | * The interface for this module is: | | 77 | * The interface for this module is: |
78 | * Dir_Init Initialize the module. | | 78 | * Dir_Init Initialize the module. |
79 | * | | 79 | * |
80 | * Dir_InitCur Set the cur CachedDir. | | 80 | * Dir_InitCur Set the cur CachedDir. |
81 | * | | 81 | * |
82 | * Dir_InitDot Set the dot CachedDir. | | 82 | * Dir_InitDot Set the dot CachedDir. |
83 | * | | 83 | * |
84 | * Dir_End Clean up the module. | | 84 | * Dir_End Clean up the module. |
85 | * | | 85 | * |
86 | * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. | | 86 | * Dir_SetPATH Set ${.PATH} to reflect the state of dirSearchPath. |
87 | * | | 87 | * |
88 | * Dir_HasWildcards | | 88 | * Dir_HasWildcards |
89 | * Returns true if the name given it needs to | | 89 | * Returns true if the name given it needs to |
90 | * be wildcard-expanded. | | 90 | * be wildcard-expanded. |
91 | * | | 91 | * |
92 | * SearchPath_Expand | | 92 | * SearchPath_Expand |
93 | * Expand a filename pattern to find all matching files | | 93 | * Expand a filename pattern to find all matching files |
94 | * from the search path. | | 94 | * from the search path. |
95 | * | | 95 | * |
96 | * Dir_FindFile Searches for a file on a given search path. | | 96 | * Dir_FindFile Searches for a file on a given search path. |
97 | * If it exists, the entire path is returned. | | 97 | * If it exists, returns the entire path, otherwise NULL. |
98 | * Otherwise NULL is returned. | | | |
99 | * | | 98 | * |
100 | * Dir_FindHereOrAbove | | 99 | * Dir_FindHereOrAbove |
101 | * Search for a path in the current directory and | | 100 | * Search for a path in the current directory and then |
102 | * then all the directories above it in turn until | | 101 | * all the directories above it in turn, until the path |
103 | * the path is found or we reach the root ("/"). | | 102 | * is found or the root directory ("/") is reached. |
104 | * | | 103 | * |
105 | * Dir_UpdateMTime | | 104 | * Dir_UpdateMTime |
106 | * Update the modification time and path of a node with | | 105 | * Update the modification time and path of a node with |
107 | * data from the file corresponding to the node. | | 106 | * data from the file corresponding to the node. |
108 | * | | 107 | * |
109 | * SearchPath_Add Add a directory to a search path. | | 108 | * SearchPath_Add Add a directory to a search path. |
110 | * | | 109 | * |
111 | * SearchPath_ToFlags | | 110 | * SearchPath_ToFlags |
112 | * Given a search path and a command flag, create | | 111 | * Given a search path and a command flag, create |
113 | * a string with each of the directories in the path | | 112 | * a string with each of the directories in the path |
114 | * preceded by the command flag and all of them | | 113 | * preceded by the command flag and all of them |
115 | * separated by a space. | | 114 | * separated by a space. |
116 | * | | 115 | * |
117 | * Dir_Destroy Destroy an element of a search path. Frees up all | | | |
118 | * things that can be freed for the element as long | | | |
119 | * as the element is no longer referenced by any other | | | |
120 | * search path. | | | |
121 | * | | | |
122 | * SearchPath_Clear | | 116 | * SearchPath_Clear |
123 | * Resets a search path to the empty list. | | 117 | * Resets a search path to the empty list. |
124 | * | | 118 | * |
125 | * For debugging: | | 119 | * For debugging: |
126 | * Dir_PrintDirectories | | 120 | * Dir_PrintDirectories |
127 | * Print stats about the directory cache. | | 121 | * Print stats about the directory cache. |
128 | */ | | 122 | */ |
129 | | | 123 | |
130 | #include <sys/types.h> | | 124 | #include <sys/types.h> |
131 | #include <sys/stat.h> | | 125 | #include <sys/stat.h> |
132 | | | 126 | |
133 | #include <dirent.h> | | 127 | #include <dirent.h> |
134 | #include <errno.h> | | 128 | #include <errno.h> |
135 | | | 129 | |
136 | #include "make.h" | | 130 | #include "make.h" |
137 | #include "dir.h" | | 131 | #include "dir.h" |
138 | #include "job.h" | | 132 | #include "job.h" |
139 | | | 133 | |
140 | /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ | | 134 | /* "@(#)dir.c 8.2 (Berkeley) 1/2/94" */ |
141 | MAKE_RCSID("$NetBSD: dir.c,v 1.282 2023/06/23 04:56:54 rillig Exp $"); | | 135 | MAKE_RCSID("$NetBSD: dir.c,v 1.283 2023/09/21 20:30:59 rillig Exp $"); |
142 | | | 136 | |
143 | /* | | 137 | /* |
144 | * A search path is a list of CachedDir structures. A CachedDir has in it the | | 138 | * A search path is a list of CachedDir structures. A CachedDir has in it the |
145 | * name of the directory and the names of all the files in the directory. | | 139 | * name of the directory and the names of all the files in the directory. |
146 | * This is used to cut down on the number of system calls necessary to find | | 140 | * This is used to cut down on the number of system calls necessary to find |
147 | * implicit dependents and their like. Since these searches are made before | | 141 | * implicit dependents and their like. Since these searches are made before |
148 | * any actions are taken, we need not worry about the directory changing due | | 142 | * any actions are taken, we need not worry about the directory changing due |
149 | * to creation commands. If this hampers the style of some makefiles, they | | 143 | * to creation commands. If this hampers the style of some makefiles, they |
150 | * must be changed. | | 144 | * must be changed. |
151 | * | | 145 | * |
152 | * All previously-read directories are kept in openDirs, which is checked | | 146 | * All previously-read directories are kept in openDirs, which is checked |
153 | * first before a directory is opened. | | 147 | * first before a directory is opened. |
154 | * | | 148 | * |
155 | * The need for the caching of whole directories is brought about by the | | 149 | * This cache is used by the multi-level transformation code in suff.c, which |
156 | * multi-level transformation code in suff.c, which tends to search for far | | 150 | * tends to search for far more files than in regular explicit targets. After |
157 | * more files than regular make does. In the initial implementation, the | | 151 | * a directory has been cached, any later changes to that directory are not |
158 | * amount of time spent performing "stat" calls was truly astronomical. | | 152 | * reflected in the cache. To keep the cache up to date, there are several |
159 | * The problem with caching at the start is, of course, that pmake doesn't | | 153 | * ideas: |
160 | * then detect changes to these directories during the course of the make. | | | |
161 | * Three possibilities suggest themselves: | | | |
162 | * | | 154 | * |
163 | * 1) just use stat to test for a file's existence. As mentioned above, | | 155 | * 1) just use stat to test for a file's existence. As mentioned above, |
164 | * this is very inefficient due to the number of checks engendered by | | 156 | * this is very inefficient due to the number of checks performed by |
165 | * the multi-level transformation code. | | 157 | * the multi-level transformation code. |
166 | * | | 158 | * |
167 | * 2) use readdir() and company to search the directories, keeping them | | 159 | * 2) use readdir() to search the directories, keeping them open between |
168 | * open between checks. I have tried this and while it didn't slow down | | 160 | * checks. Around 1993 or earlier, this didn't slow down the process too |
169 | * the process too much, it could severely affect the amount of | | 161 | * much, but it consumed one file descriptor per open directory, which |
170 | * parallelism available as each directory open would take another file | | 162 | * was critical on the then-current operating systems, as many limited |
171 | * descriptor out of play for handling I/O for another job. Given that | | 163 | * the number of open file descriptors to 20 or 32. |
172 | * it is only recently (as of 1993 or earlier) that UNIX OS's have taken | | | |
173 | * to allowing more than 20 or 32 file descriptors for a process, this | | | |
174 | * doesn't seem acceptable to me. | | | |
175 | * | | 164 | * |
176 | * 3) record the mtime of the directory in the CachedDir structure and | | 165 | * 3) record the mtime of the directory in the CachedDir structure and |
177 | * verify the directory hasn't changed since the contents were cached. | | 166 | * verify the directory hasn't changed since the contents were cached. |
178 | * This will catch the creation or deletion of files, but not the | | 167 | * This will catch the creation or deletion of files, but not the |
179 | * updating of files. However, since it is the creation and deletion | | 168 | * updating of files. However, since it is the creation and deletion |
180 | * that is the problem, this could be a good thing to do. Unfortunately, | | 169 | * that is the problem, this could be a good thing to do. Unfortunately, |
181 | * if the directory (say ".") were fairly large and changed fairly | | 170 | * if the directory (say ".") were fairly large and changed fairly |
182 | * frequently, the constant reloading could seriously degrade | | 171 | * frequently, the constant reloading could seriously degrade |
183 | * performance. It might be good in such cases to keep track of the | | 172 | * performance. It might be good in such cases to keep track of the |
184 | * number of reloadings and if the number goes over a (small) limit, | | 173 | * number of reloadings and if the number goes over a (small) limit, |
185 | * resort to using stat in its place. | | 174 | * resort to using stat in its place. |
186 | * | | 175 | * |
187 | * An additional thing to consider is that pmake is used primarily to create | | 176 | * An additional thing to consider is that make is used primarily to create |
188 | * C programs and until recently (as of 1993 or earlier) pcc-based compilers | | 177 | * C programs and until recently (as of 1993 or earlier), pcc-based compilers |
189 | * refused to allow you to specify where the resulting object file should be | | 178 | * didn't have an option to specify where the resulting object file should be |
190 | * placed. This forced all objects to be created in the current directory. | | 179 | * placed. This forced all objects to be created in the current directory. |
191 | * This isn't meant as a full excuse, just an explanation of some of the | | 180 | * This isn't meant as a full excuse, just an explanation of some of the |
192 | * reasons for the caching used here. | | 181 | * reasons for the caching used here. |
193 | * | | 182 | * |
194 | * One more note: the location of a target's file is only performed on the | | 183 | * One more note: the location of a target's file is only performed on the |
195 | * downward traversal of the graph and then only for terminal nodes in the | | 184 | * downward traversal of the graph and then only for terminal nodes in the |
196 | * graph. This could be construed as wrong in some cases, but prevents | | 185 | * graph. This could be construed as wrong in some cases, but prevents |
197 | * inadvertent modification of files when the "installed" directory for a | | 186 | * inadvertent modification of files when the "installed" directory for a |
198 | * file is provided in the search path. | | 187 | * file is provided in the search path. |
199 | * | | 188 | * |
200 | * Another data structure maintained by this module is an mtime cache used | | 189 | * Another data structure maintained by this module is an mtime cache used |
201 | * when the searching of cached directories fails to find a file. In the past, | | 190 | * when the searching of cached directories fails to find a file. In the past, |
202 | * Dir_FindFile would simply perform an access() call in such a case to | | 191 | * Dir_FindFile would simply perform an access() call in such a case to |
203 | * determine if the file could be found using just the name given. When this | | 192 | * determine if the file could be found using just the name given. When this |
204 | * hit, however, all that was gained was the knowledge that the file existed. | | 193 | * hit, however, all that was gained was the knowledge that the file existed. |
205 | * Given that an access() is essentially a stat() without the copyout() call, | | 194 | * Given that an access() is essentially a stat() without the copyout() call, |
206 | * and that the same filesystem overhead would have to be incurred in | | 195 | * and that the same filesystem overhead would have to be incurred in |
207 | * Dir_MTime, it made sense to replace the access() with a stat() and record | | 196 | * Dir_MTime, it made sense to replace the access() with a stat() and record |
208 | * the mtime in a cache for when Dir_UpdateMTime was actually called. | | 197 | * the mtime in a cache for when Dir_UpdateMTime was actually called. |
209 | */ | | 198 | */ |
210 | | | 199 | |
211 | | | 200 | |
212 | /* A cache for the filenames in a directory. */ | | 201 | /* A cache for the filenames in a directory. */ |
213 | struct CachedDir { | | 202 | struct CachedDir { |
214 | /* | | 203 | /* |
215 | * Name of directory, either absolute or relative to the current | | 204 | * Name of the directory, either absolute or relative to the current |
216 | * directory. The name is not normalized in any way, that is, "." | | 205 | * directory. The name is not normalized in any way, that is, "." |
217 | * and "./." are different. | | 206 | * and "./." are different. |
218 | * | | 207 | * |
219 | * Not sure what happens when .CURDIR is assigned a new value; see | | 208 | * Not sure what happens when .CURDIR is assigned a new value; see |
220 | * Parse_Var. | | 209 | * Parse_Var. |
221 | */ | | 210 | */ |
222 | char *name; | | 211 | char *name; |
223 | | | 212 | |
224 | /* | | 213 | /* |
225 | * The number of SearchPaths that refer to this directory. | | 214 | * The number of SearchPaths that refer to this directory. |
226 | * Plus the number of global variables that refer to this directory. | | 215 | * Plus the number of global variables that refer to this directory. |
227 | * References from openDirs do not count though. | | 216 | * References from openDirs do not count though. |
228 | */ | | 217 | */ |
| @@ -263,28 +252,27 @@ static int bigmisses; /* Sought by itse | | | @@ -263,28 +252,27 @@ static int bigmisses; /* Sought by itse |
263 | /* The cached contents of ".", the relative current directory. */ | | 252 | /* The cached contents of ".", the relative current directory. */ |
264 | static CachedDir *dot = NULL; | | 253 | static CachedDir *dot = NULL; |
265 | /* The cached contents of the absolute current directory. */ | | 254 | /* The cached contents of the absolute current directory. */ |
266 | static CachedDir *cur = NULL; | | 255 | static CachedDir *cur = NULL; |
267 | /* A fake path entry indicating we need to look for '.' last. */ | | 256 | /* A fake path entry indicating we need to look for '.' last. */ |
268 | static CachedDir *dotLast = NULL; | | 257 | static CachedDir *dotLast = NULL; |
269 | | | 258 | |
270 | /* | | 259 | /* |
271 | * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to | | 260 | * Results of doing a last-resort stat in Dir_FindFile -- if we have to go to |
272 | * the system to find the file, we might as well have its mtime on record. | | 261 | * the system to find the file, we might as well have its mtime on record. |
273 | * | | 262 | * |
274 | * XXX: If this is done way early, there's a chance other rules will have | | 263 | * XXX: If this is done way early, there's a chance other rules will have |
275 | * already updated the file, in which case we'll update it again. Generally, | | 264 | * already updated the file, in which case we'll update it again. Generally, |
276 | * there won't be two rules to update a single file, so this should be ok, | | 265 | * there won't be two rules to update a single file, so this should be ok. |
277 | * but... | | | |
278 | */ | | 266 | */ |
279 | static HashTable mtimes; | | 267 | static HashTable mtimes; |
280 | | | 268 | |
281 | static HashTable lmtimes; /* same as mtimes but for lstat */ | | 269 | static HashTable lmtimes; /* same as mtimes but for lstat */ |
282 | | | 270 | |
283 | | | 271 | |
284 | static void OpenDirs_Remove(OpenDirs *, const char *); | | 272 | static void OpenDirs_Remove(OpenDirs *, const char *); |
285 | | | 273 | |
286 | | | 274 | |
287 | static CachedDir * | | 275 | static CachedDir * |
288 | CachedDir_New(const char *name) | | 276 | CachedDir_New(const char *name) |
289 | { | | 277 | { |
290 | CachedDir *dir = bmake_malloc(sizeof *dir); | | 278 | CachedDir *dir = bmake_malloc(sizeof *dir); |
| @@ -328,27 +316,27 @@ CachedDir_Unref(CachedDir *dir) | | | @@ -328,27 +316,27 @@ CachedDir_Unref(CachedDir *dir) |
328 | return; | | 316 | return; |
329 | | | 317 | |
330 | #ifdef DEBUG_REFCNT | | 318 | #ifdef DEBUG_REFCNT |
331 | DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); | | 319 | DEBUG2(DIR, "CachedDir %p free for \"%s\"\n", dir, dir->name); |
332 | #endif | | 320 | #endif |
333 | | | 321 | |
334 | OpenDirs_Remove(&openDirs, dir->name); | | 322 | OpenDirs_Remove(&openDirs, dir->name); |
335 | | | 323 | |
336 | free(dir->name); | | 324 | free(dir->name); |
337 | HashSet_Done(&dir->files); | | 325 | HashSet_Done(&dir->files); |
338 | free(dir); | | 326 | free(dir); |
339 | } | | 327 | } |
340 | | | 328 | |
341 | /* Update the value of the CachedDir variable, updating the reference counts. */ | | 329 | /* Update the value of 'var', updating the reference counts. */ |
342 | static void | | 330 | static void |
343 | CachedDir_Assign(CachedDir **var, CachedDir *dir) | | 331 | CachedDir_Assign(CachedDir **var, CachedDir *dir) |
344 | { | | 332 | { |
345 | CachedDir *prev; | | 333 | CachedDir *prev; |
346 | | | 334 | |
347 | prev = *var; | | 335 | prev = *var; |
348 | *var = dir; | | 336 | *var = dir; |
349 | if (dir != NULL) | | 337 | if (dir != NULL) |
350 | CachedDir_Ref(dir); | | 338 | CachedDir_Ref(dir); |
351 | if (prev != NULL) | | 339 | if (prev != NULL) |
352 | CachedDir_Unref(prev); | | 340 | CachedDir_Unref(prev); |
353 | } | | 341 | } |
354 | | | 342 | |
| @@ -477,38 +465,38 @@ Dir_Init(void) | | | @@ -477,38 +465,38 @@ Dir_Init(void) |
477 | | | 465 | |
478 | /* | | 466 | /* |
479 | * Called by Dir_InitDir and whenever .CURDIR is assigned to. | | 467 | * Called by Dir_InitDir and whenever .CURDIR is assigned to. |
480 | */ | | 468 | */ |
481 | void | | 469 | void |
482 | Dir_InitCur(const char *newCurdir) | | 470 | Dir_InitCur(const char *newCurdir) |
483 | { | | 471 | { |
484 | CachedDir *dir; | | 472 | CachedDir *dir; |
485 | | | 473 | |
486 | if (newCurdir == NULL) | | 474 | if (newCurdir == NULL) |
487 | return; | | 475 | return; |
488 | | | 476 | |
489 | /* | | 477 | /* |
490 | * Our build directory is not the same as our source directory. | | 478 | * The build directory is not the same as the source directory. |
491 | * Keep this one around too. | | 479 | * Keep this one around too. |
492 | */ | | 480 | */ |
493 | dir = SearchPath_Add(NULL, newCurdir); | | 481 | dir = SearchPath_Add(NULL, newCurdir); |
494 | if (dir == NULL) | | 482 | if (dir == NULL) |
495 | return; | | 483 | return; |
496 | | | 484 | |
497 | CachedDir_Assign(&cur, dir); | | 485 | CachedDir_Assign(&cur, dir); |
498 | } | | 486 | } |
499 | | | 487 | |
500 | /* | | 488 | /* |
501 | * (Re)initialize "dot" (current/object directory) path hash. | | 489 | * (Re)initialize "dot" (the current/object directory). |
502 | * Some directories may be cached. | | 490 | * Some directories may be cached. |
503 | */ | | 491 | */ |
504 | void | | 492 | void |
505 | Dir_InitDot(void) | | 493 | Dir_InitDot(void) |
506 | { | | 494 | { |
507 | CachedDir *dir; | | 495 | CachedDir *dir; |
508 | | | 496 | |
509 | dir = SearchPath_Add(NULL, "."); | | 497 | dir = SearchPath_Add(NULL, "."); |
510 | if (dir == NULL) { | | 498 | if (dir == NULL) { |
511 | Error("Cannot open `.' (%s)", strerror(errno)); | | 499 | Error("Cannot open `.' (%s)", strerror(errno)); |
512 | exit(2); /* Not 1 so -q can distinguish error */ | | 500 | exit(2); /* Not 1 so -q can distinguish error */ |
513 | } | | 501 | } |
514 | | | 502 | |
| @@ -589,28 +577,26 @@ Dir_SetSYSPATH(void) | | | @@ -589,28 +577,26 @@ Dir_SetSYSPATH(void) |
589 | CachedDir *dir = ln->datum; | | 577 | CachedDir *dir = ln->datum; |
590 | Global_Append(".SYSPATH", dir->name); | | 578 | Global_Append(".SYSPATH", dir->name); |
591 | } | | 579 | } |
592 | Var_ReadOnly(".SYSPATH", true); | | 580 | Var_ReadOnly(".SYSPATH", true); |
593 | } | | 581 | } |
594 | | | 582 | |
595 | /* | | 583 | /* |
596 | * See if the given name has any wildcard characters in it and all braces and | | 584 | * See if the given name has any wildcard characters in it and all braces and |
597 | * brackets are properly balanced. | | 585 | * brackets are properly balanced. |
598 | * | | 586 | * |
599 | * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think | | 587 | * XXX: This code is not 100% correct ([^]] fails etc.). I really don't think |
600 | * that make(1) should be expanding patterns, because then you have to set a | | 588 | * that make(1) should be expanding patterns, because then you have to set a |
601 | * mechanism for escaping the expansion! | | 589 | * mechanism for escaping the expansion! |
602 | * | | | |
603 | * Return true if the word should be expanded, false otherwise. | | | |
604 | */ | | 590 | */ |
605 | bool | | 591 | bool |
606 | Dir_HasWildcards(const char *name) | | 592 | Dir_HasWildcards(const char *name) |
607 | { | | 593 | { |
608 | const char *p; | | 594 | const char *p; |
609 | bool wild = false; | | 595 | bool wild = false; |
610 | int braces = 0, brackets = 0; | | 596 | int braces = 0, brackets = 0; |
611 | | | 597 | |
612 | for (p = name; *p != '\0'; p++) { | | 598 | for (p = name; *p != '\0'; p++) { |
613 | switch (*p) { | | 599 | switch (*p) { |
614 | case '{': | | 600 | case '{': |
615 | braces++; | | 601 | braces++; |
616 | wild = true; | | 602 | wild = true; |
| @@ -627,40 +613,34 @@ Dir_HasWildcards(const char *name) | | | @@ -627,40 +613,34 @@ Dir_HasWildcards(const char *name) |
627 | break; | | 613 | break; |
628 | case '?': | | 614 | case '?': |
629 | case '*': | | 615 | case '*': |
630 | wild = true; | | 616 | wild = true; |
631 | break; | | 617 | break; |
632 | default: | | 618 | default: |
633 | break; | | 619 | break; |
634 | } | | 620 | } |
635 | } | | 621 | } |
636 | return wild && brackets == 0 && braces == 0; | | 622 | return wild && brackets == 0 && braces == 0; |
637 | } | | 623 | } |
638 | | | 624 | |
639 | /* | | 625 | /* |
640 | * See if any files match the pattern and add their names to the 'expansions' | | 626 | * See if any files as seen from 'dir' match 'pattern', and add their names |
641 | * list if they do. | | 627 | * to 'expansions' if they do. |
642 | * | | 628 | * |
643 | * This is incomplete -- wildcards are only expanded in the final path | | 629 | * Wildcards are only expanded in the final path component, but not in |
644 | * component, but not in directories like src/lib*c/file*.c, but it | | 630 | * directories like src/lib*c/file*.c. To expand these wildcards, |
645 | * will do for now (now being 1993 until at least 2020). To expand these, | | | |
646 | * delegate the work to the shell, using the '!=' variable assignment | | 631 | * delegate the work to the shell, using the '!=' variable assignment |
647 | * operator, the ':sh' variable modifier or the ':!...!' variable modifier, | | 632 | * operator, the ':sh' variable modifier or the ':!...!' variable modifier, |
648 | * such as in ${:!echo src/lib*c/file*.c!}. | | 633 | * such as in ${:!echo src/lib*c/file*.c!}. |
649 | * | | | |
650 | * Input: | | | |
651 | * pattern Pattern to look for | | | |
652 | * dir Directory to search | | | |
653 | * expansion Place to store the results | | | |
654 | */ | | 634 | */ |
655 | static void | | 635 | static void |
656 | DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) | | 636 | DirMatchFiles(const char *pattern, CachedDir *dir, StringList *expansions) |
657 | { | | 637 | { |
658 | const char *dirName = dir->name; | | 638 | const char *dirName = dir->name; |
659 | bool isDot = dirName[0] == '.' && dirName[1] == '\0'; | | 639 | bool isDot = dirName[0] == '.' && dirName[1] == '\0'; |
660 | HashIter hi; | | 640 | HashIter hi; |
661 | | | 641 | |
662 | /* | | 642 | /* |
663 | * XXX: Iterating over all hash entries is inefficient. If the | | 643 | * XXX: Iterating over all hash entries is inefficient. If the |
664 | * pattern is a plain string without any wildcards, a direct lookup | | 644 | * pattern is a plain string without any wildcards, a direct lookup |
665 | * is faster. | | 645 | * is faster. |
666 | */ | | 646 | */ |
| @@ -814,27 +794,27 @@ DirExpandCurly(const char *word, const c | | | @@ -814,27 +794,27 @@ DirExpandCurly(const char *word, const c |
814 | if (contains_wildcard(file)) { | | 794 | if (contains_wildcard(file)) { |
815 | SearchPath_Expand(path, file, expansions); | | 795 | SearchPath_Expand(path, file, expansions); |
816 | free(file); | | 796 | free(file); |
817 | } else { | | 797 | } else { |
818 | Lst_Append(expansions, file); | | 798 | Lst_Append(expansions, file); |
819 | } | | 799 | } |
820 | | | 800 | |
821 | /* skip over the comma or closing brace */ | | 801 | /* skip over the comma or closing brace */ |
822 | piece = piece_end + 1; | | 802 | piece = piece_end + 1; |
823 | } | | 803 | } |
824 | } | | 804 | } |
825 | | | 805 | |
826 | | | 806 | |
827 | /* Expand the pattern in each of the directories from the path. */ | | 807 | /* Expand 'pattern' in each of the directories from 'path'. */ |
828 | static void | | 808 | static void |
829 | DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions) | | 809 | DirExpandPath(const char *pattern, SearchPath *path, StringList *expansions) |
830 | { | | 810 | { |
831 | SearchPathNode *ln; | | 811 | SearchPathNode *ln; |
832 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { | | 812 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { |
833 | CachedDir *dir = ln->datum; | | 813 | CachedDir *dir = ln->datum; |
834 | DirMatchFiles(pattern, dir, expansions); | | 814 | DirMatchFiles(pattern, dir, expansions); |
835 | } | | 815 | } |
836 | } | | 816 | } |
837 | | | 817 | |
838 | static void | | 818 | static void |
839 | PrintExpansions(StringList *expansions) | | 819 | PrintExpansions(StringList *expansions) |
840 | { | | 820 | { |
| @@ -851,46 +831,41 @@ PrintExpansions(StringList *expansions) | | | @@ -851,46 +831,41 @@ PrintExpansions(StringList *expansions) |
851 | /* | | 831 | /* |
852 | * The wildcard isn't in the first component. | | 832 | * The wildcard isn't in the first component. |
853 | * Find all the components up to the one with the wildcard. | | 833 | * Find all the components up to the one with the wildcard. |
854 | */ | | 834 | */ |
855 | static void | | 835 | static void |
856 | SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, | | 836 | SearchPath_ExpandMiddle(SearchPath *path, const char *pattern, |
857 | const char *wildcardComponent, StringList *expansions) | | 837 | const char *wildcardComponent, StringList *expansions) |
858 | { | | 838 | { |
859 | char *prefix, *dirpath, *end; | | 839 | char *prefix, *dirpath, *end; |
860 | SearchPath *partPath; | | 840 | SearchPath *partPath; |
861 | | | 841 | |
862 | prefix = bmake_strsedup(pattern, wildcardComponent + 1); | | 842 | prefix = bmake_strsedup(pattern, wildcardComponent + 1); |
863 | /* | | 843 | /* |
864 | * XXX: Check the "the directory is added to the path" part. | | | |
865 | * It is probably surprising that the directory before a | | | |
866 | * wildcard gets added to the path. | | | |
867 | */ | | | |
868 | /* | | | |
869 | * XXX: Only the first match of the prefix in the path is | | 844 | * XXX: Only the first match of the prefix in the path is |
870 | * taken, any others are ignored. The expectation may be | | 845 | * taken, any others are ignored. The expectation may be |
871 | * that the pattern is expanded in the whole path. | | 846 | * that the pattern is expanded in the whole path. |
872 | */ | | 847 | */ |
873 | dirpath = Dir_FindFile(prefix, path); | | 848 | dirpath = Dir_FindFile(prefix, path); |
874 | free(prefix); | | 849 | free(prefix); |
875 | | | 850 | |
876 | /* | | 851 | /* |
877 | * dirpath is null if can't find the leading component | | 852 | * dirpath is null if can't find the leading component |
878 | * | | 853 | * |
879 | * XXX: Dir_FindFile won't find internal components. i.e. if the | | 854 | * XXX: Dir_FindFile won't find internal components. i.e. if the |
880 | * path contains ../Etc/Object and we're looking for Etc, it won't | | 855 | * path contains ../Etc/Object and we're looking for Etc, it won't |
881 | * be found. Ah well. Probably not important. | | 856 | * be found. Ah well. Probably not important. |
882 | * | | 857 | * |
883 | * XXX: Check whether the above comment is still true. | | 858 | * TODO: Check whether the above comment is still true. |
884 | */ | | 859 | */ |
885 | if (dirpath == NULL) | | 860 | if (dirpath == NULL) |
886 | return; | | 861 | return; |
887 | | | 862 | |
888 | end = &dirpath[strlen(dirpath) - 1]; | | 863 | end = &dirpath[strlen(dirpath) - 1]; |
889 | /* XXX: What about multiple trailing slashes? */ | | 864 | /* XXX: What about multiple trailing slashes? */ |
890 | if (*end == '/') | | 865 | if (*end == '/') |
891 | *end = '\0'; | | 866 | *end = '\0'; |
892 | | | 867 | |
893 | partPath = SearchPath_New(); | | 868 | partPath = SearchPath_New(); |
894 | (void)SearchPath_Add(partPath, dirpath); | | 869 | (void)SearchPath_Add(partPath, dirpath); |
895 | DirExpandPath(wildcardComponent + 1, partPath, expansions); | | 870 | DirExpandPath(wildcardComponent + 1, partPath, expansions); |
896 | SearchPath_Free(partPath); | | 871 | SearchPath_Free(partPath); |
| @@ -911,32 +886,28 @@ SearchPath_Expand(SearchPath *path, cons | | | @@ -911,32 +886,28 @@ SearchPath_Expand(SearchPath *path, cons |
911 | const char *brace, *slash, *wildcard, *wildcardComponent; | | 886 | const char *brace, *slash, *wildcard, *wildcardComponent; |
912 | | | 887 | |
913 | assert(path != NULL); | | 888 | assert(path != NULL); |
914 | assert(expansions != NULL); | | 889 | assert(expansions != NULL); |
915 | | | 890 | |
916 | DEBUG1(DIR, "Expanding \"%s\"... ", pattern); | | 891 | DEBUG1(DIR, "Expanding \"%s\"... ", pattern); |
917 | | | 892 | |
918 | brace = strchr(pattern, '{'); | | 893 | brace = strchr(pattern, '{'); |
919 | if (brace != NULL) { | | 894 | if (brace != NULL) { |
920 | DirExpandCurly(pattern, brace, path, expansions); | | 895 | DirExpandCurly(pattern, brace, path, expansions); |
921 | goto done; | | 896 | goto done; |
922 | } | | 897 | } |
923 | | | 898 | |
924 | /* At this point, the pattern does not contain '{'. */ | | | |
925 | | | | |
926 | slash = strchr(pattern, '/'); | | 899 | slash = strchr(pattern, '/'); |
927 | if (slash == NULL) { | | 900 | if (slash == NULL) { |
928 | /* The pattern has no directory component. */ | | | |
929 | | | | |
930 | /* First the files in dot. */ | | 901 | /* First the files in dot. */ |
931 | DirMatchFiles(pattern, dot, expansions); | | 902 | DirMatchFiles(pattern, dot, expansions); |
932 | /* Then the files in every other directory on the path. */ | | 903 | /* Then the files in every other directory on the path. */ |
933 | DirExpandPath(pattern, path, expansions); | | 904 | DirExpandPath(pattern, path, expansions); |
934 | goto done; | | 905 | goto done; |
935 | } | | 906 | } |
936 | | | 907 | |
937 | /* At this point, the pattern has a directory component. */ | | 908 | /* At this point, the pattern has a directory component. */ |
938 | | | 909 | |
939 | /* Find the first wildcard in the pattern. */ | | 910 | /* Find the first wildcard in the pattern. */ |
940 | for (wildcard = pattern; *wildcard != '\0'; wildcard++) | | 911 | for (wildcard = pattern; *wildcard != '\0'; wildcard++) |
941 | if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') | | 912 | if (*wildcard == '?' || *wildcard == '[' || *wildcard == '*') |
942 | break; | | 913 | break; |
| @@ -962,95 +933,95 @@ SearchPath_Expand(SearchPath *path, cons | | | @@ -962,95 +933,95 @@ SearchPath_Expand(SearchPath *path, cons |
962 | /* Start the search from the local directory */ | | 933 | /* Start the search from the local directory */ |
963 | DirExpandPath(pattern, path, expansions); | | 934 | DirExpandPath(pattern, path, expansions); |
964 | } else { | | 935 | } else { |
965 | SearchPath_ExpandMiddle(path, pattern, wildcardComponent, | | 936 | SearchPath_ExpandMiddle(path, pattern, wildcardComponent, |
966 | expansions); | | 937 | expansions); |
967 | } | | 938 | } |
968 | | | 939 | |
969 | done: | | 940 | done: |
970 | if (DEBUG(DIR)) | | 941 | if (DEBUG(DIR)) |
971 | PrintExpansions(expansions); | | 942 | PrintExpansions(expansions); |
972 | } | | 943 | } |
973 | | | 944 | |
974 | /* | | 945 | /* |
975 | * Find if the file with the given name exists in the given path. | | 946 | * Find if 'base' exists in 'dir'. |
976 | * Return the freshly allocated path to the file, or NULL. | | 947 | * Return the freshly allocated path to the file, or NULL. |
977 | */ | | 948 | */ |
978 | static char * | | 949 | static char * |
979 | DirLookup(CachedDir *dir, const char *base) | | 950 | DirLookup(CachedDir *dir, const char *base) |
980 | { | | 951 | { |
981 | char *file; /* the current filename to check */ | | 952 | char *file; |
982 | | | 953 | |
983 | DEBUG1(DIR, " %s ...\n", dir->name); | | 954 | DEBUG1(DIR, " %s ...\n", dir->name); |
984 | | | 955 | |
985 | if (!HashSet_Contains(&dir->files, base)) | | 956 | if (!HashSet_Contains(&dir->files, base)) |
986 | return NULL; | | 957 | return NULL; |
987 | | | 958 | |
988 | file = str_concat3(dir->name, "/", base); | | 959 | file = str_concat3(dir->name, "/", base); |
989 | DEBUG1(DIR, " returning %s\n", file); | | 960 | DEBUG1(DIR, " returning %s\n", file); |
990 | dir->hits++; | | 961 | dir->hits++; |
991 | hits++; | | 962 | hits++; |
992 | return file; | | 963 | return file; |
993 | } | | 964 | } |
994 | | | 965 | |
995 | | | 966 | |
996 | /* | | 967 | /* |
997 | * Find if the file with the given name exists in the given directory. | | 968 | * Find if 'name' exists in 'dir'. |
998 | * Return the freshly allocated path to the file, or NULL. | | 969 | * Return the freshly allocated path to the file, or NULL. |
999 | */ | | 970 | */ |
1000 | static char * | | 971 | static char * |
1001 | DirLookupSubdir(CachedDir *dir, const char *name) | | 972 | DirLookupSubdir(CachedDir *dir, const char *name) |
1002 | { | | 973 | { |
1003 | struct cached_stat cst; | | 974 | struct cached_stat cst; |
1004 | char *file = dir == dot | | 975 | char *file = dir == dot |
1005 | ? bmake_strdup(name) | | 976 | ? bmake_strdup(name) |
1006 | : str_concat3(dir->name, "/", name); | | 977 | : str_concat3(dir->name, "/", name); |
1007 | | | 978 | |
1008 | DEBUG1(DIR, "checking %s ...\n", file); | | 979 | DEBUG1(DIR, "checking %s ...\n", file); |
1009 | | | 980 | |
1010 | if (cached_stat(file, &cst) == 0) { | | 981 | if (cached_stat(file, &cst) == 0) { |
1011 | nearmisses++; | | 982 | nearmisses++; |
1012 | return file; | | 983 | return file; |
1013 | } | | 984 | } |
1014 | free(file); | | 985 | free(file); |
1015 | return NULL; | | 986 | return NULL; |
1016 | } | | 987 | } |
1017 | | | 988 | |
1018 | /* | | 989 | /* |
1019 | * Find if the file with the given name exists in the given path. | | 990 | * Find if 'name' (which has basename 'base') exists in 'dir'. |
1020 | * Return the freshly allocated path to the file, the empty string, or NULL. | | 991 | * Return the freshly allocated path to the file, an empty string, or NULL. |
1021 | * Returning the empty string means that the search should be terminated. | | 992 | * Returning an empty string means that the search should be terminated. |
1022 | */ | | 993 | */ |
1023 | static char * | | 994 | static char * |
1024 | DirLookupAbs(CachedDir *dir, const char *name, const char *cp) | | 995 | DirLookupAbs(CachedDir *dir, const char *name, const char *base) |
1025 | { | | 996 | { |
1026 | const char *dnp; /* pointer into dir->name */ | | 997 | const char *dnp; /* pointer into dir->name */ |
1027 | const char *np; /* pointer into name */ | | 998 | const char *np; /* pointer into name */ |
1028 | | | 999 | |
1029 | DEBUG1(DIR, " %s ...\n", dir->name); | | 1000 | DEBUG1(DIR, " %s ...\n", dir->name); |
1030 | | | 1001 | |
1031 | /* | | 1002 | /* |
1032 | * If the file has a leading path component and that component | | 1003 | * If the file has a leading path component and that component |
1033 | * exactly matches the entire name of the current search | | 1004 | * exactly matches the entire name of the current search |
1034 | * directory, we can attempt another cache lookup. And if we don't | | 1005 | * directory, we can attempt another cache lookup. And if we don't |
1035 | * have a hit, we can safely assume the file does not exist at all. | | 1006 | * have a hit, we can safely assume the file does not exist at all. |
1036 | */ | | 1007 | */ |
1037 | for (dnp = dir->name, np = name; | | 1008 | for (dnp = dir->name, np = name; |
1038 | *dnp != '\0' && *dnp == *np; dnp++, np++) | | 1009 | *dnp != '\0' && *dnp == *np; dnp++, np++) |
1039 | continue; | | 1010 | continue; |
1040 | if (*dnp != '\0' || np != cp - 1) | | 1011 | if (*dnp != '\0' || np != base - 1) |
1041 | return NULL; | | 1012 | return NULL; |
1042 | | | 1013 | |
1043 | if (!HashSet_Contains(&dir->files, cp)) { | | 1014 | if (!HashSet_Contains(&dir->files, base)) { |
1044 | DEBUG0(DIR, " must be here but isn't -- returning\n"); | | 1015 | DEBUG0(DIR, " must be here but isn't -- returning\n"); |
1045 | return bmake_strdup(""); /* to terminate the search */ | | 1016 | return bmake_strdup(""); /* to terminate the search */ |
1046 | } | | 1017 | } |
1047 | | | 1018 | |
1048 | dir->hits++; | | 1019 | dir->hits++; |
1049 | hits++; | | 1020 | hits++; |
1050 | DEBUG1(DIR, " returning %s\n", name); | | 1021 | DEBUG1(DIR, " returning %s\n", name); |
1051 | return bmake_strdup(name); | | 1022 | return bmake_strdup(name); |
1052 | } | | 1023 | } |
1053 | | | 1024 | |
1054 | /* | | 1025 | /* |
1055 | * Find the given file in "." or curdir. | | 1026 | * Find the given file in "." or curdir. |
1056 | * Return the freshly allocated path to the file, or NULL. | | 1027 | * Return the freshly allocated path to the file, or NULL. |
| @@ -1080,91 +1051,81 @@ static bool | | | @@ -1080,91 +1051,81 @@ static bool |
1080 | FindFileRelative(SearchPath *path, bool seenDotLast, | | 1051 | FindFileRelative(SearchPath *path, bool seenDotLast, |
1081 | const char *name, char **out_file) | | 1052 | const char *name, char **out_file) |
1082 | { | | 1053 | { |
1083 | SearchPathNode *ln; | | 1054 | SearchPathNode *ln; |
1084 | bool checkedDot = false; | | 1055 | bool checkedDot = false; |
1085 | char *file; | | 1056 | char *file; |
1086 | | | 1057 | |
1087 | DEBUG0(DIR, " Trying subdirectories...\n"); | | 1058 | DEBUG0(DIR, " Trying subdirectories...\n"); |
1088 | | | 1059 | |
1089 | if (!seenDotLast) { | | 1060 | if (!seenDotLast) { |
1090 | if (dot != NULL) { | | 1061 | if (dot != NULL) { |
1091 | checkedDot = true; | | 1062 | checkedDot = true; |
1092 | if ((file = DirLookupSubdir(dot, name)) != NULL) | | 1063 | if ((file = DirLookupSubdir(dot, name)) != NULL) |
1093 | goto found; | | 1064 | goto done; |
1094 | } | | 1065 | } |
1095 | if (cur != NULL && | | 1066 | if (cur != NULL && |
1096 | (file = DirLookupSubdir(cur, name)) != NULL) | | 1067 | (file = DirLookupSubdir(cur, name)) != NULL) |
1097 | goto found; | | 1068 | goto done; |
1098 | } | | 1069 | } |
1099 | | | 1070 | |
1100 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { | | 1071 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { |
1101 | CachedDir *dir = ln->datum; | | 1072 | CachedDir *dir = ln->datum; |
1102 | if (dir == dotLast) | | 1073 | if (dir == dotLast) |
1103 | continue; | | 1074 | continue; |
1104 | if (dir == dot) { | | 1075 | if (dir == dot) { |
1105 | if (checkedDot) | | 1076 | if (checkedDot) |
1106 | continue; | | 1077 | continue; |
1107 | checkedDot = true; | | 1078 | checkedDot = true; |
1108 | } | | 1079 | } |
1109 | if ((file = DirLookupSubdir(dir, name)) != NULL) | | 1080 | if ((file = DirLookupSubdir(dir, name)) != NULL) |
1110 | goto found; | | 1081 | goto done; |
1111 | } | | 1082 | } |
1112 | | | 1083 | |
1113 | if (seenDotLast) { | | 1084 | if (seenDotLast) { |
1114 | if (dot != NULL && !checkedDot) { | | 1085 | if (dot != NULL && !checkedDot) { |
1115 | checkedDot = true; | | 1086 | checkedDot = true; |
1116 | if ((file = DirLookupSubdir(dot, name)) != NULL) | | 1087 | if ((file = DirLookupSubdir(dot, name)) != NULL) |
1117 | goto found; | | 1088 | goto done; |
1118 | } | | 1089 | } |
1119 | if (cur != NULL && | | 1090 | if (cur != NULL && |
1120 | (file = DirLookupSubdir(cur, name)) != NULL) | | 1091 | (file = DirLookupSubdir(cur, name)) != NULL) |
1121 | goto found; | | 1092 | goto done; |
1122 | } | | 1093 | } |
1123 | | | 1094 | |
1124 | if (checkedDot) { | | 1095 | if (checkedDot) { |
1125 | /* | | 1096 | /* |
1126 | * Already checked by the given name, since . was in | | 1097 | * Already checked by the given name, since . was in |
1127 | * the path, so no point in proceeding. | | 1098 | * the path, so no point in proceeding. |
1128 | */ | | 1099 | */ |
1129 | DEBUG0(DIR, " Checked . already, returning NULL\n"); | | 1100 | DEBUG0(DIR, " Checked . already, returning NULL\n"); |
1130 | file = NULL; | | 1101 | file = NULL; |
1131 | goto found; | | 1102 | goto done; |
1132 | } | | 1103 | } |
1133 | | | 1104 | |
1134 | return false; | | 1105 | return false; |
1135 | | | 1106 | |
1136 | found: | | 1107 | done: |
1137 | *out_file = file; | | 1108 | *out_file = file; |
1138 | return true; | | 1109 | return true; |
1139 | } | | 1110 | } |
1140 | | | 1111 | |
1141 | static bool | | 1112 | static bool |
1142 | FindFileAbsolute(SearchPath *path, bool seenDotLast, | | 1113 | FindFileAbsolute(SearchPath *path, bool seenDotLast, |
1143 | const char *name, const char *base, char **out_file) | | 1114 | const char *name, const char *base, char **out_file) |
1144 | { | | 1115 | { |
1145 | char *file; | | 1116 | char *file; |
1146 | SearchPathNode *ln; | | 1117 | SearchPathNode *ln; |
1147 | | | 1118 | |
1148 | /* | | | |
1149 | * For absolute names, compare directory path prefix against | | | |
1150 | * the the directory path of each member on the search path | | | |
1151 | * for an exact match. If we have an exact match on any member | | | |
1152 | * of the search path, use the cached contents of that member | | | |
1153 | * to lookup the final file component. If that lookup fails we | | | |
1154 | * can safely assume that the file does not exist at all. | | | |
1155 | * This is signified by DirLookupAbs() returning an empty | | | |
1156 | * string. | | | |
1157 | */ | | | |
1158 | DEBUG0(DIR, " Trying exact path matches...\n"); | | 1119 | DEBUG0(DIR, " Trying exact path matches...\n"); |
1159 | | | 1120 | |
1160 | if (!seenDotLast && cur != NULL && | | 1121 | if (!seenDotLast && cur != NULL && |
1161 | ((file = DirLookupAbs(cur, name, base)) != NULL)) | | 1122 | ((file = DirLookupAbs(cur, name, base)) != NULL)) |
1162 | goto found; | | 1123 | goto found; |
1163 | | | 1124 | |
1164 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { | | 1125 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { |
1165 | CachedDir *dir = ln->datum; | | 1126 | CachedDir *dir = ln->datum; |
1166 | if (dir == dotLast) | | 1127 | if (dir == dotLast) |
1167 | continue; | | 1128 | continue; |
1168 | if ((file = DirLookupAbs(dir, name, base)) != NULL) | | 1129 | if ((file = DirLookupAbs(dir, name, base)) != NULL) |
1169 | goto found; | | 1130 | goto found; |
1170 | } | | 1131 | } |
| @@ -1177,46 +1138,39 @@ FindFileAbsolute(SearchPath *path, bool | | | @@ -1177,46 +1138,39 @@ FindFileAbsolute(SearchPath *path, bool |
1177 | | | 1138 | |
1178 | found: | | 1139 | found: |
1179 | if (file[0] == '\0') { | | 1140 | if (file[0] == '\0') { |
1180 | free(file); | | 1141 | free(file); |
1181 | file = NULL; | | 1142 | file = NULL; |
1182 | } | | 1143 | } |
1183 | *out_file = file; | | 1144 | *out_file = file; |
1184 | return true; | | 1145 | return true; |
1185 | } | | 1146 | } |
1186 | | | 1147 | |
1187 | /* | | 1148 | /* |
1188 | * Find the file with the given name along the given search path. | | 1149 | * Find the file with the given name along the given search path. |
1189 | * | | 1150 | * |
1190 | * If the file is found in a directory that is not on the path | | | |
1191 | * already (either 'name' is absolute or it is a relative path | | | |
1192 | * [ dir1/.../dirn/file ] which exists below one of the directories | | | |
1193 | * already on the search path), its directory is added to the end | | | |
1194 | * of the path, on the assumption that there will be more files in | | | |
1195 | * that directory later on. Sometimes this is true. Sometimes not. | | | |
1196 | * | | | |
1197 | * Input: | | 1151 | * Input: |
1198 | * name the file to find | | 1152 | * name the file to find |
1199 | * path the directories to search, or NULL | | 1153 | * path the directories to search, or NULL |
1200 | * | | 1154 | * |
1201 | * Results: | | 1155 | * Results: |
1202 | * The freshly allocated path to the file, or NULL. | | 1156 | * The freshly allocated path to the file, or NULL. |
1203 | */ | | 1157 | */ |
1204 | char * | | 1158 | char * |
1205 | Dir_FindFile(const char *name, SearchPath *path) | | 1159 | Dir_FindFile(const char *name, SearchPath *path) |
1206 | { | | 1160 | { |
1207 | char *file; /* the current filename to check */ | | 1161 | char *file; /* the current filename to check */ |
1208 | bool seenDotLast = false; /* true if we should search dot last */ | | 1162 | bool seenDotLast = false; /* true if we should search dot last */ |
1209 | struct cached_stat cst; /* Buffer for stat, if necessary */ | | 1163 | struct cached_stat cst; |
1210 | const char *trailing_dot = "."; | | 1164 | const char *trailing_dot = "."; |
1211 | const char *base = str_basename(name); | | 1165 | const char *base = str_basename(name); |
1212 | | | 1166 | |
1213 | DEBUG1(DIR, "Searching for %s ...", name); | | 1167 | DEBUG1(DIR, "Searching for %s ...", name); |
1214 | | | 1168 | |
1215 | if (path == NULL) { | | 1169 | if (path == NULL) { |
1216 | DEBUG0(DIR, "couldn't open path, file not found\n"); | | 1170 | DEBUG0(DIR, "couldn't open path, file not found\n"); |
1217 | misses++; | | 1171 | misses++; |
1218 | return NULL; | | 1172 | return NULL; |
1219 | } | | 1173 | } |
1220 | | | 1174 | |
1221 | if (path->dirs.first != NULL) { | | 1175 | if (path->dirs.first != NULL) { |
1222 | CachedDir *dir = path->dirs.first->datum; | | 1176 | CachedDir *dir = path->dirs.first->datum; |
| @@ -1226,196 +1180,128 @@ Dir_FindFile(const char *name, SearchPat | | | @@ -1226,196 +1180,128 @@ Dir_FindFile(const char *name, SearchPat |
1226 | } | | 1180 | } |
1227 | } | | 1181 | } |
1228 | DEBUG0(DIR, "\n"); | | 1182 | DEBUG0(DIR, "\n"); |
1229 | | | 1183 | |
1230 | /* | | 1184 | /* |
1231 | * If there's no leading directory components or if the leading | | 1185 | * If there's no leading directory components or if the leading |
1232 | * directory component is exactly `./', consult the cached contents | | 1186 | * directory component is exactly `./', consult the cached contents |
1233 | * of each of the directories on the search path. | | 1187 | * of each of the directories on the search path. |
1234 | */ | | 1188 | */ |
1235 | if (base == name || (base - name == 2 && *name == '.')) { | | 1189 | if (base == name || (base - name == 2 && *name == '.')) { |
1236 | SearchPathNode *ln; | | 1190 | SearchPathNode *ln; |
1237 | | | 1191 | |
1238 | /* | | 1192 | /* |
1239 | * We look through all the directories on the path seeking one | | 1193 | * Look through all the directories on the path seeking one |
1240 | * which contains the final component of the given name. If | | 1194 | * which contains the final component of the given name. If |
1241 | * such a file is found, we concatenate the directory name | | 1195 | * such a file is found, return its pathname. |
1242 | * and the final component and return the resulting string. | | 1196 | * If there is no such file, go on to phase two. |
1243 | * If we don't find any such thing, we go on to phase two. | | | |
1244 | * | | 1197 | * |
1245 | * No matter what, we always look for the file in the current | | 1198 | * No matter what, always look for the file in the current |
1246 | * directory before anywhere else (unless we found the magic | | 1199 | * directory before anywhere else (unless the path contains |
1247 | * DOTLAST path, in which case we search it last) and we *do | | 1200 | * the magic '.DOTLAST', in which case search it last). |
1248 | * not* add the ./ to it if it exists. | | | |
1249 | * This is so there are no conflicts between what the user | | 1201 | * This is so there are no conflicts between what the user |
1250 | * specifies (fish.c) and what pmake finds (./fish.c). | | 1202 | * specifies (fish.c) and what make finds (./fish.c). |
1251 | */ | | 1203 | */ |
1252 | if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) | | 1204 | if (!seenDotLast && (file = DirFindDot(name, base)) != NULL) |
1253 | return file; | | 1205 | return file; |
1254 | | | 1206 | |
1255 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { | | 1207 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { |
1256 | CachedDir *dir = ln->datum; | | 1208 | CachedDir *dir = ln->datum; |
1257 | if (dir == dotLast) | | 1209 | if (dir == dotLast) |
1258 | continue; | | 1210 | continue; |
1259 | if ((file = DirLookup(dir, base)) != NULL) | | 1211 | if ((file = DirLookup(dir, base)) != NULL) |
1260 | return file; | | 1212 | return file; |
1261 | } | | 1213 | } |
1262 | | | 1214 | |
1263 | if (seenDotLast && (file = DirFindDot(name, base)) != NULL) | | 1215 | if (seenDotLast && (file = DirFindDot(name, base)) != NULL) |
1264 | return file; | | 1216 | return file; |
1265 | } | | 1217 | } |
1266 | | | 1218 | |
1267 | /* | | | |
1268 | * We didn't find the file on any directory in the search path. | | | |
1269 | * If the name doesn't contain a slash, that means it doesn't exist. | | | |
1270 | * If it *does* contain a slash, however, there is still hope: it | | | |
1271 | * could be in a subdirectory of one of the members of the search | | | |
1272 | * path. (eg. /usr/include and sys/types.h. The above search would | | | |
1273 | * fail to turn up types.h in /usr/include, but it *is* in | | | |
1274 | * /usr/include/sys/types.h). | | | |
1275 | * [ This no longer applies: If we find such a file, we assume there | | | |
1276 | * will be more (what else can we assume?) and add all but the last | | | |
1277 | * component of the resulting name onto the search path (at the | | | |
1278 | * end).] | | | |
1279 | * This phase is only performed if the file is *not* absolute. | | | |
1280 | */ | | | |
1281 | if (base == name) { | | 1219 | if (base == name) { |
1282 | DEBUG0(DIR, " failed.\n"); | | 1220 | DEBUG0(DIR, " failed.\n"); |
1283 | misses++; | | 1221 | misses++; |
1284 | return NULL; | | 1222 | return NULL; |
1285 | } | | 1223 | } |
1286 | | | 1224 | |
1287 | if (*base == '\0') { | | 1225 | if (*base == '\0') |
1288 | /* we were given a trailing "/" */ | | 1226 | base = trailing_dot; /* we were given a trailing "/" */ |
1289 | base = trailing_dot; | | | |
1290 | } | | | |
1291 | | | 1227 | |
1292 | if (name[0] != '/') { | | 1228 | if (name[0] != '/') { |
1293 | if (FindFileRelative(path, seenDotLast, name, &file)) | | 1229 | if (FindFileRelative(path, seenDotLast, name, &file)) |
1294 | return file; | | 1230 | return file; |
1295 | } else { | | 1231 | } else { |
1296 | if (FindFileAbsolute(path, seenDotLast, name, base, &file)) | | 1232 | if (FindFileAbsolute(path, seenDotLast, name, base, &file)) |
1297 | return file; | | 1233 | return file; |
1298 | } | | 1234 | } |
1299 | | | 1235 | |
1300 | /* | | 1236 | /* |
1301 | * Didn't find it that way, either. Sigh. Phase 3. Add its directory | | 1237 | * We cannot add the directory onto the search path because |
1302 | * onto the search path in any case, just in case, then look for the | | | |
1303 | * thing in the hash table. If we find it, grand. We return a new | | | |
1304 | * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. | | | |
1305 | * Note that if the directory holding the file doesn't exist, this | | | |
1306 | * will do an extra search of the final directory on the path. Unless | | | |
1307 | * something weird happens, this search won't succeed and life will | | | |
1308 | * be groovy. | | | |
1309 | * | | | |
1310 | * Sigh. We cannot add the directory onto the search path because | | | |
1311 | * of this amusing case: | | 1238 | * of this amusing case: |
1312 | * $(INSTALLDIR)/$(FILE): $(FILE) | | 1239 | * $(INSTALLDIR)/$(FILE): $(FILE) |
1313 | * | | 1240 | * |
1314 | * $(FILE) exists in $(INSTALLDIR) but not in the current one. | | 1241 | * $(FILE) exists in $(INSTALLDIR) but not in the current one. |
1315 | * When searching for $(FILE), we will find it in $(INSTALLDIR) | | 1242 | * When searching for $(FILE), we will find it in $(INSTALLDIR) |
1316 | * b/c we added it here. This is not good... | | 1243 | * b/c we added it here. This is not good... |
1317 | */ | | 1244 | */ |
1318 | #if 0 | | 1245 | |
1319 | { | | | |
1320 | CachedDir *dir; | | | |
1321 | char *prefix; | | | |
1322 | | | | |
1323 | if (base == trailing_dot) { | | | |
1324 | base = strrchr(name, '/'); | | | |
1325 | base++; | | | |
1326 | } | | | |
1327 | prefix = bmake_strsedup(name, base - 1); | | | |
1328 | (void)SearchPath_Add(path, prefix); | | | |
1329 | free(prefix); | | | |
1330 | | | | |
1331 | bigmisses++; | | | |
1332 | if (path->last == NULL) | | | |
1333 | return NULL; | | | |
1334 | | | | |
1335 | dir = path->last->datum; | | | |
1336 | if (HashSet_Contains(&dir->files, base)) | | | |
1337 | return bmake_strdup(name); | | | |
1338 | return NULL; | | | |
1339 | } | | | |
1340 | #else | | | |
1341 | DEBUG1(DIR, " Looking for \"%s\" ...\n", name); | | 1246 | DEBUG1(DIR, " Looking for \"%s\" ...\n", name); |
1342 | | | 1247 | |
1343 | bigmisses++; | | 1248 | bigmisses++; |
1344 | if (cached_stat(name, &cst) == 0) { | | 1249 | if (cached_stat(name, &cst) == 0) |
1345 | return bmake_strdup(name); | | 1250 | return bmake_strdup(name); |
1346 | } | | | |
1347 | | | 1251 | |
1348 | DEBUG0(DIR, " failed. Returning NULL\n"); | | 1252 | DEBUG0(DIR, " failed. Returning NULL\n"); |
1349 | return NULL; | | 1253 | return NULL; |
1350 | #endif | | | |
1351 | } | | 1254 | } |
1352 | | | 1255 | |
1353 | | | 1256 | |
1354 | /* | | 1257 | /* |
1355 | * Search for a path starting at a given directory and then working our way | | 1258 | * Search for 'needle' starting at the directory 'here' and then working our |
1356 | * up towards the root. | | 1259 | * way up towards the root directory. Return the allocated path, or NULL. |
1357 | * | | | |
1358 | * Input: | | | |
1359 | * here starting directory | | | |
1360 | * search_path the relative path we are looking for | | | |
1361 | * | | | |
1362 | * Results: | | | |
1363 | * The found path, or NULL. | | | |
1364 | */ | | 1260 | */ |
1365 | char * | | 1261 | char * |
1366 | Dir_FindHereOrAbove(const char *here, const char *search_path) | | 1262 | Dir_FindHereOrAbove(const char *here, const char *needle) |
1367 | { | | 1263 | { |
1368 | struct cached_stat cst; | | 1264 | struct cached_stat cst; |
1369 | char *dirbase, *dirbase_end; | | 1265 | char *dirbase, *dirbase_end; |
1370 | char *try, *try_end; | | 1266 | char *try, *try_end; |
1371 | | | 1267 | |
1372 | /* copy out our starting point */ | | | |
1373 | dirbase = bmake_strdup(here); | | 1268 | dirbase = bmake_strdup(here); |
1374 | dirbase_end = dirbase + strlen(dirbase); | | 1269 | dirbase_end = dirbase + strlen(dirbase); |
1375 | | | 1270 | |
1376 | /* loop until we determine a result */ | | | |
1377 | for (;;) { | | 1271 | for (;;) { |
1378 | | | 1272 | try = str_concat3(dirbase, "/", needle); |
1379 | /* try and stat(2) it ... */ | | | |
1380 | try = str_concat3(dirbase, "/", search_path); | | | |
1381 | if (cached_stat(try, &cst) != -1) { | | 1273 | if (cached_stat(try, &cst) != -1) { |
1382 | /* | | | |
1383 | * success! if we found a file, chop off | | | |
1384 | * the filename so we return a directory. | | | |
1385 | */ | | | |
1386 | if ((cst.cst_mode & S_IFMT) != S_IFDIR) { | | 1274 | if ((cst.cst_mode & S_IFMT) != S_IFDIR) { |
| | | 1275 | /* |
| | | 1276 | * Chop off the filename, to return a |
| | | 1277 | * directory. |
| | | 1278 | */ |
1387 | try_end = try + strlen(try); | | 1279 | try_end = try + strlen(try); |
1388 | while (try_end > try && *try_end != '/') | | 1280 | while (try_end > try && *try_end != '/') |
1389 | try_end--; | | 1281 | try_end--; |
1390 | if (try_end > try) | | 1282 | if (try_end > try) |
1391 | *try_end = '\0'; /* chop! */ | | 1283 | *try_end = '\0'; /* chop! */ |
1392 | } | | 1284 | } |
1393 | | | 1285 | |
1394 | free(dirbase); | | 1286 | free(dirbase); |
1395 | return try; | | 1287 | return try; |
1396 | } | | 1288 | } |
1397 | free(try); | | 1289 | free(try); |
1398 | | | 1290 | |
1399 | /* | | | |
1400 | * nope, we didn't find it. if we used up dirbase we've | | | |
1401 | * reached the root and failed. | | | |
1402 | */ | | | |
1403 | if (dirbase_end == dirbase) | | 1291 | if (dirbase_end == dirbase) |
1404 | break; /* failed! */ | | 1292 | break; /* failed! */ |
1405 | | | 1293 | |
1406 | /* | | 1294 | /* Truncate dirbase from the end to move up a dir. */ |
1407 | * truncate dirbase from the end to move up a dir | | | |
1408 | */ | | | |
1409 | while (dirbase_end > dirbase && *dirbase_end != '/') | | 1295 | while (dirbase_end > dirbase && *dirbase_end != '/') |
1410 | dirbase_end--; | | 1296 | dirbase_end--; |
1411 | *dirbase_end = '\0'; /* chop! */ | | 1297 | *dirbase_end = '\0'; /* chop! */ |
1412 | } | | 1298 | } |
1413 | | | 1299 | |
1414 | free(dirbase); | | 1300 | free(dirbase); |
1415 | return NULL; | | 1301 | return NULL; |
1416 | } | | 1302 | } |
1417 | | | 1303 | |
1418 | /* | | 1304 | /* |
1419 | * This is an implied source, and it may have moved, | | 1305 | * This is an implied source, and it may have moved, |
1420 | * see if we can find it via the current .PATH | | 1306 | * see if we can find it via the current .PATH |
1421 | */ | | 1307 | */ |
| @@ -1466,30 +1352,30 @@ ResolveFullName(GNode *gn) | | | @@ -1466,30 +1352,30 @@ ResolveFullName(GNode *gn) |
1466 | DEBUG2(DIR, "Found '%s' as '%s'\n", | | 1352 | DEBUG2(DIR, "Found '%s' as '%s'\n", |
1467 | gn->name, fullName != NULL ? fullName : "(not found)"); | | 1353 | gn->name, fullName != NULL ? fullName : "(not found)"); |
1468 | } | | 1354 | } |
1469 | | | 1355 | |
1470 | if (fullName == NULL) | | 1356 | if (fullName == NULL) |
1471 | fullName = bmake_strdup(gn->name); | | 1357 | fullName = bmake_strdup(gn->name); |
1472 | | | 1358 | |
1473 | /* XXX: Is every piece of memory freed as it should? */ | | 1359 | /* XXX: Is every piece of memory freed as it should? */ |
1474 | | | 1360 | |
1475 | return fullName; | | 1361 | return fullName; |
1476 | } | | 1362 | } |
1477 | | | 1363 | |
1478 | /* | | 1364 | /* |
1479 | * Search gn along dirSearchPath and store its modification time in gn->mtime. | | 1365 | * Search 'gn' along 'dirSearchPath' and store its modification time in |
1480 | * If no file is found, store 0 instead. | | 1366 | * 'gn->mtime'. If no file is found, store 0 instead. |
1481 | * | | 1367 | * |
1482 | * The found file is stored in gn->path, unless the node already had a path. | | 1368 | * The found file is stored in 'gn->path', unless the node already had a path. |
1483 | */ | | 1369 | */ |
1484 | void | | 1370 | void |
1485 | Dir_UpdateMTime(GNode *gn, bool forceRefresh) | | 1371 | Dir_UpdateMTime(GNode *gn, bool forceRefresh) |
1486 | { | | 1372 | { |
1487 | char *fullName; | | 1373 | char *fullName; |
1488 | struct cached_stat cst; | | 1374 | struct cached_stat cst; |
1489 | | | 1375 | |
1490 | if (gn->type & OP_ARCHV) { | | 1376 | if (gn->type & OP_ARCHV) { |
1491 | Arch_UpdateMTime(gn); | | 1377 | Arch_UpdateMTime(gn); |
1492 | return; | | 1378 | return; |
1493 | } | | 1379 | } |
1494 | | | 1380 | |
1495 | if (gn->type & OP_PHONY) { | | 1381 | if (gn->type & OP_PHONY) { |
| @@ -1552,34 +1438,34 @@ CacheNewDir(const char *name, SearchPath | | | @@ -1552,34 +1438,34 @@ CacheNewDir(const char *name, SearchPath |
1552 | (void)HashSet_Add(&dir->files, dp->d_name); | | 1438 | (void)HashSet_Add(&dir->files, dp->d_name); |
1553 | } | | 1439 | } |
1554 | (void)closedir(d); | | 1440 | (void)closedir(d); |
1555 | | | 1441 | |
1556 | OpenDirs_Add(&openDirs, dir); | | 1442 | OpenDirs_Add(&openDirs, dir); |
1557 | if (path != NULL) | | 1443 | if (path != NULL) |
1558 | Lst_Append(&path->dirs, CachedDir_Ref(dir)); | | 1444 | Lst_Append(&path->dirs, CachedDir_Ref(dir)); |
1559 | | | 1445 | |
1560 | DEBUG1(DIR, "Caching %s done\n", name); | | 1446 | DEBUG1(DIR, "Caching %s done\n", name); |
1561 | return dir; | | 1447 | return dir; |
1562 | } | | 1448 | } |
1563 | | | 1449 | |
1564 | /* | | 1450 | /* |
1565 | * Read the list of filenames in the directory and store the result | | 1451 | * Read the list of filenames in the directory 'name' and store the result |
1566 | * in openDirs. | | 1452 | * in 'openDirs'. |
1567 | * | | 1453 | * |
1568 | * If a path is given, append the directory to that path. | | 1454 | * If a search path is given, append the directory to that path. |
1569 | * | | 1455 | * |
1570 | * Input: | | 1456 | * Input: |
1571 | * path The path to which the directory should be | | 1457 | * path The path to which the directory should be |
1572 | * added, or NULL to only add the directory to openDirs | | 1458 | * added, or NULL to only add the directory to openDirs. |
1573 | * name The name of the directory to add. | | 1459 | * name The name of the directory to add. |
1574 | * The name is not normalized in any way. | | 1460 | * The name is not normalized in any way. |
1575 | * Output: | | 1461 | * Output: |
1576 | * result If no path is given and the directory exists, the | | 1462 | * result If no path is given and the directory exists, the |
1577 | * returned CachedDir has a reference count of 0. It | | 1463 | * returned CachedDir has a reference count of 0. It |
1578 | * must either be assigned to a variable using | | 1464 | * must either be assigned to a variable using |
1579 | * CachedDir_Assign or be appended to a SearchPath using | | 1465 | * CachedDir_Assign or be appended to a SearchPath using |
1580 | * Lst_Append and CachedDir_Ref. | | 1466 | * Lst_Append and CachedDir_Ref. |
1581 | */ | | 1467 | */ |
1582 | CachedDir * | | 1468 | CachedDir * |
1583 | SearchPath_Add(SearchPath *path, const char *name) | | 1469 | SearchPath_Add(SearchPath *path, const char *name) |
1584 | { | | 1470 | { |
1585 | | | 1471 | |
| @@ -1618,36 +1504,28 @@ Dir_CopyDirSearchPath(void) | | | @@ -1618,36 +1504,28 @@ Dir_CopyDirSearchPath(void) |
1618 | { | | 1504 | { |
1619 | SearchPath *path = SearchPath_New(); | | 1505 | SearchPath *path = SearchPath_New(); |
1620 | SearchPathNode *ln; | | 1506 | SearchPathNode *ln; |
1621 | for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { | | 1507 | for (ln = dirSearchPath.dirs.first; ln != NULL; ln = ln->next) { |
1622 | CachedDir *dir = ln->datum; | | 1508 | CachedDir *dir = ln->datum; |
1623 | Lst_Append(&path->dirs, CachedDir_Ref(dir)); | | 1509 | Lst_Append(&path->dirs, CachedDir_Ref(dir)); |
1624 | } | | 1510 | } |
1625 | return path; | | 1511 | return path; |
1626 | } | | 1512 | } |
1627 | | | 1513 | |
1628 | /* | | 1514 | /* |
1629 | * Make a string by taking all the directories in the given search path and | | 1515 | * Make a string by taking all the directories in the given search path and |
1630 | * preceding them by the given flag. Used by the suffix module to create | | 1516 | * preceding them by the given flag. Used by the suffix module to create |
1631 | * variables for compilers based on suffix search paths. | | 1517 | * variables for compilers based on suffix search paths. Note that there is no |
1632 | * | | 1518 | * space between the given flag and each directory. |
1633 | * Input: | | | |
1634 | * flag flag which should precede each directory | | | |
1635 | * path list of directories | | | |
1636 | * | | | |
1637 | * Results: | | | |
1638 | * The string mentioned above. Note that there is no space between the | | | |
1639 | * given flag and each directory. The empty string is returned if things | | | |
1640 | * don't go well. | | | |
1641 | */ | | 1519 | */ |
1642 | char * | | 1520 | char * |
1643 | SearchPath_ToFlags(SearchPath *path, const char *flag) | | 1521 | SearchPath_ToFlags(SearchPath *path, const char *flag) |
1644 | { | | 1522 | { |
1645 | Buffer buf; | | 1523 | Buffer buf; |
1646 | SearchPathNode *ln; | | 1524 | SearchPathNode *ln; |
1647 | | | 1525 | |
1648 | Buf_Init(&buf); | | 1526 | Buf_Init(&buf); |
1649 | | | 1527 | |
1650 | if (path != NULL) { | | 1528 | if (path != NULL) { |
1651 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { | | 1529 | for (ln = path->dirs.first; ln != NULL; ln = ln->next) { |
1652 | CachedDir *dir = ln->datum; | | 1530 | CachedDir *dir = ln->datum; |
1653 | Buf_AddStr(&buf, " "); | | 1531 | Buf_AddStr(&buf, " "); |
| @@ -1699,27 +1577,26 @@ SearchPath_AddAll(SearchPath *dst, Searc | | | @@ -1699,27 +1577,26 @@ SearchPath_AddAll(SearchPath *dst, Searc |
1699 | for (ln = src->dirs.first; ln != NULL; ln = ln->next) { | | 1577 | for (ln = src->dirs.first; ln != NULL; ln = ln->next) { |
1700 | CachedDir *dir = ln->datum; | | 1578 | CachedDir *dir = ln->datum; |
1701 | if (Lst_FindDatum(&dst->dirs, dir) == NULL) | | 1579 | if (Lst_FindDatum(&dst->dirs, dir) == NULL) |
1702 | Lst_Append(&dst->dirs, CachedDir_Ref(dir)); | | 1580 | Lst_Append(&dst->dirs, CachedDir_Ref(dir)); |
1703 | } | | 1581 | } |
1704 | } | | 1582 | } |
1705 | | | 1583 | |
1706 | static int | | 1584 | static int |
1707 | percentage(int num, int den) | | 1585 | percentage(int num, int den) |
1708 | { | | 1586 | { |
1709 | return den != 0 ? num * 100 / den : 0; | | 1587 | return den != 0 ? num * 100 / den : 0; |
1710 | } | | 1588 | } |
1711 | | | 1589 | |
1712 | /********** DEBUG INFO **********/ | | | |
1713 | void | | 1590 | void |
1714 | Dir_PrintDirectories(void) | | 1591 | Dir_PrintDirectories(void) |
1715 | { | | 1592 | { |
1716 | CachedDirListNode *ln; | | 1593 | CachedDirListNode *ln; |
1717 | | | 1594 | |
1718 | debug_printf("#*** Directory Cache:\n"); | | 1595 | debug_printf("#*** Directory Cache:\n"); |
1719 | debug_printf( | | 1596 | debug_printf( |
1720 | "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", | | 1597 | "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", |
1721 | hits, misses, nearmisses, bigmisses, | | 1598 | hits, misses, nearmisses, bigmisses, |
1722 | percentage(hits, hits + bigmisses + nearmisses)); | | 1599 | percentage(hits, hits + bigmisses + nearmisses)); |
1723 | debug_printf("# refs hits directory\n"); | | 1600 | debug_printf("# refs hits directory\n"); |
1724 | | | 1601 | |
1725 | for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { | | 1602 | for (ln = openDirs.list.first; ln != NULL; ln = ln->next) { |