| @@ -1,1812 +1,1815 @@ | | | @@ -1,1812 +1,1815 @@ |
1 | /* $NetBSD: dir.c,v 1.111 2020/08/26 22:55:46 rillig Exp $ */ | | 1 | /* $NetBSD: dir.c,v 1.112 2020/08/27 06:28:44 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. |
15 | * 2. Redistributions in binary form must reproduce the above copyright | | 15 | * 2. Redistributions in binary form must reproduce the above copyright |
16 | * notice, this list of conditions and the following disclaimer in the | | 16 | * notice, this list of conditions and the following disclaimer in the |
17 | * documentation and/or other materials provided with the distribution. | | 17 | * documentation and/or other materials provided with the distribution. |
18 | * 3. Neither the name of the University nor the names of its contributors | | 18 | * 3. Neither the name of the University nor the names of its contributors |
19 | * may be used to endorse or promote products derived from this software | | 19 | * may be used to endorse or promote products derived from this software |
20 | * without specific prior written permission. | | 20 | * without specific prior written permission. |
21 | * | | 21 | * |
22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 22 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 23 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 24 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 25 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 26 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 27 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 28 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 29 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 30 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 31 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
32 | * SUCH DAMAGE. | | 32 | * SUCH DAMAGE. |
33 | */ | | 33 | */ |
34 | | | 34 | |
35 | /* | | 35 | /* |
36 | * Copyright (c) 1988, 1989 by Adam de Boor | | 36 | * Copyright (c) 1988, 1989 by Adam de Boor |
37 | * Copyright (c) 1989 by Berkeley Softworks | | 37 | * Copyright (c) 1989 by Berkeley Softworks |
38 | * All rights reserved. | | 38 | * All rights reserved. |
39 | * | | 39 | * |
40 | * This code is derived from software contributed to Berkeley by | | 40 | * This code is derived from software contributed to Berkeley by |
41 | * Adam de Boor. | | 41 | * Adam de Boor. |
42 | * | | 42 | * |
43 | * Redistribution and use in source and binary forms, with or without | | 43 | * Redistribution and use in source and binary forms, with or without |
44 | * modification, are permitted provided that the following conditions | | 44 | * modification, are permitted provided that the following conditions |
45 | * are met: | | 45 | * are met: |
46 | * 1. Redistributions of source code must retain the above copyright | | 46 | * 1. Redistributions of source code must retain the above copyright |
47 | * notice, this list of conditions and the following disclaimer. | | 47 | * notice, this list of conditions and the following disclaimer. |
48 | * 2. Redistributions in binary form must reproduce the above copyright | | 48 | * 2. Redistributions in binary form must reproduce the above copyright |
49 | * notice, this list of conditions and the following disclaimer in the | | 49 | * notice, this list of conditions and the following disclaimer in the |
50 | * documentation and/or other materials provided with the distribution. | | 50 | * documentation and/or other materials provided with the distribution. |
51 | * 3. All advertising materials mentioning features or use of this software | | 51 | * 3. All advertising materials mentioning features or use of this software |
52 | * must display the following acknowledgement: | | 52 | * must display the following acknowledgement: |
53 | * This product includes software developed by the University of | | 53 | * This product includes software developed by the University of |
54 | * California, Berkeley and its contributors. | | 54 | * California, Berkeley and its contributors. |
55 | * 4. Neither the name of the University nor the names of its contributors | | 55 | * 4. Neither the name of the University nor the names of its contributors |
56 | * may be used to endorse or promote products derived from this software | | 56 | * may be used to endorse or promote products derived from this software |
57 | * without specific prior written permission. | | 57 | * without specific prior written permission. |
58 | * | | 58 | * |
59 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND | | 59 | * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND |
60 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE | | 60 | * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
61 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE | | 61 | * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
62 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE | | 62 | * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE |
63 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL | | 63 | * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
64 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS | | 64 | * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
65 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) | | 65 | * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
66 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT | | 66 | * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
67 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY | | 67 | * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
68 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF | | 68 | * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
69 | * SUCH DAMAGE. | | 69 | * SUCH DAMAGE. |
70 | */ | | 70 | */ |
71 | | | 71 | |
72 | #ifndef MAKE_NATIVE | | 72 | #ifndef MAKE_NATIVE |
73 | static char rcsid[] = "$NetBSD: dir.c,v 1.111 2020/08/26 22:55:46 rillig Exp $"; | | 73 | static char rcsid[] = "$NetBSD: dir.c,v 1.112 2020/08/27 06:28:44 rillig Exp $"; |
74 | #else | | 74 | #else |
75 | #include <sys/cdefs.h> | | 75 | #include <sys/cdefs.h> |
76 | #ifndef lint | | 76 | #ifndef lint |
77 | #if 0 | | 77 | #if 0 |
78 | static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; | | 78 | static char sccsid[] = "@(#)dir.c 8.2 (Berkeley) 1/2/94"; |
79 | #else | | 79 | #else |
80 | __RCSID("$NetBSD: dir.c,v 1.111 2020/08/26 22:55:46 rillig Exp $"); | | 80 | __RCSID("$NetBSD: dir.c,v 1.112 2020/08/27 06:28:44 rillig Exp $"); |
81 | #endif | | 81 | #endif |
82 | #endif /* not lint */ | | 82 | #endif /* not lint */ |
83 | #endif | | 83 | #endif |
84 | | | 84 | |
85 | /*- | | 85 | /*- |
86 | * dir.c -- | | 86 | * dir.c -- |
87 | * Directory searching using wildcards and/or normal names... | | 87 | * Directory searching using wildcards and/or normal names... |
88 | * Used both for source wildcarding in the Makefile and for finding | | 88 | * Used both for source wildcarding in the Makefile and for finding |
89 | * implicit sources. | | 89 | * implicit sources. |
90 | * | | 90 | * |
91 | * The interface for this module is: | | 91 | * The interface for this module is: |
92 | * Dir_Init Initialize the module. | | 92 | * Dir_Init Initialize the module. |
93 | * | | 93 | * |
94 | * Dir_InitCur Set the cur Path. | | 94 | * Dir_InitCur Set the cur Path. |
95 | * | | 95 | * |
96 | * Dir_InitDot Set the dot Path. | | 96 | * Dir_InitDot Set the dot Path. |
97 | * | | 97 | * |
98 | * Dir_End Cleanup the module. | | 98 | * Dir_End Cleanup the module. |
99 | * | | 99 | * |
100 | * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. | | 100 | * Dir_SetPATH Set ${.PATH} to reflect state of dirSearchPath. |
101 | * | | 101 | * |
102 | * Dir_HasWildcards Returns TRUE if the name given it needs to | | 102 | * Dir_HasWildcards Returns TRUE if the name given it needs to |
103 | * be wildcard-expanded. | | 103 | * be wildcard-expanded. |
104 | * | | 104 | * |
105 | * Dir_Expand Given a pattern and a path, return a Lst of names | | 105 | * Dir_Expand Given a pattern and a path, return a Lst of names |
106 | * which match the pattern on the search path. | | 106 | * which match the pattern on the search path. |
107 | * | | 107 | * |
108 | * Dir_FindFile Searches for a file on a given search path. | | 108 | * Dir_FindFile Searches for a file on a given search path. |
109 | * If it exists, the entire path is returned. | | 109 | * If it exists, the entire path is returned. |
110 | * Otherwise NULL is returned. | | 110 | * Otherwise NULL is returned. |
111 | * | | 111 | * |
112 | * Dir_FindHereOrAbove Search for a path in the current directory and | | 112 | * Dir_FindHereOrAbove Search for a path in the current directory and |
113 | * then all the directories above it in turn until | | 113 | * then all the directories above it in turn until |
114 | * the path is found or we reach the root ("/"). | | 114 | * the path is found or we reach the root ("/"). |
115 | * | | 115 | * |
116 | * Dir_MTime Return the modification time of a node. The file | | 116 | * Dir_MTime Return the modification time of a node. The file |
117 | * is searched for along the default search path. | | 117 | * is searched for along the default search path. |
118 | * The path and mtime fields of the node are filled | | 118 | * The path and mtime fields of the node are filled |
119 | * in. | | 119 | * in. |
120 | * | | 120 | * |
121 | * Dir_AddDir Add a directory to a search path. | | 121 | * Dir_AddDir Add a directory to a search path. |
122 | * | | 122 | * |
123 | * Dir_MakeFlags Given a search path and a command flag, create | | 123 | * Dir_MakeFlags Given a search path and a command flag, create |
124 | * a string with each of the directories in the path | | 124 | * a string with each of the directories in the path |
125 | * preceded by the command flag and all of them | | 125 | * preceded by the command flag and all of them |
126 | * separated by a space. | | 126 | * separated by a space. |
127 | * | | 127 | * |
128 | * Dir_Destroy Destroy an element of a search path. Frees up all | | 128 | * Dir_Destroy Destroy an element of a search path. Frees up all |
129 | * things that can be freed for the element as long | | 129 | * things that can be freed for the element as long |
130 | * as the element is no longer referenced by any other | | 130 | * as the element is no longer referenced by any other |
131 | * search path. | | 131 | * search path. |
132 | * Dir_ClearPath Resets a search path to the empty list. | | 132 | * Dir_ClearPath Resets a search path to the empty list. |
133 | * | | 133 | * |
134 | * For debugging: | | 134 | * For debugging: |
135 | * Dir_PrintDirectories Print stats about the directory cache. | | 135 | * Dir_PrintDirectories Print stats about the directory cache. |
136 | */ | | 136 | */ |
137 | | | 137 | |
138 | #include <sys/types.h> | | 138 | #include <sys/types.h> |
139 | #include <sys/stat.h> | | 139 | #include <sys/stat.h> |
140 | | | 140 | |
141 | #include <dirent.h> | | 141 | #include <dirent.h> |
142 | #include <errno.h> | | 142 | #include <errno.h> |
143 | #include <stdio.h> | | 143 | #include <stdio.h> |
144 | | | 144 | |
145 | #include "make.h" | | 145 | #include "make.h" |
146 | #include "dir.h" | | 146 | #include "dir.h" |
147 | #include "job.h" | | 147 | #include "job.h" |
148 | | | 148 | |
149 | | | 149 | |
150 | #define DIR_DEBUG0(fmt) \ | | 150 | #define DIR_DEBUG0(fmt) \ |
151 | if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt) | | 151 | if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt) |
152 | | | 152 | |
153 | #define DIR_DEBUG1(fmt, arg1) \ | | 153 | #define DIR_DEBUG1(fmt, arg1) \ |
154 | if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1) | | 154 | if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1) |
155 | | | 155 | |
156 | #define DIR_DEBUG2(fmt, arg1, arg2) \ | | 156 | #define DIR_DEBUG2(fmt, arg1, arg2) \ |
157 | if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2) | | 157 | if (!DEBUG(DIR)) (void) 0; else fprintf(debug_file, fmt, arg1, arg2) |
158 | | | 158 | |
159 | | | 159 | |
160 | /* | | 160 | /* |
161 | * A search path consists of a Lst of Path structures. A Path structure | | 161 | * A search path consists of a Lst of Path structures. A Path structure |
162 | * has in it the name of the directory and a hash table of all the files | | 162 | * has in it the name of the directory and a hash table of all the files |
163 | * in the directory. This is used to cut down on the number of system | | 163 | * in the directory. This is used to cut down on the number of system |
164 | * calls necessary to find implicit dependents and their like. Since | | 164 | * calls necessary to find implicit dependents and their like. Since |
165 | * these searches are made before any actions are taken, we need not | | 165 | * these searches are made before any actions are taken, we need not |
166 | * worry about the directory changing due to creation commands. If this | | 166 | * worry about the directory changing due to creation commands. If this |
167 | * hampers the style of some makefiles, they must be changed. | | 167 | * hampers the style of some makefiles, they must be changed. |
168 | * | | 168 | * |
169 | * A list of all previously-read directories is kept in the | | 169 | * A list of all previously-read directories is kept in the |
170 | * openDirectories Lst. This list is checked first before a directory | | 170 | * openDirectories Lst. This list is checked first before a directory |
171 | * is opened. | | 171 | * is opened. |
172 | * | | 172 | * |
173 | * The need for the caching of whole directories is brought about by | | 173 | * The need for the caching of whole directories is brought about by |
174 | * the multi-level transformation code in suff.c, which tends to search | | 174 | * the multi-level transformation code in suff.c, which tends to search |
175 | * for far more files than regular make does. In the initial | | 175 | * for far more files than regular make does. In the initial |
176 | * implementation, the amount of time spent performing "stat" calls was | | 176 | * implementation, the amount of time spent performing "stat" calls was |
177 | * truly astronomical. The problem with hashing at the start is, | | 177 | * truly astronomical. The problem with hashing at the start is, |
178 | * of course, that pmake doesn't then detect changes to these directories | | 178 | * of course, that pmake doesn't then detect changes to these directories |
179 | * during the course of the make. Three possibilities suggest themselves: | | 179 | * during the course of the make. Three possibilities suggest themselves: |
180 | * | | 180 | * |
181 | * 1) just use stat to test for a file's existence. As mentioned | | 181 | * 1) just use stat to test for a file's existence. As mentioned |
182 | * above, this is very inefficient due to the number of checks | | 182 | * above, this is very inefficient due to the number of checks |
183 | * engendered by the multi-level transformation code. | | 183 | * engendered by the multi-level transformation code. |
184 | * 2) use readdir() and company to search the directories, keeping | | 184 | * 2) use readdir() and company to search the directories, keeping |
185 | * them open between checks. I have tried this and while it | | 185 | * them open between checks. I have tried this and while it |
186 | * didn't slow down the process too much, it could severely | | 186 | * didn't slow down the process too much, it could severely |
187 | * affect the amount of parallelism available as each directory | | 187 | * affect the amount of parallelism available as each directory |
188 | * open would take another file descriptor out of play for | | 188 | * open would take another file descriptor out of play for |
189 | * handling I/O for another job. Given that it is only recently | | 189 | * handling I/O for another job. Given that it is only recently |
190 | * that UNIX OS's have taken to allowing more than 20 or 32 | | 190 | * that UNIX OS's have taken to allowing more than 20 or 32 |
191 | * file descriptors for a process, this doesn't seem acceptable | | 191 | * file descriptors for a process, this doesn't seem acceptable |
192 | * to me. | | 192 | * to me. |
193 | * 3) record the mtime of the directory in the Path structure and | | 193 | * 3) record the mtime of the directory in the Path structure and |
194 | * verify the directory hasn't changed since the contents were | | 194 | * verify the directory hasn't changed since the contents were |
195 | * hashed. This will catch the creation or deletion of files, | | 195 | * hashed. This will catch the creation or deletion of files, |
196 | * but not the updating of files. However, since it is the | | 196 | * but not the updating of files. However, since it is the |
197 | * creation and deletion that is the problem, this could be | | 197 | * creation and deletion that is the problem, this could be |
198 | * a good thing to do. Unfortunately, if the directory (say ".") | | 198 | * a good thing to do. Unfortunately, if the directory (say ".") |
199 | * were fairly large and changed fairly frequently, the constant | | 199 | * were fairly large and changed fairly frequently, the constant |
200 | * rehashing could seriously degrade performance. It might be | | 200 | * rehashing could seriously degrade performance. It might be |
201 | * good in such cases to keep track of the number of rehashes | | 201 | * good in such cases to keep track of the number of rehashes |
202 | * and if the number goes over a (small) limit, resort to using | | 202 | * and if the number goes over a (small) limit, resort to using |
203 | * stat in its place. | | 203 | * stat in its place. |
204 | * | | 204 | * |
205 | * An additional thing to consider is that pmake is used primarily | | 205 | * An additional thing to consider is that pmake is used primarily |
206 | * to create C programs and until recently pcc-based compilers refused | | 206 | * to create C programs and until recently pcc-based compilers refused |
207 | * to allow you to specify where the resulting object file should be | | 207 | * to allow you to specify where the resulting object file should be |
208 | * placed. This forced all objects to be created in the current | | 208 | * placed. This forced all objects to be created in the current |
209 | * directory. This isn't meant as a full excuse, just an explanation of | | 209 | * directory. This isn't meant as a full excuse, just an explanation of |
210 | * some of the reasons for the caching used here. | | 210 | * some of the reasons for the caching used here. |
211 | * | | 211 | * |
212 | * One more note: the location of a target's file is only performed | | 212 | * One more note: the location of a target's file is only performed |
213 | * on the downward traversal of the graph and then only for terminal | | 213 | * on the downward traversal of the graph and then only for terminal |
214 | * nodes in the graph. This could be construed as wrong in some cases, | | 214 | * nodes in the graph. This could be construed as wrong in some cases, |
215 | * but prevents inadvertent modification of files when the "installed" | | 215 | * but prevents inadvertent modification of files when the "installed" |
216 | * directory for a file is provided in the search path. | | 216 | * directory for a file is provided in the search path. |
217 | * | | 217 | * |
218 | * Another data structure maintained by this module is an mtime | | 218 | * Another data structure maintained by this module is an mtime |
219 | * cache used when the searching of cached directories fails to find | | 219 | * cache used when the searching of cached directories fails to find |
220 | * a file. In the past, Dir_FindFile would simply perform an access() | | 220 | * a file. In the past, Dir_FindFile would simply perform an access() |
221 | * call in such a case to determine if the file could be found using | | 221 | * call in such a case to determine if the file could be found using |
222 | * just the name given. When this hit, however, all that was gained | | 222 | * just the name given. When this hit, however, all that was gained |
223 | * was the knowledge that the file existed. Given that an access() is | | 223 | * was the knowledge that the file existed. Given that an access() is |
224 | * essentially a stat() without the copyout() call, and that the same | | 224 | * essentially a stat() without the copyout() call, and that the same |
225 | * filesystem overhead would have to be incurred in Dir_MTime, it made | | 225 | * filesystem overhead would have to be incurred in Dir_MTime, it made |
226 | * sense to replace the access() with a stat() and record the mtime | | 226 | * sense to replace the access() with a stat() and record the mtime |
227 | * in a cache for when Dir_MTime was actually called. | | 227 | * in a cache for when Dir_MTime was actually called. |
228 | */ | | 228 | */ |
229 | | | 229 | |
230 | Lst dirSearchPath; /* main search path */ | | 230 | Lst dirSearchPath; /* main search path */ |
231 | | | 231 | |
232 | static Lst openDirectories; /* the list of all open directories */ | | 232 | static Lst openDirectories; /* the list of all open directories */ |
233 | | | 233 | |
234 | /* | | 234 | /* |
235 | * Variables for gathering statistics on the efficiency of the hashing | | 235 | * Variables for gathering statistics on the efficiency of the hashing |
236 | * mechanism. | | 236 | * mechanism. |
237 | */ | | 237 | */ |
238 | static int hits; /* Found in directory cache */ | | 238 | static int hits; /* Found in directory cache */ |
239 | static int misses; /* Sad, but not evil misses */ | | 239 | static int misses; /* Sad, but not evil misses */ |
240 | static int nearmisses; /* Found under search path */ | | 240 | static int nearmisses; /* Found under search path */ |
241 | static int bigmisses; /* Sought by itself */ | | 241 | static int bigmisses; /* Sought by itself */ |
242 | | | 242 | |
243 | static Path *dot; /* contents of current directory */ | | 243 | static Path *dot; /* contents of current directory */ |
244 | static Path *cur; /* contents of current directory, if not dot */ | | 244 | static Path *cur; /* contents of current directory, if not dot */ |
245 | static Path *dotLast; /* a fake path entry indicating we need to | | 245 | static Path *dotLast; /* a fake path entry indicating we need to |
246 | * look for . last */ | | 246 | * look for . last */ |
247 | | | 247 | |
248 | /* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to | | 248 | /* Results of doing a last-resort stat in Dir_FindFile -- if we have to go to |
249 | * the system to find the file, we might as well have its mtime on record. | | 249 | * the system to find the file, we might as well have its mtime on record. |
250 | * | | 250 | * |
251 | * XXX: If this is done way early, there's a chance other rules will have | | 251 | * XXX: If this is done way early, there's a chance other rules will have |
252 | * already updated the file, in which case we'll update it again. Generally, | | 252 | * already updated the file, in which case we'll update it again. Generally, |
253 | * there won't be two rules to update a single file, so this should be ok, | | 253 | * there won't be two rules to update a single file, so this should be ok, |
254 | * but... */ | | 254 | * but... */ |
255 | static Hash_Table mtimes; | | 255 | static Hash_Table mtimes; |
256 | | | 256 | |
257 | static Hash_Table lmtimes; /* same as mtimes but for lstat */ | | 257 | static Hash_Table lmtimes; /* same as mtimes but for lstat */ |
258 | | | 258 | |
259 | static int DirFindName(const void *, const void *); | | 259 | static int DirFindName(const void *, const void *); |
260 | static void DirExpandCurly(const char *, const char *, Lst, Lst); | | 260 | static void DirExpandCurly(const char *, const char *, Lst, Lst); |
261 | static void DirExpandInt(const char *, Lst, Lst); | | 261 | static void DirExpandInt(const char *, Lst, Lst); |
262 | static int DirPrintWord(void *, void *); | | 262 | static int DirPrintWord(void *, void *); |
263 | static int DirPrintDir(void *, void *); | | 263 | static int DirPrintDir(void *, void *); |
264 | static char *DirLookup(Path *, const char *, const char *, Boolean); | | 264 | static char *DirLookup(Path *, const char *, const char *, Boolean); |
265 | static char *DirLookupSubdir(Path *, const char *); | | 265 | static char *DirLookupSubdir(Path *, const char *); |
266 | static char *DirFindDot(Boolean, const char *, const char *); | | 266 | static char *DirFindDot(Boolean, const char *, const char *); |
267 | static char *DirLookupAbs(Path *, const char *, const char *); | | 267 | static char *DirLookupAbs(Path *, const char *, const char *); |
268 | | | 268 | |
269 | | | 269 | |
270 | /* | | 270 | /* |
271 | * We use stat(2) a lot, cache the results. | | 271 | * We use stat(2) a lot, cache the results. |
272 | * mtime and mode are all we care about. | | 272 | * mtime and mode are all we care about. |
273 | */ | | 273 | */ |
274 | struct cache_st { | | 274 | struct cache_st { |
275 | time_t lmtime; /* lstat */ | | 275 | time_t lmtime; /* lstat */ |
276 | time_t mtime; /* stat */ | | 276 | time_t mtime; /* stat */ |
277 | mode_t mode; | | 277 | mode_t mode; |
278 | }; | | 278 | }; |
279 | | | 279 | |
280 | /* minimize changes below */ | | 280 | /* minimize changes below */ |
281 | typedef enum { | | 281 | typedef enum { |
282 | CST_LSTAT = 0x01, /* call lstat(2) instead of stat(2) */ | | 282 | CST_LSTAT = 0x01, /* call lstat(2) instead of stat(2) */ |
283 | CST_UPDATE = 0x02 /* ignore existing cached entry */ | | 283 | CST_UPDATE = 0x02 /* ignore existing cached entry */ |
284 | } CachedStatsFlags; | | 284 | } CachedStatsFlags; |
285 | | | 285 | |
286 | /* Returns 0 and the result of stat(2) or lstat(2) in *st, or -1 on error. | | 286 | /* Returns 0 and the result of stat(2) or lstat(2) in *st, or -1 on error. |
287 | * Only st->st_mode and st->st_mtime are filled. */ | | 287 | * Only st->st_mode and st->st_mtime are filled. */ |
288 | static int | | 288 | static int |
289 | cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, | | 289 | cached_stats(Hash_Table *htp, const char *pathname, struct stat *st, |
290 | CachedStatsFlags flags) | | 290 | CachedStatsFlags flags) |
291 | { | | 291 | { |
292 | Hash_Entry *entry; | | 292 | Hash_Entry *entry; |
293 | struct cache_st *cst; | | 293 | struct cache_st *cst; |
294 | int rc; | | 294 | int rc; |
295 | | | 295 | |
296 | if (!pathname || !pathname[0]) | | 296 | if (!pathname || !pathname[0]) |
297 | return -1; | | 297 | return -1; |
298 | | | 298 | |
299 | entry = Hash_FindEntry(htp, pathname); | | 299 | entry = Hash_FindEntry(htp, pathname); |
300 | | | 300 | |
301 | if (entry && !(flags & CST_UPDATE)) { | | 301 | if (entry && !(flags & CST_UPDATE)) { |
302 | cst = entry->clientPtr; | | 302 | cst = entry->clientPtr; |
303 | | | 303 | |
304 | memset(st, 0, sizeof(*st)); | | 304 | memset(st, 0, sizeof(*st)); |
305 | st->st_mode = cst->mode; | | 305 | st->st_mode = cst->mode; |
306 | st->st_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; | | 306 | st->st_mtime = (flags & CST_LSTAT) ? cst->lmtime : cst->mtime; |
307 | if (st->st_mtime) { | | 307 | if (st->st_mtime) { |
308 | DIR_DEBUG2("Using cached time %s for %s\n", | | 308 | DIR_DEBUG2("Using cached time %s for %s\n", |
309 | Targ_FmtTime(st->st_mtime), pathname); | | 309 | Targ_FmtTime(st->st_mtime), pathname); |
310 | return 0; | | 310 | return 0; |
311 | } | | 311 | } |
312 | } | | 312 | } |
313 | | | 313 | |
314 | rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st); | | 314 | rc = (flags & CST_LSTAT) ? lstat(pathname, st) : stat(pathname, st); |
315 | if (rc == -1) | | 315 | if (rc == -1) |
316 | return -1; | | 316 | return -1; |
317 | | | 317 | |
318 | if (st->st_mtime == 0) | | 318 | if (st->st_mtime == 0) |
319 | st->st_mtime = 1; /* avoid confusion with missing file */ | | 319 | st->st_mtime = 1; /* avoid confusion with missing file */ |
320 | | | 320 | |
321 | if (!entry) | | 321 | if (!entry) |
322 | entry = Hash_CreateEntry(htp, pathname, NULL); | | 322 | entry = Hash_CreateEntry(htp, pathname, NULL); |
323 | if (!entry->clientPtr) { | | 323 | if (!entry->clientPtr) { |
324 | entry->clientPtr = bmake_malloc(sizeof(*cst)); | | 324 | entry->clientPtr = bmake_malloc(sizeof(*cst)); |
325 | memset(entry->clientPtr, 0, sizeof(*cst)); | | 325 | memset(entry->clientPtr, 0, sizeof(*cst)); |
326 | } | | 326 | } |
327 | cst = entry->clientPtr; | | 327 | cst = entry->clientPtr; |
328 | if (flags & CST_LSTAT) { | | 328 | if (flags & CST_LSTAT) { |
329 | cst->lmtime = st->st_mtime; | | 329 | cst->lmtime = st->st_mtime; |
330 | } else { | | 330 | } else { |
331 | cst->mtime = st->st_mtime; | | 331 | cst->mtime = st->st_mtime; |
332 | } | | 332 | } |
333 | cst->mode = st->st_mode; | | 333 | cst->mode = st->st_mode; |
334 | DIR_DEBUG2(" Caching %s for %s\n", | | 334 | DIR_DEBUG2(" Caching %s for %s\n", |
335 | Targ_FmtTime(st->st_mtime), pathname); | | 335 | Targ_FmtTime(st->st_mtime), pathname); |
336 | | | 336 | |
337 | return 0; | | 337 | return 0; |
338 | } | | 338 | } |
339 | | | 339 | |
340 | int | | 340 | int |
341 | cached_stat(const char *pathname, void *st) | | 341 | cached_stat(const char *pathname, void *st) |
342 | { | | 342 | { |
343 | return cached_stats(&mtimes, pathname, st, 0); | | 343 | return cached_stats(&mtimes, pathname, st, 0); |
344 | } | | 344 | } |
345 | | | 345 | |
346 | int | | 346 | int |
347 | cached_lstat(const char *pathname, void *st) | | 347 | cached_lstat(const char *pathname, void *st) |
348 | { | | 348 | { |
349 | return cached_stats(&lmtimes, pathname, st, CST_LSTAT); | | 349 | return cached_stats(&lmtimes, pathname, st, CST_LSTAT); |
350 | } | | 350 | } |
351 | | | 351 | |
352 | /* Initialize things for this module. */ | | 352 | /* Initialize things for this module. */ |
353 | void | | 353 | void |
354 | Dir_Init(void) | | 354 | Dir_Init(void) |
355 | { | | 355 | { |
356 | dirSearchPath = Lst_Init(); | | 356 | dirSearchPath = Lst_Init(); |
357 | openDirectories = Lst_Init(); | | 357 | openDirectories = Lst_Init(); |
358 | Hash_InitTable(&mtimes, 0); | | 358 | Hash_InitTable(&mtimes, 0); |
359 | Hash_InitTable(&lmtimes, 0); | | 359 | Hash_InitTable(&lmtimes, 0); |
360 | } | | 360 | } |
361 | | | 361 | |
362 | void | | 362 | void |
363 | Dir_InitDir(const char *cdname) | | 363 | Dir_InitDir(const char *cdname) |
364 | { | | 364 | { |
365 | Dir_InitCur(cdname); | | 365 | Dir_InitCur(cdname); |
366 | | | 366 | |
367 | dotLast = bmake_malloc(sizeof(Path)); | | 367 | dotLast = bmake_malloc(sizeof(Path)); |
368 | dotLast->refCount = 1; | | 368 | dotLast->refCount = 1; |
369 | dotLast->hits = 0; | | 369 | dotLast->hits = 0; |
370 | dotLast->name = bmake_strdup(".DOTLAST"); | | 370 | dotLast->name = bmake_strdup(".DOTLAST"); |
371 | Hash_InitTable(&dotLast->files, -1); | | 371 | Hash_InitTable(&dotLast->files, -1); |
372 | } | | 372 | } |
373 | | | 373 | |
374 | /* | | 374 | /* |
375 | * Called by Dir_InitDir and whenever .CURDIR is assigned to. | | 375 | * Called by Dir_InitDir and whenever .CURDIR is assigned to. |
376 | */ | | 376 | */ |
377 | void | | 377 | void |
378 | Dir_InitCur(const char *cdname) | | 378 | Dir_InitCur(const char *cdname) |
379 | { | | 379 | { |
380 | Path *p; | | 380 | Path *p; |
381 | | | 381 | |
382 | if (cdname != NULL) { | | 382 | if (cdname != NULL) { |
383 | /* | | 383 | /* |
384 | * Our build directory is not the same as our source directory. | | 384 | * Our build directory is not the same as our source directory. |
385 | * Keep this one around too. | | 385 | * Keep this one around too. |
386 | */ | | 386 | */ |
387 | if ((p = Dir_AddDir(NULL, cdname))) { | | 387 | if ((p = Dir_AddDir(NULL, cdname))) { |
388 | p->refCount += 1; | | 388 | p->refCount += 1; |
389 | if (cur && cur != p) { | | 389 | if (cur && cur != p) { |
390 | /* | | 390 | /* |
391 | * We've been here before, cleanup. | | 391 | * We've been here before, cleanup. |
392 | */ | | 392 | */ |
393 | cur->refCount -= 1; | | 393 | cur->refCount -= 1; |
394 | Dir_Destroy(cur); | | 394 | Dir_Destroy(cur); |
395 | } | | 395 | } |
396 | cur = p; | | 396 | cur = p; |
397 | } | | 397 | } |
398 | } | | 398 | } |
399 | } | | 399 | } |
400 | | | 400 | |
401 | /*- | | 401 | /*- |
402 | *----------------------------------------------------------------------- | | 402 | *----------------------------------------------------------------------- |
403 | * Dir_InitDot -- | | 403 | * Dir_InitDot -- |
404 | * (re)initialize "dot" (current/object directory) path hash | | 404 | * (re)initialize "dot" (current/object directory) path hash |
405 | * | | 405 | * |
406 | * Results: | | 406 | * Results: |
407 | * none | | 407 | * none |
408 | * | | 408 | * |
409 | * Side Effects: | | 409 | * Side Effects: |
410 | * some directories may be opened. | | 410 | * some directories may be opened. |
411 | *----------------------------------------------------------------------- | | 411 | *----------------------------------------------------------------------- |
412 | */ | | 412 | */ |
413 | void | | 413 | void |
414 | Dir_InitDot(void) | | 414 | Dir_InitDot(void) |
415 | { | | 415 | { |
416 | if (dot != NULL) { | | 416 | if (dot != NULL) { |
417 | LstNode ln; | | 417 | LstNode ln; |
418 | | | 418 | |
419 | /* Remove old entry from openDirectories, but do not destroy. */ | | 419 | /* Remove old entry from openDirectories, but do not destroy. */ |
420 | ln = Lst_MemberS(openDirectories, dot); | | 420 | ln = Lst_MemberS(openDirectories, dot); |
421 | Lst_RemoveS(openDirectories, ln); | | 421 | Lst_RemoveS(openDirectories, ln); |
422 | } | | 422 | } |
423 | | | 423 | |
424 | dot = Dir_AddDir(NULL, "."); | | 424 | dot = Dir_AddDir(NULL, "."); |
425 | | | 425 | |
426 | if (dot == NULL) { | | 426 | if (dot == NULL) { |
427 | Error("Cannot open `.' (%s)", strerror(errno)); | | 427 | Error("Cannot open `.' (%s)", strerror(errno)); |
428 | exit(1); | | 428 | exit(1); |
429 | } | | 429 | } |
430 | | | 430 | |
431 | /* | | 431 | /* |
432 | * We always need to have dot around, so we increment its reference count | | 432 | * We always need to have dot around, so we increment its reference count |
433 | * to make sure it's not destroyed. | | 433 | * to make sure it's not destroyed. |
434 | */ | | 434 | */ |
435 | dot->refCount += 1; | | 435 | dot->refCount += 1; |
436 | Dir_SetPATH(); /* initialize */ | | 436 | Dir_SetPATH(); /* initialize */ |
437 | } | | 437 | } |
438 | | | 438 | |
439 | /*- | | 439 | /*- |
440 | *----------------------------------------------------------------------- | | 440 | *----------------------------------------------------------------------- |
441 | * Dir_End -- | | 441 | * Dir_End -- |
442 | * cleanup things for this module | | 442 | * cleanup things for this module |
443 | * | | 443 | * |
444 | * Results: | | 444 | * Results: |
445 | * none | | 445 | * none |
446 | * | | 446 | * |
447 | * Side Effects: | | 447 | * Side Effects: |
448 | * none | | 448 | * none |
449 | *----------------------------------------------------------------------- | | 449 | *----------------------------------------------------------------------- |
450 | */ | | 450 | */ |
451 | void | | 451 | void |
452 | Dir_End(void) | | 452 | Dir_End(void) |
453 | { | | 453 | { |
454 | #ifdef CLEANUP | | 454 | #ifdef CLEANUP |
455 | if (cur) { | | 455 | if (cur) { |
456 | cur->refCount -= 1; | | 456 | cur->refCount -= 1; |
457 | Dir_Destroy(cur); | | 457 | Dir_Destroy(cur); |
458 | } | | 458 | } |
459 | dot->refCount -= 1; | | 459 | dot->refCount -= 1; |
460 | dotLast->refCount -= 1; | | 460 | dotLast->refCount -= 1; |
461 | Dir_Destroy(dotLast); | | 461 | Dir_Destroy(dotLast); |
462 | Dir_Destroy(dot); | | 462 | Dir_Destroy(dot); |
463 | Dir_ClearPath(dirSearchPath); | | 463 | Dir_ClearPath(dirSearchPath); |
464 | Lst_FreeS(dirSearchPath); | | 464 | Lst_FreeS(dirSearchPath); |
465 | Dir_ClearPath(openDirectories); | | 465 | Dir_ClearPath(openDirectories); |
466 | Lst_FreeS(openDirectories); | | 466 | Lst_FreeS(openDirectories); |
467 | Hash_DeleteTable(&mtimes); | | 467 | Hash_DeleteTable(&mtimes); |
468 | #endif | | 468 | #endif |
469 | } | | 469 | } |
470 | | | 470 | |
471 | /* | | 471 | /* |
472 | * We want ${.PATH} to indicate the order in which we will actually | | 472 | * We want ${.PATH} to indicate the order in which we will actually |
473 | * search, so we rebuild it after any .PATH: target. | | 473 | * search, so we rebuild it after any .PATH: target. |
474 | * This is the simplest way to deal with the effect of .DOTLAST. | | 474 | * This is the simplest way to deal with the effect of .DOTLAST. |
475 | */ | | 475 | */ |
476 | void | | 476 | void |
477 | Dir_SetPATH(void) | | 477 | Dir_SetPATH(void) |
478 | { | | 478 | { |
479 | LstNode ln; /* a list element */ | | 479 | LstNode ln; /* a list element */ |
480 | Path *p; | | 480 | Path *p; |
481 | Boolean hasLastDot = FALSE; /* true if we should search dot last */ | | 481 | Boolean hasLastDot = FALSE; /* true if we should search dot last */ |
482 | | | 482 | |
483 | Var_Delete(".PATH", VAR_GLOBAL); | | 483 | Var_Delete(".PATH", VAR_GLOBAL); |
484 | | | 484 | |
485 | Lst_OpenS(dirSearchPath); | | 485 | Lst_OpenS(dirSearchPath); |
486 | if ((ln = Lst_First(dirSearchPath)) != NULL) { | | 486 | if ((ln = Lst_First(dirSearchPath)) != NULL) { |
487 | p = Lst_DatumS(ln); | | 487 | p = Lst_DatumS(ln); |
488 | if (p == dotLast) { | | 488 | if (p == dotLast) { |
489 | hasLastDot = TRUE; | | 489 | hasLastDot = TRUE; |
490 | Var_Append(".PATH", dotLast->name, VAR_GLOBAL); | | 490 | Var_Append(".PATH", dotLast->name, VAR_GLOBAL); |
491 | } | | 491 | } |
492 | } | | 492 | } |
493 | | | 493 | |
494 | if (!hasLastDot) { | | 494 | if (!hasLastDot) { |
495 | if (dot) | | 495 | if (dot) |
496 | Var_Append(".PATH", dot->name, VAR_GLOBAL); | | 496 | Var_Append(".PATH", dot->name, VAR_GLOBAL); |
497 | if (cur) | | 497 | if (cur) |
498 | Var_Append(".PATH", cur->name, VAR_GLOBAL); | | 498 | Var_Append(".PATH", cur->name, VAR_GLOBAL); |
499 | } | | 499 | } |
500 | | | 500 | |
501 | while ((ln = Lst_NextS(dirSearchPath)) != NULL) { | | 501 | while ((ln = Lst_NextS(dirSearchPath)) != NULL) { |
502 | p = Lst_DatumS(ln); | | 502 | p = Lst_DatumS(ln); |
503 | if (p == dotLast) | | 503 | if (p == dotLast) |
504 | continue; | | 504 | continue; |
505 | if (p == dot && hasLastDot) | | 505 | if (p == dot && hasLastDot) |
506 | continue; | | 506 | continue; |
507 | Var_Append(".PATH", p->name, VAR_GLOBAL); | | 507 | Var_Append(".PATH", p->name, VAR_GLOBAL); |
508 | } | | 508 | } |
509 | | | 509 | |
510 | if (hasLastDot) { | | 510 | if (hasLastDot) { |
511 | if (dot) | | 511 | if (dot) |
512 | Var_Append(".PATH", dot->name, VAR_GLOBAL); | | 512 | Var_Append(".PATH", dot->name, VAR_GLOBAL); |
513 | if (cur) | | 513 | if (cur) |
514 | Var_Append(".PATH", cur->name, VAR_GLOBAL); | | 514 | Var_Append(".PATH", cur->name, VAR_GLOBAL); |
515 | } | | 515 | } |
516 | Lst_CloseS(dirSearchPath); | | 516 | Lst_CloseS(dirSearchPath); |
517 | } | | 517 | } |
518 | | | 518 | |
519 | /*- | | 519 | /*- |
520 | *----------------------------------------------------------------------- | | 520 | *----------------------------------------------------------------------- |
521 | * DirFindName -- | | 521 | * DirFindName -- |
522 | * See if the Path structure describes the same directory as the | | 522 | * See if the Path structure describes the same directory as the |
523 | * given one by comparing their names. Called from Dir_AddDir via | | 523 | * given one by comparing their names. Called from Dir_AddDir via |
524 | * Lst_Find when searching the list of open directories. | | 524 | * Lst_Find when searching the list of open directories. |
525 | * | | 525 | * |
526 | * Input: | | 526 | * Input: |
527 | * p Current name | | 527 | * p Current name |
528 | * dname Desired name | | 528 | * dname Desired name |
529 | * | | 529 | * |
530 | * Results: | | 530 | * Results: |
531 | * 0 if it is the same. Non-zero otherwise | | 531 | * 0 if it is the same. Non-zero otherwise |
532 | * | | 532 | * |
533 | * Side Effects: | | 533 | * Side Effects: |
534 | * None | | 534 | * None |
535 | *----------------------------------------------------------------------- | | 535 | *----------------------------------------------------------------------- |
536 | */ | | 536 | */ |
537 | static int | | 537 | static int |
538 | DirFindName(const void *p, const void *dname) | | 538 | DirFindName(const void *p, const void *dname) |
539 | { | | 539 | { |
540 | return strcmp(((const Path *)p)->name, dname); | | 540 | return strcmp(((const Path *)p)->name, dname); |
541 | } | | 541 | } |
542 | | | 542 | |
543 | /*- | | 543 | /*- |
544 | *----------------------------------------------------------------------- | | 544 | *----------------------------------------------------------------------- |
545 | * Dir_HasWildcards -- | | 545 | * Dir_HasWildcards -- |
546 | * see if the given name has any wildcard characters in it | | 546 | * see if the given name has any wildcard characters in it |
547 | * be careful not to expand unmatching brackets or braces. | | 547 | * be careful not to expand unmatching brackets or braces. |
548 | * XXX: This code is not 100% correct. ([^]] fails etc.) | | 548 | * XXX: This code is not 100% correct. ([^]] fails etc.) |
549 | * I really don't think that make(1) should be expanding | | 549 | * I really don't think that make(1) should be expanding |
550 | * patterns, because then you have to set a mechanism for | | 550 | * patterns, because then you have to set a mechanism for |
551 | * escaping the expansion! | | 551 | * escaping the expansion! |
552 | * | | 552 | * |
553 | * Input: | | 553 | * Input: |
554 | * name name to check | | 554 | * name name to check |
555 | * | | 555 | * |
556 | * Results: | | 556 | * Results: |
557 | * returns TRUE if the word should be expanded, FALSE otherwise | | 557 | * returns TRUE if the word should be expanded, FALSE otherwise |
558 | * | | 558 | * |
559 | * Side Effects: | | 559 | * Side Effects: |
560 | * none | | 560 | * none |
561 | *----------------------------------------------------------------------- | | 561 | *----------------------------------------------------------------------- |
562 | */ | | 562 | */ |
563 | Boolean | | 563 | Boolean |
564 | Dir_HasWildcards(char *name) | | 564 | Dir_HasWildcards(char *name) |
565 | { | | 565 | { |
566 | char *cp; | | 566 | char *cp; |
567 | int wild = 0, brace = 0, bracket = 0; | | 567 | int wild = 0, brace = 0, bracket = 0; |
568 | | | 568 | |
569 | for (cp = name; *cp; cp++) { | | 569 | for (cp = name; *cp; cp++) { |
570 | switch (*cp) { | | 570 | switch (*cp) { |
571 | case '{': | | 571 | case '{': |
572 | brace++; | | 572 | brace++; |
573 | wild = 1; | | 573 | wild = 1; |
574 | break; | | 574 | break; |
575 | case '}': | | 575 | case '}': |
576 | brace--; | | 576 | brace--; |
577 | break; | | 577 | break; |
578 | case '[': | | 578 | case '[': |
579 | bracket++; | | 579 | bracket++; |
580 | wild = 1; | | 580 | wild = 1; |
581 | break; | | 581 | break; |
582 | case ']': | | 582 | case ']': |
583 | bracket--; | | 583 | bracket--; |
584 | break; | | 584 | break; |
585 | case '?': | | 585 | case '?': |
586 | case '*': | | 586 | case '*': |
587 | wild = 1; | | 587 | wild = 1; |
588 | break; | | 588 | break; |
589 | default: | | 589 | default: |
590 | break; | | 590 | break; |
591 | } | | 591 | } |
592 | } | | 592 | } |
593 | return wild && bracket == 0 && brace == 0; | | 593 | return wild && bracket == 0 && brace == 0; |
594 | } | | 594 | } |
595 | | | 595 | |
596 | /*- | | 596 | /*- |
597 | *----------------------------------------------------------------------- | | 597 | *----------------------------------------------------------------------- |
598 | * DirMatchFiles -- | | 598 | * DirMatchFiles -- |
599 | * Given a pattern and a Path structure, see if any files | | 599 | * Given a pattern and a Path structure, see if any files |
600 | * match the pattern and add their names to the 'expansions' list if | | 600 | * match the pattern and add their names to the 'expansions' list if |
601 | * any do. This is incomplete -- it doesn't take care of patterns like | | 601 | * any do. This is incomplete -- it doesn't take care of patterns like |
602 | * src / *src / *.c properly (just *.c on any of the directories), but it | | 602 | * src / *src / *.c properly (just *.c on any of the directories), but it |
603 | * will do for now. | | 603 | * will do for now. |
604 | * | | 604 | * |
605 | * Input: | | 605 | * Input: |
606 | * pattern Pattern to look for | | 606 | * pattern Pattern to look for |
607 | * p Directory to search | | 607 | * p Directory to search |
608 | * expansion Place to store the results | | 608 | * expansion Place to store the results |
609 | * | | 609 | * |
610 | * Side Effects: | | 610 | * Side Effects: |
611 | * File names are added to the expansions lst. The directory will be | | 611 | * File names are added to the expansions lst. The directory will be |
612 | * fully hashed when this is done. | | 612 | * fully hashed when this is done. |
613 | *----------------------------------------------------------------------- | | 613 | *----------------------------------------------------------------------- |
614 | */ | | 614 | */ |
615 | static void | | 615 | static void |
616 | DirMatchFiles(const char *pattern, Path *p, Lst expansions) | | 616 | DirMatchFiles(const char *pattern, Path *p, Lst expansions) |
617 | { | | 617 | { |
618 | Hash_Search search; /* Index into the directory's table */ | | 618 | Hash_Search search; /* Index into the directory's table */ |
619 | Hash_Entry *entry; /* Current entry in the table */ | | 619 | Hash_Entry *entry; /* Current entry in the table */ |
620 | Boolean isDot; /* TRUE if the directory being searched is . */ | | 620 | Boolean isDot; /* TRUE if the directory being searched is . */ |
621 | | | 621 | |
622 | isDot = (*p->name == '.' && p->name[1] == '\0'); | | 622 | isDot = (*p->name == '.' && p->name[1] == '\0'); |
623 | | | 623 | |
624 | for (entry = Hash_EnumFirst(&p->files, &search); | | 624 | for (entry = Hash_EnumFirst(&p->files, &search); |
625 | entry != NULL; | | 625 | entry != NULL; |
626 | entry = Hash_EnumNext(&search)) | | 626 | entry = Hash_EnumNext(&search)) |
627 | { | | 627 | { |
628 | /* | | 628 | /* |
629 | * See if the file matches the given pattern. Note we follow the UNIX | | 629 | * See if the file matches the given pattern. Note we follow the UNIX |
630 | * convention that dot files will only be found if the pattern | | 630 | * convention that dot files will only be found if the pattern |
631 | * begins with a dot (note also that as a side effect of the hashing | | 631 | * begins with a dot (note also that as a side effect of the hashing |
632 | * scheme, .* won't match . or .. since they aren't hashed). | | 632 | * scheme, .* won't match . or .. since they aren't hashed). |
633 | */ | | 633 | */ |
634 | if (Str_Match(entry->name, pattern) && | | 634 | if (Str_Match(entry->name, pattern) && |
635 | ((entry->name[0] != '.') || | | 635 | ((entry->name[0] != '.') || |
636 | (pattern[0] == '.'))) | | 636 | (pattern[0] == '.'))) |
637 | { | | 637 | { |
638 | Lst_AppendS(expansions, | | 638 | Lst_AppendS(expansions, |
639 | (isDot ? bmake_strdup(entry->name) : | | 639 | (isDot ? bmake_strdup(entry->name) : |
640 | str_concat3(p->name, "/", entry->name))); | | 640 | str_concat3(p->name, "/", entry->name))); |
641 | } | | 641 | } |
642 | } | | 642 | } |
643 | } | | 643 | } |
644 | | | 644 | |
645 | /* Find the next closing brace in the string, taking nested braces into | | 645 | /* Find the next closing brace in the string, taking nested braces into |
646 | * account. */ | | 646 | * account. */ |
647 | static const char * | | 647 | static const char * |
648 | closing_brace(const char *p) | | 648 | closing_brace(const char *p) |
649 | { | | 649 | { |
650 | int nest = 0; | | 650 | int nest = 0; |
651 | while (*p != '\0') { | | 651 | while (*p != '\0') { |
652 | if (*p == '}' && nest == 0) | | 652 | if (*p == '}' && nest == 0) |
653 | break; | | 653 | break; |
654 | if (*p == '{') | | 654 | if (*p == '{') |
655 | nest++; | | 655 | nest++; |
656 | if (*p == '}') | | 656 | if (*p == '}') |
657 | nest--; | | 657 | nest--; |
658 | p++; | | 658 | p++; |
659 | } | | 659 | } |
660 | return p; | | 660 | return p; |
661 | } | | 661 | } |
662 | | | 662 | |
663 | /* Find the next closing brace or comma in the string, taking nested braces | | 663 | /* Find the next closing brace or comma in the string, taking nested braces |
664 | * into account. */ | | 664 | * into account. */ |
665 | static const char * | | 665 | static const char * |
666 | separator_comma(const char *p) | | 666 | separator_comma(const char *p) |
667 | { | | 667 | { |
668 | int nest = 0; | | 668 | int nest = 0; |
669 | while (*p != '\0') { | | 669 | while (*p != '\0') { |
670 | if ((*p == '}' || *p == ',') && nest == 0) | | 670 | if ((*p == '}' || *p == ',') && nest == 0) |
671 | break; | | 671 | break; |
672 | if (*p == '{') | | 672 | if (*p == '{') |
673 | nest++; | | 673 | nest++; |
674 | if (*p == '}') | | 674 | if (*p == '}') |
675 | nest--; | | 675 | nest--; |
676 | p++; | | 676 | p++; |
677 | } | | 677 | } |
678 | return p; | | 678 | return p; |
679 | } | | 679 | } |
680 | | | 680 | |
681 | static Boolean | | 681 | static Boolean |
682 | contains_wildcard(const char *p) | | 682 | contains_wildcard(const char *p) |
683 | { | | 683 | { |
684 | for (; *p != '\0'; p++) { | | 684 | for (; *p != '\0'; p++) { |
685 | switch (*p) { | | 685 | switch (*p) { |
686 | case '*': | | 686 | case '*': |
687 | case '?': | | 687 | case '?': |
688 | case '{': | | 688 | case '{': |
689 | case '[': | | 689 | case '[': |
690 | return TRUE; | | 690 | return TRUE; |
691 | } | | 691 | } |
692 | } | | 692 | } |
693 | return FALSE; | | 693 | return FALSE; |
694 | } | | 694 | } |
695 | | | 695 | |
696 | static char * | | 696 | static char * |
697 | concat3(const char *a, size_t a_len, const char *b, size_t b_len, | | 697 | concat3(const char *a, size_t a_len, const char *b, size_t b_len, |
698 | const char *c, size_t c_len) | | 698 | const char *c, size_t c_len) |
699 | { | | 699 | { |
700 | size_t s_len = a_len + b_len + c_len; | | 700 | size_t s_len = a_len + b_len + c_len; |
701 | char *s = bmake_malloc(s_len + 1); | | 701 | char *s = bmake_malloc(s_len + 1); |
702 | memcpy(s, a, a_len); | | 702 | memcpy(s, a, a_len); |
703 | memcpy(s + a_len, b, b_len); | | 703 | memcpy(s + a_len, b, b_len); |
704 | memcpy(s + a_len + b_len, c, c_len); | | 704 | memcpy(s + a_len + b_len, c, c_len); |
705 | s[s_len] = '\0'; | | 705 | s[s_len] = '\0'; |
706 | return s; | | 706 | return s; |
707 | } | | 707 | } |
708 | | | 708 | |
709 | /*- | | 709 | /*- |
710 | *----------------------------------------------------------------------- | | 710 | *----------------------------------------------------------------------- |
711 | * DirExpandCurly -- | | 711 | * DirExpandCurly -- |
712 | * Expand curly braces like the C shell. Does this recursively. | | 712 | * Expand curly braces like the C shell. Does this recursively. |
713 | * Note the special case: if after the piece of the curly brace is | | 713 | * Note the special case: if after the piece of the curly brace is |
714 | * done there are no wildcard characters in the result, the result is | | 714 | * done there are no wildcard characters in the result, the result is |
715 | * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. | | 715 | * placed on the list WITHOUT CHECKING FOR ITS EXISTENCE. |
716 | * | | 716 | * |
717 | * Input: | | 717 | * Input: |
718 | * word Entire word to expand | | 718 | * word Entire word to expand |
719 | * brace First curly brace in it | | 719 | * brace First curly brace in it |
720 | * path Search path to use | | 720 | * path Search path to use |
721 | * expansions Place to store the expansions | | 721 | * expansions Place to store the expansions |
722 | * | | 722 | * |
723 | * Results: | | 723 | * Results: |
724 | * None. | | 724 | * None. |
725 | * | | 725 | * |
726 | * Side Effects: | | 726 | * Side Effects: |
727 | * The given list is filled with the expansions... | | 727 | * The given list is filled with the expansions... |
728 | * | | 728 | * |
729 | *----------------------------------------------------------------------- | | 729 | *----------------------------------------------------------------------- |
730 | */ | | 730 | */ |
731 | static void | | 731 | static void |
732 | DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) | | 732 | DirExpandCurly(const char *word, const char *brace, Lst path, Lst expansions) |
733 | { | | 733 | { |
734 | const char *prefix, *middle, *piece, *middle_end, *suffix; | | 734 | const char *prefix, *middle, *piece, *middle_end, *suffix; |
735 | size_t prefix_len, suffix_len; | | 735 | size_t prefix_len, suffix_len; |
736 | | | 736 | |
737 | /* Split the word into prefix '{' middle '}' suffix. */ | | 737 | /* Split the word into prefix '{' middle '}' suffix. */ |
738 | | | 738 | |
739 | middle = brace + 1; | | 739 | middle = brace + 1; |
740 | middle_end = closing_brace(middle); | | 740 | middle_end = closing_brace(middle); |
741 | if (*middle_end == '\0') { | | 741 | if (*middle_end == '\0') { |
742 | Error("Unterminated {} clause \"%s\"", middle); | | 742 | Error("Unterminated {} clause \"%s\"", middle); |
743 | return; | | 743 | return; |
744 | } | | 744 | } |
745 | | | 745 | |
746 | prefix = word; | | 746 | prefix = word; |
747 | prefix_len = (size_t)(brace - prefix); | | 747 | prefix_len = (size_t)(brace - prefix); |
748 | suffix = middle_end + 1; | | 748 | suffix = middle_end + 1; |
749 | suffix_len = strlen(suffix); | | 749 | suffix_len = strlen(suffix); |
750 | | | 750 | |
751 | /* Split the middle into pieces, separated by commas. */ | | 751 | /* Split the middle into pieces, separated by commas. */ |
752 | | | 752 | |
753 | piece = middle; | | 753 | piece = middle; |
754 | while (piece < middle_end + 1) { | | 754 | while (piece < middle_end + 1) { |
755 | const char *piece_end = separator_comma(piece); | | 755 | const char *piece_end = separator_comma(piece); |
756 | size_t piece_len = (size_t)(piece_end - piece); | | 756 | size_t piece_len = (size_t)(piece_end - piece); |
757 | | | 757 | |
758 | char *file = concat3(prefix, prefix_len, piece, piece_len, | | 758 | char *file = concat3(prefix, prefix_len, piece, piece_len, |
759 | suffix, suffix_len); | | 759 | suffix, suffix_len); |
760 | | | 760 | |
761 | if (contains_wildcard(file)) { | | 761 | if (contains_wildcard(file)) { |
762 | Dir_Expand(file, path, expansions); | | 762 | Dir_Expand(file, path, expansions); |
763 | free(file); | | 763 | free(file); |
764 | } else { | | 764 | } else { |
765 | Lst_AppendS(expansions, file); | | 765 | Lst_AppendS(expansions, file); |
766 | } | | 766 | } |
767 | | | 767 | |
768 | piece = piece_end + 1; /* skip over the comma or closing brace */ | | 768 | piece = piece_end + 1; /* skip over the comma or closing brace */ |
769 | } | | 769 | } |
770 | } | | 770 | } |
771 | | | 771 | |
772 | | | 772 | |
773 | /*- | | 773 | /*- |
774 | *----------------------------------------------------------------------- | | 774 | *----------------------------------------------------------------------- |
775 | * DirExpandInt -- | | 775 | * DirExpandInt -- |
776 | * Internal expand routine. Passes through the directories in the | | 776 | * Internal expand routine. Passes through the directories in the |
777 | * path one by one, calling DirMatchFiles for each. NOTE: This still | | 777 | * path one by one, calling DirMatchFiles for each. NOTE: This still |
778 | * doesn't handle patterns in directories... | | 778 | * doesn't handle patterns in directories... |
779 | * | | 779 | * |
780 | * Input: | | 780 | * Input: |
781 | * word Word to expand | | 781 | * word Word to expand |
782 | * path Path on which to look | | 782 | * path Path on which to look |
783 | * expansions Place to store the result | | 783 | * expansions Place to store the result |
784 | * | | 784 | * |
785 | * Results: | | 785 | * Results: |
786 | * None. | | 786 | * None. |
787 | * | | 787 | * |
788 | * Side Effects: | | 788 | * Side Effects: |
789 | * Things are added to the expansions list. | | 789 | * Things are added to the expansions list. |
790 | * | | 790 | * |
791 | *----------------------------------------------------------------------- | | 791 | *----------------------------------------------------------------------- |
792 | */ | | 792 | */ |
793 | static void | | 793 | static void |
794 | DirExpandInt(const char *word, Lst path, Lst expansions) | | 794 | DirExpandInt(const char *word, Lst path, Lst expansions) |
795 | { | | 795 | { |
796 | LstNode ln; /* Current node */ | | 796 | LstNode ln; /* Current node */ |
797 | | | 797 | |
798 | if (Lst_Open(path) == SUCCESS) { | | 798 | Lst_OpenS(path); |
799 | while ((ln = Lst_NextS(path)) != NULL) { | | 799 | while ((ln = Lst_NextS(path)) != NULL) { |
800 | Path *p = Lst_DatumS(ln); | | 800 | Path *p = Lst_DatumS(ln); |
801 | DirMatchFiles(word, p, expansions); | | 801 | DirMatchFiles(word, p, expansions); |
802 | } | | | |
803 | Lst_CloseS(path); | | | |
804 | } | | 802 | } |
| | | 803 | Lst_CloseS(path); |
805 | } | | 804 | } |
806 | | | 805 | |
807 | /* Print a word in the list of expansions. | | 806 | /* Print a word in the list of expansions. |
808 | * Callback for Dir_Expand when DEBUG(DIR), via Lst_ForEach. */ | | 807 | * Callback for Dir_Expand when DEBUG(DIR), via Lst_ForEach. */ |
809 | static int | | 808 | static int |
810 | DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED) | | 809 | DirPrintWord(void *word, void *dummy MAKE_ATTR_UNUSED) |
811 | { | | 810 | { |
812 | fprintf(debug_file, "%s ", (char *)word); | | 811 | fprintf(debug_file, "%s ", (char *)word); |
813 | | | 812 | |
814 | return 0; | | 813 | return 0; |
815 | } | | 814 | } |
816 | | | 815 | |
817 | /*- | | 816 | /*- |
818 | *----------------------------------------------------------------------- | | 817 | *----------------------------------------------------------------------- |
819 | * Dir_Expand -- | | 818 | * Dir_Expand -- |
820 | * Expand the given word into a list of words by globbing it looking | | 819 | * Expand the given word into a list of words by globbing it looking |
821 | * in the directories on the given search path. | | 820 | * in the directories on the given search path. |
822 | * | | 821 | * |
823 | * Input: | | 822 | * Input: |
824 | * word the word to expand | | 823 | * word the word to expand |
825 | * path the list of directories in which to find the | | 824 | * path the list of directories in which to find the |
826 | * resulting files | | 825 | * resulting files |
827 | * expansions the list on which to place the results | | 826 | * expansions the list on which to place the results |
828 | * | | 827 | * |
829 | * Results: | | 828 | * Results: |
830 | * A list of words consisting of the files which exist along the search | | 829 | * A list of words consisting of the files which exist along the search |
831 | * path matching the given pattern. | | 830 | * path matching the given pattern. |
832 | * | | 831 | * |
833 | * Side Effects: | | 832 | * Side Effects: |
834 | * Directories may be opened. Who knows? | | 833 | * Directories may be opened. Who knows? |
835 | * Undefined behavior if the word is really in read-only memory. | | 834 | * Undefined behavior if the word is really in read-only memory. |
836 | *----------------------------------------------------------------------- | | 835 | *----------------------------------------------------------------------- |
837 | */ | | 836 | */ |
838 | void | | 837 | void |
839 | Dir_Expand(const char *word, Lst path, Lst expansions) | | 838 | Dir_Expand(const char *word, Lst path, Lst expansions) |
840 | { | | 839 | { |
841 | const char *cp; | | 840 | const char *cp; |
842 | | | 841 | |
| | | 842 | assert(path != NULL); |
| | | 843 | assert(expansions != NULL); |
| | | 844 | |
843 | DIR_DEBUG1("Expanding \"%s\"... ", word); | | 845 | DIR_DEBUG1("Expanding \"%s\"... ", word); |
844 | | | 846 | |
845 | cp = strchr(word, '{'); | | 847 | cp = strchr(word, '{'); |
846 | if (cp) { | | 848 | if (cp) { |
847 | DirExpandCurly(word, cp, path, expansions); | | 849 | DirExpandCurly(word, cp, path, expansions); |
848 | } else { | | 850 | } else { |
849 | cp = strchr(word, '/'); | | 851 | cp = strchr(word, '/'); |
850 | if (cp) { | | 852 | if (cp) { |
851 | /* | | 853 | /* |
852 | * The thing has a directory component -- find the first wildcard | | 854 | * The thing has a directory component -- find the first wildcard |
853 | * in the string. | | 855 | * in the string. |
854 | */ | | 856 | */ |
855 | for (cp = word; *cp; cp++) { | | 857 | for (cp = word; *cp; cp++) { |
856 | if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { | | 858 | if (*cp == '?' || *cp == '[' || *cp == '*' || *cp == '{') { |
857 | break; | | 859 | break; |
858 | } | | 860 | } |
859 | } | | 861 | } |
860 | if (*cp == '{') { | | 862 | if (*cp == '{') { |
861 | /* | | 863 | /* |
862 | * This one will be fun. | | 864 | * This one will be fun. |
863 | */ | | 865 | */ |
864 | DirExpandCurly(word, cp, path, expansions); | | 866 | DirExpandCurly(word, cp, path, expansions); |
865 | return; | | 867 | return; |
866 | } else if (*cp != '\0') { | | 868 | } else if (*cp != '\0') { |
867 | /* | | 869 | /* |
868 | * Back up to the start of the component | | 870 | * Back up to the start of the component |
869 | */ | | 871 | */ |
870 | while (cp > word && *cp != '/') { | | 872 | while (cp > word && *cp != '/') { |
871 | cp--; | | 873 | cp--; |
872 | } | | 874 | } |
873 | if (cp != word) { | | 875 | if (cp != word) { |
874 | char sc; | | 876 | char sc; |
875 | char *dirpath; | | 877 | char *dirpath; |
876 | /* | | 878 | /* |
877 | * If the glob isn't in the first component, try and find | | 879 | * If the glob isn't in the first component, try and find |
878 | * all the components up to the one with a wildcard. | | 880 | * all the components up to the one with a wildcard. |
879 | */ | | 881 | */ |
880 | sc = cp[1]; | | 882 | sc = cp[1]; |
881 | ((char *)UNCONST(cp))[1] = '\0'; | | 883 | ((char *)UNCONST(cp))[1] = '\0'; |
882 | dirpath = Dir_FindFile(word, path); | | 884 | dirpath = Dir_FindFile(word, path); |
883 | ((char *)UNCONST(cp))[1] = sc; | | 885 | ((char *)UNCONST(cp))[1] = sc; |
884 | /* | | 886 | /* |
885 | * dirpath is null if can't find the leading component | | 887 | * dirpath is null if can't find the leading component |
886 | * XXX: Dir_FindFile won't find internal components. | | 888 | * XXX: Dir_FindFile won't find internal components. |
887 | * i.e. if the path contains ../Etc/Object and we're | | 889 | * i.e. if the path contains ../Etc/Object and we're |
888 | * looking for Etc, it won't be found. Ah well. | | 890 | * looking for Etc, it won't be found. Ah well. |
889 | * Probably not important. | | 891 | * Probably not important. |
890 | */ | | 892 | */ |
891 | if (dirpath != NULL) { | | 893 | if (dirpath != NULL) { |
892 | char *dp = &dirpath[strlen(dirpath) - 1]; | | 894 | char *dp = &dirpath[strlen(dirpath) - 1]; |
893 | if (*dp == '/') | | 895 | if (*dp == '/') |
894 | *dp = '\0'; | | 896 | *dp = '\0'; |
895 | path = Lst_Init(); | | 897 | path = Lst_Init(); |
896 | (void)Dir_AddDir(path, dirpath); | | 898 | (void)Dir_AddDir(path, dirpath); |
897 | DirExpandInt(cp + 1, path, expansions); | | 899 | DirExpandInt(cp + 1, path, expansions); |
898 | Lst_FreeS(path); | | 900 | Lst_FreeS(path); |
899 | } | | 901 | } |
900 | } else { | | 902 | } else { |
901 | /* | | 903 | /* |
902 | * Start the search from the local directory | | 904 | * Start the search from the local directory |
903 | */ | | 905 | */ |
904 | DirExpandInt(word, path, expansions); | | 906 | DirExpandInt(word, path, expansions); |
905 | } | | 907 | } |
906 | } else { | | 908 | } else { |
907 | /* | | 909 | /* |
908 | * Return the file -- this should never happen. | | 910 | * Return the file -- this should never happen. |
909 | */ | | 911 | */ |
910 | DirExpandInt(word, path, expansions); | | 912 | DirExpandInt(word, path, expansions); |
911 | } | | 913 | } |
912 | } else { | | 914 | } else { |
913 | /* | | 915 | /* |
914 | * First the files in dot | | 916 | * First the files in dot |
915 | */ | | 917 | */ |
916 | DirMatchFiles(word, dot, expansions); | | 918 | DirMatchFiles(word, dot, expansions); |
917 | | | 919 | |
918 | /* | | 920 | /* |
919 | * Then the files in every other directory on the path. | | 921 | * Then the files in every other directory on the path. |
920 | */ | | 922 | */ |
921 | DirExpandInt(word, path, expansions); | | 923 | DirExpandInt(word, path, expansions); |
922 | } | | 924 | } |
923 | } | | 925 | } |
924 | if (DEBUG(DIR)) { | | 926 | if (DEBUG(DIR)) { |
925 | Lst_ForEach(expansions, DirPrintWord, NULL); | | 927 | Lst_ForEach(expansions, DirPrintWord, NULL); |
926 | fprintf(debug_file, "\n"); | | 928 | fprintf(debug_file, "\n"); |
927 | } | | 929 | } |
928 | } | | 930 | } |
929 | | | 931 | |
930 | /*- | | 932 | /*- |
931 | *----------------------------------------------------------------------- | | 933 | *----------------------------------------------------------------------- |
932 | * DirLookup -- | | 934 | * DirLookup -- |
933 | * Find if the file with the given name exists in the given path. | | 935 | * Find if the file with the given name exists in the given path. |
934 | * | | 936 | * |
935 | * Results: | | 937 | * Results: |
936 | * The path to the file or NULL. This path is guaranteed to be in a | | 938 | * The path to the file or NULL. This path is guaranteed to be in a |
937 | * different part of memory than name and so may be safely free'd. | | 939 | * different part of memory than name and so may be safely free'd. |
938 | * | | 940 | * |
939 | * Side Effects: | | 941 | * Side Effects: |
940 | * None. | | 942 | * None. |
941 | *----------------------------------------------------------------------- | | 943 | *----------------------------------------------------------------------- |
942 | */ | | 944 | */ |
943 | static char * | | 945 | static char * |
944 | DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, | | 946 | DirLookup(Path *p, const char *name MAKE_ATTR_UNUSED, const char *cp, |
945 | Boolean hasSlash MAKE_ATTR_UNUSED) | | 947 | Boolean hasSlash MAKE_ATTR_UNUSED) |
946 | { | | 948 | { |
947 | char *file; /* the current filename to check */ | | 949 | char *file; /* the current filename to check */ |
948 | | | 950 | |
949 | DIR_DEBUG1(" %s ...\n", p->name); | | 951 | DIR_DEBUG1(" %s ...\n", p->name); |
950 | | | 952 | |
951 | if (Hash_FindEntry(&p->files, cp) == NULL) | | 953 | if (Hash_FindEntry(&p->files, cp) == NULL) |
952 | return NULL; | | 954 | return NULL; |
953 | | | 955 | |
954 | file = str_concat3(p->name, "/", cp); | | 956 | file = str_concat3(p->name, "/", cp); |
955 | DIR_DEBUG1(" returning %s\n", file); | | 957 | DIR_DEBUG1(" returning %s\n", file); |
956 | p->hits += 1; | | 958 | p->hits += 1; |
957 | hits += 1; | | 959 | hits += 1; |
958 | return file; | | 960 | return file; |
959 | } | | 961 | } |
960 | | | 962 | |
961 | | | 963 | |
962 | /*- | | 964 | /*- |
963 | *----------------------------------------------------------------------- | | 965 | *----------------------------------------------------------------------- |
964 | * DirLookupSubdir -- | | 966 | * DirLookupSubdir -- |
965 | * Find if the file with the given name exists in the given path. | | 967 | * Find if the file with the given name exists in the given path. |
966 | * | | 968 | * |
967 | * Results: | | 969 | * Results: |
968 | * The path to the file or NULL. This path is guaranteed to be in a | | 970 | * The path to the file or NULL. This path is guaranteed to be in a |
969 | * different part of memory than name and so may be safely free'd. | | 971 | * different part of memory than name and so may be safely free'd. |
970 | * | | 972 | * |
971 | * Side Effects: | | 973 | * Side Effects: |
972 | * If the file is found, it is added in the modification times hash | | 974 | * If the file is found, it is added in the modification times hash |
973 | * table. | | 975 | * table. |
974 | *----------------------------------------------------------------------- | | 976 | *----------------------------------------------------------------------- |
975 | */ | | 977 | */ |
976 | static char * | | 978 | static char * |
977 | DirLookupSubdir(Path *p, const char *name) | | 979 | DirLookupSubdir(Path *p, const char *name) |
978 | { | | 980 | { |
979 | struct stat stb; /* Buffer for stat, if necessary */ | | 981 | struct stat stb; /* Buffer for stat, if necessary */ |
980 | char *file; /* the current filename to check */ | | 982 | char *file; /* the current filename to check */ |
981 | | | 983 | |
982 | if (p != dot) { | | 984 | if (p != dot) { |
983 | file = str_concat3(p->name, "/", name); | | 985 | file = str_concat3(p->name, "/", name); |
984 | } else { | | 986 | } else { |
985 | /* | | 987 | /* |
986 | * Checking in dot -- DON'T put a leading ./ on the thing. | | 988 | * Checking in dot -- DON'T put a leading ./ on the thing. |
987 | */ | | 989 | */ |
988 | file = bmake_strdup(name); | | 990 | file = bmake_strdup(name); |
989 | } | | 991 | } |
990 | | | 992 | |
991 | DIR_DEBUG1("checking %s ...\n", file); | | 993 | DIR_DEBUG1("checking %s ...\n", file); |
992 | | | 994 | |
993 | if (cached_stat(file, &stb) == 0) { | | 995 | if (cached_stat(file, &stb) == 0) { |
994 | nearmisses += 1; | | 996 | nearmisses += 1; |
995 | return file; | | 997 | return file; |
996 | } | | 998 | } |
997 | free(file); | | 999 | free(file); |
998 | return NULL; | | 1000 | return NULL; |
999 | } | | 1001 | } |
1000 | | | 1002 | |
1001 | /*- | | 1003 | /*- |
1002 | *----------------------------------------------------------------------- | | 1004 | *----------------------------------------------------------------------- |
1003 | * DirLookupAbs -- | | 1005 | * DirLookupAbs -- |
1004 | * Find if the file with the given name exists in the given path. | | 1006 | * Find if the file with the given name exists in the given path. |
1005 | * | | 1007 | * |
1006 | * Results: | | 1008 | * Results: |
1007 | * The path to the file, the empty string or NULL. If the file is | | 1009 | * The path to the file, the empty string or NULL. If the file is |
1008 | * the empty string, the search should be terminated. | | 1010 | * the empty string, the search should be terminated. |
1009 | * This path is guaranteed to be in a different part of memory | | 1011 | * This path is guaranteed to be in a different part of memory |
1010 | * than name and so may be safely free'd. | | 1012 | * than name and so may be safely free'd. |
1011 | * | | 1013 | * |
1012 | * Side Effects: | | 1014 | * Side Effects: |
1013 | * None. | | 1015 | * None. |
1014 | *----------------------------------------------------------------------- | | 1016 | *----------------------------------------------------------------------- |
1015 | */ | | 1017 | */ |
1016 | static char * | | 1018 | static char * |
1017 | DirLookupAbs(Path *p, const char *name, const char *cp) | | 1019 | DirLookupAbs(Path *p, const char *name, const char *cp) |
1018 | { | | 1020 | { |
1019 | char *p1; /* pointer into p->name */ | | 1021 | char *p1; /* pointer into p->name */ |
1020 | const char *p2; /* pointer into name */ | | 1022 | const char *p2; /* pointer into name */ |
1021 | | | 1023 | |
1022 | DIR_DEBUG1(" %s ...\n", p->name); | | 1024 | DIR_DEBUG1(" %s ...\n", p->name); |
1023 | | | 1025 | |
1024 | /* | | 1026 | /* |
1025 | * If the file has a leading path component and that component | | 1027 | * If the file has a leading path component and that component |
1026 | * exactly matches the entire name of the current search | | 1028 | * exactly matches the entire name of the current search |
1027 | * directory, we can attempt another cache lookup. And if we don't | | 1029 | * directory, we can attempt another cache lookup. And if we don't |
1028 | * have a hit, we can safely assume the file does not exist at all. | | 1030 | * have a hit, we can safely assume the file does not exist at all. |
1029 | */ | | 1031 | */ |
1030 | for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { | | 1032 | for (p1 = p->name, p2 = name; *p1 && *p1 == *p2; p1++, p2++) { |
1031 | continue; | | 1033 | continue; |
1032 | } | | 1034 | } |
1033 | if (*p1 != '\0' || p2 != cp - 1) { | | 1035 | if (*p1 != '\0' || p2 != cp - 1) { |
1034 | return NULL; | | 1036 | return NULL; |
1035 | } | | 1037 | } |
1036 | | | 1038 | |
1037 | if (Hash_FindEntry(&p->files, cp) == NULL) { | | 1039 | if (Hash_FindEntry(&p->files, cp) == NULL) { |
1038 | DIR_DEBUG0(" must be here but isn't -- returning\n"); | | 1040 | DIR_DEBUG0(" must be here but isn't -- returning\n"); |
1039 | /* Return empty string: terminates search */ | | 1041 | /* Return empty string: terminates search */ |
1040 | return bmake_strdup(""); | | 1042 | return bmake_strdup(""); |
1041 | } | | 1043 | } |
1042 | | | 1044 | |
1043 | p->hits += 1; | | 1045 | p->hits += 1; |
1044 | hits += 1; | | 1046 | hits += 1; |
1045 | DIR_DEBUG1(" returning %s\n", name); | | 1047 | DIR_DEBUG1(" returning %s\n", name); |
1046 | return bmake_strdup(name); | | 1048 | return bmake_strdup(name); |
1047 | } | | 1049 | } |
1048 | | | 1050 | |
1049 | /*- | | 1051 | /*- |
1050 | *----------------------------------------------------------------------- | | 1052 | *----------------------------------------------------------------------- |
1051 | * DirFindDot -- | | 1053 | * DirFindDot -- |
1052 | * Find the file given on "." or curdir | | 1054 | * Find the file given on "." or curdir |
1053 | * | | 1055 | * |
1054 | * Results: | | 1056 | * Results: |
1055 | * The path to the file or NULL. This path is guaranteed to be in a | | 1057 | * The path to the file or NULL. This path is guaranteed to be in a |
1056 | * different part of memory than name and so may be safely free'd. | | 1058 | * different part of memory than name and so may be safely free'd. |
1057 | * | | 1059 | * |
1058 | * Side Effects: | | 1060 | * Side Effects: |
1059 | * Hit counts change | | 1061 | * Hit counts change |
1060 | *----------------------------------------------------------------------- | | 1062 | *----------------------------------------------------------------------- |
1061 | */ | | 1063 | */ |
1062 | static char * | | 1064 | static char * |
1063 | DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) | | 1065 | DirFindDot(Boolean hasSlash MAKE_ATTR_UNUSED, const char *name, const char *cp) |
1064 | { | | 1066 | { |
1065 | | | 1067 | |
1066 | if (Hash_FindEntry(&dot->files, cp) != NULL) { | | 1068 | if (Hash_FindEntry(&dot->files, cp) != NULL) { |
1067 | DIR_DEBUG0(" in '.'\n"); | | 1069 | DIR_DEBUG0(" in '.'\n"); |
1068 | hits += 1; | | 1070 | hits += 1; |
1069 | dot->hits += 1; | | 1071 | dot->hits += 1; |
1070 | return bmake_strdup(name); | | 1072 | return bmake_strdup(name); |
1071 | } | | 1073 | } |
1072 | if (cur && Hash_FindEntry(&cur->files, cp) != NULL) { | | 1074 | if (cur && Hash_FindEntry(&cur->files, cp) != NULL) { |
1073 | DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name); | | 1075 | DIR_DEBUG1(" in ${.CURDIR} = %s\n", cur->name); |
1074 | hits += 1; | | 1076 | hits += 1; |
1075 | cur->hits += 1; | | 1077 | cur->hits += 1; |
1076 | return str_concat3(cur->name, "/", cp); | | 1078 | return str_concat3(cur->name, "/", cp); |
1077 | } | | 1079 | } |
1078 | | | 1080 | |
1079 | return NULL; | | 1081 | return NULL; |
1080 | } | | 1082 | } |
1081 | | | 1083 | |
1082 | /*- | | 1084 | /*- |
1083 | *----------------------------------------------------------------------- | | 1085 | *----------------------------------------------------------------------- |
1084 | * Dir_FindFile -- | | 1086 | * Dir_FindFile -- |
1085 | * Find the file with the given name along the given search path. | | 1087 | * Find the file with the given name along the given search path. |
1086 | * | | 1088 | * |
1087 | * Input: | | 1089 | * Input: |
1088 | * name the file to find | | 1090 | * name the file to find |
1089 | * path the Lst of directories to search | | 1091 | * path the Lst of directories to search |
1090 | * | | 1092 | * |
1091 | * Results: | | 1093 | * Results: |
1092 | * The path to the file or NULL. This path is guaranteed to be in a | | 1094 | * The path to the file or NULL. This path is guaranteed to be in a |
1093 | * different part of memory than name and so may be safely free'd. | | 1095 | * different part of memory than name and so may be safely free'd. |
1094 | * | | 1096 | * |
1095 | * Side Effects: | | 1097 | * Side Effects: |
1096 | * If the file is found in a directory which is not on the path | | 1098 | * If the file is found in a directory which is not on the path |
1097 | * already (either 'name' is absolute or it is a relative path | | 1099 | * already (either 'name' is absolute or it is a relative path |
1098 | * [ dir1/.../dirn/file ] which exists below one of the directories | | 1100 | * [ dir1/.../dirn/file ] which exists below one of the directories |
1099 | * already on the search path), its directory is added to the end | | 1101 | * already on the search path), its directory is added to the end |
1100 | * of the path on the assumption that there will be more files in | | 1102 | * of the path on the assumption that there will be more files in |
1101 | * that directory later on. Sometimes this is true. Sometimes not. | | 1103 | * that directory later on. Sometimes this is true. Sometimes not. |
1102 | *----------------------------------------------------------------------- | | 1104 | *----------------------------------------------------------------------- |
1103 | */ | | 1105 | */ |
1104 | char * | | 1106 | char * |
1105 | Dir_FindFile(const char *name, Lst path) | | 1107 | Dir_FindFile(const char *name, Lst path) |
1106 | { | | 1108 | { |
1107 | LstNode ln; /* a list element */ | | 1109 | LstNode ln; /* a list element */ |
1108 | char *file; /* the current filename to check */ | | 1110 | char *file; /* the current filename to check */ |
1109 | Path *p; /* current path member */ | | 1111 | Path *p; /* current path member */ |
1110 | const char *cp; /* Terminal name of file */ | | 1112 | const char *cp; /* Terminal name of file */ |
1111 | Boolean hasLastDot = FALSE; /* true we should search dot last */ | | 1113 | Boolean hasLastDot = FALSE; /* true we should search dot last */ |
1112 | Boolean hasSlash; /* true if 'name' contains a / */ | | 1114 | Boolean hasSlash; /* true if 'name' contains a / */ |
1113 | struct stat stb; /* Buffer for stat, if necessary */ | | 1115 | struct stat stb; /* Buffer for stat, if necessary */ |
1114 | const char *trailing_dot = "."; | | 1116 | const char *trailing_dot = "."; |
1115 | | | 1117 | |
1116 | /* | | 1118 | /* |
1117 | * Find the final component of the name and note whether it has a | | 1119 | * Find the final component of the name and note whether it has a |
1118 | * slash in it (the name, I mean) | | 1120 | * slash in it (the name, I mean) |
1119 | */ | | 1121 | */ |
1120 | cp = strrchr(name, '/'); | | 1122 | cp = strrchr(name, '/'); |
1121 | if (cp) { | | 1123 | if (cp) { |
1122 | hasSlash = TRUE; | | 1124 | hasSlash = TRUE; |
1123 | cp += 1; | | 1125 | cp += 1; |
1124 | } else { | | 1126 | } else { |
1125 | hasSlash = FALSE; | | 1127 | hasSlash = FALSE; |
1126 | cp = name; | | 1128 | cp = name; |
1127 | } | | 1129 | } |
1128 | | | 1130 | |
1129 | DIR_DEBUG1("Searching for %s ...", name); | | 1131 | DIR_DEBUG1("Searching for %s ...", name); |
1130 | | | 1132 | |
1131 | if (Lst_Open(path) == FAILURE) { | | 1133 | if (path == NULL) { |
1132 | DIR_DEBUG0("couldn't open path, file not found\n"); | | 1134 | DIR_DEBUG0("couldn't open path, file not found\n"); |
1133 | misses += 1; | | 1135 | misses += 1; |
1134 | return NULL; | | 1136 | return NULL; |
1135 | } | | 1137 | } |
1136 | | | 1138 | |
| | | 1139 | Lst_OpenS(path); |
1137 | if ((ln = Lst_First(path)) != NULL) { | | 1140 | if ((ln = Lst_First(path)) != NULL) { |
1138 | p = Lst_DatumS(ln); | | 1141 | p = Lst_DatumS(ln); |
1139 | if (p == dotLast) { | | 1142 | if (p == dotLast) { |
1140 | hasLastDot = TRUE; | | 1143 | hasLastDot = TRUE; |
1141 | DIR_DEBUG0("[dot last]..."); | | 1144 | DIR_DEBUG0("[dot last]..."); |
1142 | } | | 1145 | } |
1143 | } | | 1146 | } |
1144 | DIR_DEBUG0("\n"); | | 1147 | DIR_DEBUG0("\n"); |
1145 | | | 1148 | |
1146 | /* | | 1149 | /* |
1147 | * If there's no leading directory components or if the leading | | 1150 | * If there's no leading directory components or if the leading |
1148 | * directory component is exactly `./', consult the cached contents | | 1151 | * directory component is exactly `./', consult the cached contents |
1149 | * of each of the directories on the search path. | | 1152 | * of each of the directories on the search path. |
1150 | */ | | 1153 | */ |
1151 | if (!hasSlash || (cp - name == 2 && *name == '.')) { | | 1154 | if (!hasSlash || (cp - name == 2 && *name == '.')) { |
1152 | /* | | 1155 | /* |
1153 | * We look through all the directories on the path seeking one which | | 1156 | * We look through all the directories on the path seeking one which |
1154 | * contains the final component of the given name. If such a beast | | 1157 | * contains the final component of the given name. If such a beast |
1155 | * is found, we concatenate the directory name and the final | | 1158 | * is found, we concatenate the directory name and the final |
1156 | * component and return the resulting string. If we don't find any | | 1159 | * component and return the resulting string. If we don't find any |
1157 | * such thing, we go on to phase two... | | 1160 | * such thing, we go on to phase two... |
1158 | * | | 1161 | * |
1159 | * No matter what, we always look for the file in the current | | 1162 | * No matter what, we always look for the file in the current |
1160 | * directory before anywhere else (unless we found the magic | | 1163 | * directory before anywhere else (unless we found the magic |
1161 | * DOTLAST path, in which case we search it last) and we *do not* | | 1164 | * DOTLAST path, in which case we search it last) and we *do not* |
1162 | * add the ./ to it if it exists. | | 1165 | * add the ./ to it if it exists. |
1163 | * This is so there are no conflicts between what the user | | 1166 | * This is so there are no conflicts between what the user |
1164 | * specifies (fish.c) and what pmake finds (./fish.c). | | 1167 | * specifies (fish.c) and what pmake finds (./fish.c). |
1165 | */ | | 1168 | */ |
1166 | if (!hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { | | 1169 | if (!hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { |
1167 | Lst_CloseS(path); | | 1170 | Lst_CloseS(path); |
1168 | return file; | | 1171 | return file; |
1169 | } | | 1172 | } |
1170 | | | 1173 | |
1171 | while ((ln = Lst_NextS(path)) != NULL) { | | 1174 | while ((ln = Lst_NextS(path)) != NULL) { |
1172 | p = Lst_DatumS(ln); | | 1175 | p = Lst_DatumS(ln); |
1173 | if (p == dotLast) | | 1176 | if (p == dotLast) |
1174 | continue; | | 1177 | continue; |
1175 | if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { | | 1178 | if ((file = DirLookup(p, name, cp, hasSlash)) != NULL) { |
1176 | Lst_CloseS(path); | | 1179 | Lst_CloseS(path); |
1177 | return file; | | 1180 | return file; |
1178 | } | | 1181 | } |
1179 | } | | 1182 | } |
1180 | | | 1183 | |
1181 | if (hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { | | 1184 | if (hasLastDot && (file = DirFindDot(hasSlash, name, cp)) != NULL) { |
1182 | Lst_CloseS(path); | | 1185 | Lst_CloseS(path); |
1183 | return file; | | 1186 | return file; |
1184 | } | | 1187 | } |
1185 | } | | 1188 | } |
1186 | Lst_CloseS(path); | | 1189 | Lst_CloseS(path); |
1187 | | | 1190 | |
1188 | /* | | 1191 | /* |
1189 | * We didn't find the file on any directory in the search path. | | 1192 | * We didn't find the file on any directory in the search path. |
1190 | * If the name doesn't contain a slash, that means it doesn't exist. | | 1193 | * If the name doesn't contain a slash, that means it doesn't exist. |
1191 | * If it *does* contain a slash, however, there is still hope: it | | 1194 | * If it *does* contain a slash, however, there is still hope: it |
1192 | * could be in a subdirectory of one of the members of the search | | 1195 | * could be in a subdirectory of one of the members of the search |
1193 | * path. (eg. /usr/include and sys/types.h. The above search would | | 1196 | * path. (eg. /usr/include and sys/types.h. The above search would |
1194 | * fail to turn up types.h in /usr/include, but it *is* in | | 1197 | * fail to turn up types.h in /usr/include, but it *is* in |
1195 | * /usr/include/sys/types.h). | | 1198 | * /usr/include/sys/types.h). |
1196 | * [ This no longer applies: If we find such a beast, we assume there | | 1199 | * [ This no longer applies: If we find such a beast, we assume there |
1197 | * will be more (what else can we assume?) and add all but the last | | 1200 | * will be more (what else can we assume?) and add all but the last |
1198 | * component of the resulting name onto the search path (at the | | 1201 | * component of the resulting name onto the search path (at the |
1199 | * end).] | | 1202 | * end).] |
1200 | * This phase is only performed if the file is *not* absolute. | | 1203 | * This phase is only performed if the file is *not* absolute. |
1201 | */ | | 1204 | */ |
1202 | if (!hasSlash) { | | 1205 | if (!hasSlash) { |
1203 | DIR_DEBUG0(" failed.\n"); | | 1206 | DIR_DEBUG0(" failed.\n"); |
1204 | misses += 1; | | 1207 | misses += 1; |
1205 | return NULL; | | 1208 | return NULL; |
1206 | } | | 1209 | } |
1207 | | | 1210 | |
1208 | if (*cp == '\0') { | | 1211 | if (*cp == '\0') { |
1209 | /* we were given a trailing "/" */ | | 1212 | /* we were given a trailing "/" */ |
1210 | cp = trailing_dot; | | 1213 | cp = trailing_dot; |
1211 | } | | 1214 | } |
1212 | | | 1215 | |
1213 | if (name[0] != '/') { | | 1216 | if (name[0] != '/') { |
1214 | Boolean checkedDot = FALSE; | | 1217 | Boolean checkedDot = FALSE; |
1215 | | | 1218 | |
1216 | DIR_DEBUG0(" Trying subdirectories...\n"); | | 1219 | DIR_DEBUG0(" Trying subdirectories...\n"); |
1217 | | | 1220 | |
1218 | if (!hasLastDot) { | | 1221 | if (!hasLastDot) { |
1219 | if (dot) { | | 1222 | if (dot) { |
1220 | checkedDot = TRUE; | | 1223 | checkedDot = TRUE; |
1221 | if ((file = DirLookupSubdir(dot, name)) != NULL) | | 1224 | if ((file = DirLookupSubdir(dot, name)) != NULL) |
1222 | return file; | | 1225 | return file; |
1223 | } | | 1226 | } |
1224 | if (cur && (file = DirLookupSubdir(cur, name)) != NULL) | | 1227 | if (cur && (file = DirLookupSubdir(cur, name)) != NULL) |
1225 | return file; | | 1228 | return file; |
1226 | } | | 1229 | } |
1227 | | | 1230 | |
1228 | Lst_OpenS(path); | | 1231 | Lst_OpenS(path); |
1229 | while ((ln = Lst_NextS(path)) != NULL) { | | 1232 | while ((ln = Lst_NextS(path)) != NULL) { |
1230 | p = Lst_DatumS(ln); | | 1233 | p = Lst_DatumS(ln); |
1231 | if (p == dotLast) | | 1234 | if (p == dotLast) |
1232 | continue; | | 1235 | continue; |
1233 | if (p == dot) { | | 1236 | if (p == dot) { |
1234 | if (checkedDot) | | 1237 | if (checkedDot) |
1235 | continue; | | 1238 | continue; |
1236 | checkedDot = TRUE; | | 1239 | checkedDot = TRUE; |
1237 | } | | 1240 | } |
1238 | if ((file = DirLookupSubdir(p, name)) != NULL) { | | 1241 | if ((file = DirLookupSubdir(p, name)) != NULL) { |
1239 | Lst_CloseS(path); | | 1242 | Lst_CloseS(path); |
1240 | return file; | | 1243 | return file; |
1241 | } | | 1244 | } |
1242 | } | | 1245 | } |
1243 | Lst_CloseS(path); | | 1246 | Lst_CloseS(path); |
1244 | | | 1247 | |
1245 | if (hasLastDot) { | | 1248 | if (hasLastDot) { |
1246 | if (dot && !checkedDot) { | | 1249 | if (dot && !checkedDot) { |
1247 | checkedDot = TRUE; | | 1250 | checkedDot = TRUE; |
1248 | if ((file = DirLookupSubdir(dot, name)) != NULL) | | 1251 | if ((file = DirLookupSubdir(dot, name)) != NULL) |
1249 | return file; | | 1252 | return file; |
1250 | } | | 1253 | } |
1251 | if (cur && (file = DirLookupSubdir(cur, name)) != NULL) | | 1254 | if (cur && (file = DirLookupSubdir(cur, name)) != NULL) |
1252 | return file; | | 1255 | return file; |
1253 | } | | 1256 | } |
1254 | | | 1257 | |
1255 | if (checkedDot) { | | 1258 | if (checkedDot) { |
1256 | /* | | 1259 | /* |
1257 | * Already checked by the given name, since . was in the path, | | 1260 | * Already checked by the given name, since . was in the path, |
1258 | * so no point in proceeding... | | 1261 | * so no point in proceeding... |
1259 | */ | | 1262 | */ |
1260 | DIR_DEBUG0(" Checked . already, returning NULL\n"); | | 1263 | DIR_DEBUG0(" Checked . already, returning NULL\n"); |
1261 | return NULL; | | 1264 | return NULL; |
1262 | } | | 1265 | } |
1263 | | | 1266 | |
1264 | } else { /* name[0] == '/' */ | | 1267 | } else { /* name[0] == '/' */ |
1265 | | | 1268 | |
1266 | /* | | 1269 | /* |
1267 | * For absolute names, compare directory path prefix against the | | 1270 | * For absolute names, compare directory path prefix against the |
1268 | * the directory path of each member on the search path for an exact | | 1271 | * the directory path of each member on the search path for an exact |
1269 | * match. If we have an exact match on any member of the search path, | | 1272 | * match. If we have an exact match on any member of the search path, |
1270 | * use the cached contents of that member to lookup the final file | | 1273 | * use the cached contents of that member to lookup the final file |
1271 | * component. If that lookup fails we can safely assume that the | | 1274 | * component. If that lookup fails we can safely assume that the |
1272 | * file does not exist at all. This is signified by DirLookupAbs() | | 1275 | * file does not exist at all. This is signified by DirLookupAbs() |
1273 | * returning an empty string. | | 1276 | * returning an empty string. |
1274 | */ | | 1277 | */ |
1275 | DIR_DEBUG0(" Trying exact path matches...\n"); | | 1278 | DIR_DEBUG0(" Trying exact path matches...\n"); |
1276 | | | 1279 | |
1277 | if (!hasLastDot && cur && | | 1280 | if (!hasLastDot && cur && |
1278 | ((file = DirLookupAbs(cur, name, cp)) != NULL)) { | | 1281 | ((file = DirLookupAbs(cur, name, cp)) != NULL)) { |
1279 | if (file[0] == '\0') { | | 1282 | if (file[0] == '\0') { |
1280 | free(file); | | 1283 | free(file); |
1281 | return NULL; | | 1284 | return NULL; |
1282 | } | | 1285 | } |
1283 | return file; | | 1286 | return file; |
1284 | } | | 1287 | } |
1285 | | | 1288 | |
1286 | Lst_OpenS(path); | | 1289 | Lst_OpenS(path); |
1287 | while ((ln = Lst_NextS(path)) != NULL) { | | 1290 | while ((ln = Lst_NextS(path)) != NULL) { |
1288 | p = Lst_DatumS(ln); | | 1291 | p = Lst_DatumS(ln); |
1289 | if (p == dotLast) | | 1292 | if (p == dotLast) |
1290 | continue; | | 1293 | continue; |
1291 | if ((file = DirLookupAbs(p, name, cp)) != NULL) { | | 1294 | if ((file = DirLookupAbs(p, name, cp)) != NULL) { |
1292 | Lst_CloseS(path); | | 1295 | Lst_CloseS(path); |
1293 | if (file[0] == '\0') { | | 1296 | if (file[0] == '\0') { |
1294 | free(file); | | 1297 | free(file); |
1295 | return NULL; | | 1298 | return NULL; |
1296 | } | | 1299 | } |
1297 | return file; | | 1300 | return file; |
1298 | } | | 1301 | } |
1299 | } | | 1302 | } |
1300 | Lst_CloseS(path); | | 1303 | Lst_CloseS(path); |
1301 | | | 1304 | |
1302 | if (hasLastDot && cur && | | 1305 | if (hasLastDot && cur && |
1303 | ((file = DirLookupAbs(cur, name, cp)) != NULL)) { | | 1306 | ((file = DirLookupAbs(cur, name, cp)) != NULL)) { |
1304 | if (file[0] == '\0') { | | 1307 | if (file[0] == '\0') { |
1305 | free(file); | | 1308 | free(file); |
1306 | return NULL; | | 1309 | return NULL; |
1307 | } | | 1310 | } |
1308 | return file; | | 1311 | return file; |
1309 | } | | 1312 | } |
1310 | } | | 1313 | } |
1311 | | | 1314 | |
1312 | /* | | 1315 | /* |
1313 | * Didn't find it that way, either. Sigh. Phase 3. Add its directory | | 1316 | * Didn't find it that way, either. Sigh. Phase 3. Add its directory |
1314 | * onto the search path in any case, just in case, then look for the | | 1317 | * onto the search path in any case, just in case, then look for the |
1315 | * thing in the hash table. If we find it, grand. We return a new | | 1318 | * thing in the hash table. If we find it, grand. We return a new |
1316 | * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. | | 1319 | * copy of the name. Otherwise we sadly return a NULL pointer. Sigh. |
1317 | * Note that if the directory holding the file doesn't exist, this will | | 1320 | * Note that if the directory holding the file doesn't exist, this will |
1318 | * do an extra search of the final directory on the path. Unless something | | 1321 | * do an extra search of the final directory on the path. Unless something |
1319 | * weird happens, this search won't succeed and life will be groovy. | | 1322 | * weird happens, this search won't succeed and life will be groovy. |
1320 | * | | 1323 | * |
1321 | * Sigh. We cannot add the directory onto the search path because | | 1324 | * Sigh. We cannot add the directory onto the search path because |
1322 | * of this amusing case: | | 1325 | * of this amusing case: |
1323 | * $(INSTALLDIR)/$(FILE): $(FILE) | | 1326 | * $(INSTALLDIR)/$(FILE): $(FILE) |
1324 | * | | 1327 | * |
1325 | * $(FILE) exists in $(INSTALLDIR) but not in the current one. | | 1328 | * $(FILE) exists in $(INSTALLDIR) but not in the current one. |
1326 | * When searching for $(FILE), we will find it in $(INSTALLDIR) | | 1329 | * When searching for $(FILE), we will find it in $(INSTALLDIR) |
1327 | * b/c we added it here. This is not good... | | 1330 | * b/c we added it here. This is not good... |
1328 | */ | | 1331 | */ |
1329 | #ifdef notdef | | 1332 | #ifdef notdef |
1330 | if (cp == traling_dot) { | | 1333 | if (cp == traling_dot) { |
1331 | cp = strrchr(name, '/'); | | 1334 | cp = strrchr(name, '/'); |
1332 | cp += 1; | | 1335 | cp += 1; |
1333 | } | | 1336 | } |
1334 | cp[-1] = '\0'; | | 1337 | cp[-1] = '\0'; |
1335 | (void)Dir_AddDir(path, name); | | 1338 | (void)Dir_AddDir(path, name); |
1336 | cp[-1] = '/'; | | 1339 | cp[-1] = '/'; |
1337 | | | 1340 | |
1338 | bigmisses += 1; | | 1341 | bigmisses += 1; |
1339 | ln = Lst_Last(path); | | 1342 | ln = Lst_Last(path); |
1340 | if (ln == NULL) { | | 1343 | if (ln == NULL) { |
1341 | return NULL; | | 1344 | return NULL; |
1342 | } else { | | 1345 | } else { |
1343 | p = Lst_DatumS(ln); | | 1346 | p = Lst_DatumS(ln); |
1344 | } | | 1347 | } |
1345 | | | 1348 | |
1346 | if (Hash_FindEntry(&p->files, cp) != NULL) { | | 1349 | if (Hash_FindEntry(&p->files, cp) != NULL) { |
1347 | return bmake_strdup(name); | | 1350 | return bmake_strdup(name); |
1348 | } else { | | 1351 | } else { |
1349 | return NULL; | | 1352 | return NULL; |
1350 | } | | 1353 | } |
1351 | #else /* !notdef */ | | 1354 | #else /* !notdef */ |
1352 | DIR_DEBUG1(" Looking for \"%s\" ...\n", name); | | 1355 | DIR_DEBUG1(" Looking for \"%s\" ...\n", name); |
1353 | | | 1356 | |
1354 | bigmisses += 1; | | 1357 | bigmisses += 1; |
1355 | if (cached_stat(name, &stb) == 0) { | | 1358 | if (cached_stat(name, &stb) == 0) { |
1356 | return bmake_strdup(name); | | 1359 | return bmake_strdup(name); |
1357 | } | | 1360 | } |
1358 | | | 1361 | |
1359 | DIR_DEBUG0(" failed. Returning NULL\n"); | | 1362 | DIR_DEBUG0(" failed. Returning NULL\n"); |
1360 | return NULL; | | 1363 | return NULL; |
1361 | #endif /* notdef */ | | 1364 | #endif /* notdef */ |
1362 | } | | 1365 | } |
1363 | | | 1366 | |
1364 | | | 1367 | |
1365 | /*- | | 1368 | /*- |
1366 | *----------------------------------------------------------------------- | | 1369 | *----------------------------------------------------------------------- |
1367 | * Dir_FindHereOrAbove -- | | 1370 | * Dir_FindHereOrAbove -- |
1368 | * search for a path starting at a given directory and then working | | 1371 | * search for a path starting at a given directory and then working |
1369 | * our way up towards the root. | | 1372 | * our way up towards the root. |
1370 | * | | 1373 | * |
1371 | * Input: | | 1374 | * Input: |
1372 | * here starting directory | | 1375 | * here starting directory |
1373 | * search_path the path we are looking for | | 1376 | * search_path the path we are looking for |
1374 | * result the result of a successful search is placed here | | 1377 | * result the result of a successful search is placed here |
1375 | * rlen the length of the result buffer | | 1378 | * rlen the length of the result buffer |
1376 | * (typically MAXPATHLEN + 1) | | 1379 | * (typically MAXPATHLEN + 1) |
1377 | * | | 1380 | * |
1378 | * Results: | | 1381 | * Results: |
1379 | * 0 on failure, 1 on success [in which case the found path is put | | 1382 | * 0 on failure, 1 on success [in which case the found path is put |
1380 | * in the result buffer]. | | 1383 | * in the result buffer]. |
1381 | * | | 1384 | * |
1382 | * Side Effects: | | 1385 | * Side Effects: |
1383 | *----------------------------------------------------------------------- | | 1386 | *----------------------------------------------------------------------- |
1384 | */ | | 1387 | */ |
1385 | int | | 1388 | int |
1386 | Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) | | 1389 | Dir_FindHereOrAbove(char *here, char *search_path, char *result, int rlen) |
1387 | { | | 1390 | { |
1388 | struct stat st; | | 1391 | struct stat st; |
1389 | char dirbase[MAXPATHLEN + 1], *db_end; | | 1392 | char dirbase[MAXPATHLEN + 1], *db_end; |
1390 | char try[MAXPATHLEN + 1], *try_end; | | 1393 | char try[MAXPATHLEN + 1], *try_end; |
1391 | | | 1394 | |
1392 | /* copy out our starting point */ | | 1395 | /* copy out our starting point */ |
1393 | snprintf(dirbase, sizeof(dirbase), "%s", here); | | 1396 | snprintf(dirbase, sizeof(dirbase), "%s", here); |
1394 | db_end = dirbase + strlen(dirbase); | | 1397 | db_end = dirbase + strlen(dirbase); |
1395 | | | 1398 | |
1396 | /* loop until we determine a result */ | | 1399 | /* loop until we determine a result */ |
1397 | while (1) { | | 1400 | while (1) { |
1398 | | | 1401 | |
1399 | /* try and stat(2) it ... */ | | 1402 | /* try and stat(2) it ... */ |
1400 | snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); | | 1403 | snprintf(try, sizeof(try), "%s/%s", dirbase, search_path); |
1401 | if (cached_stat(try, &st) != -1) { | | 1404 | if (cached_stat(try, &st) != -1) { |
1402 | /* | | 1405 | /* |
1403 | * success! if we found a file, chop off | | 1406 | * success! if we found a file, chop off |
1404 | * the filename so we return a directory. | | 1407 | * the filename so we return a directory. |
1405 | */ | | 1408 | */ |
1406 | if ((st.st_mode & S_IFMT) != S_IFDIR) { | | 1409 | if ((st.st_mode & S_IFMT) != S_IFDIR) { |
1407 | try_end = try + strlen(try); | | 1410 | try_end = try + strlen(try); |
1408 | while (try_end > try && *try_end != '/') | | 1411 | while (try_end > try && *try_end != '/') |
1409 | try_end--; | | 1412 | try_end--; |
1410 | if (try_end > try) | | 1413 | if (try_end > try) |
1411 | *try_end = 0; /* chop! */ | | 1414 | *try_end = 0; /* chop! */ |
1412 | } | | 1415 | } |
1413 | | | 1416 | |
1414 | /* | | 1417 | /* |
1415 | * done! | | 1418 | * done! |
1416 | */ | | 1419 | */ |
1417 | snprintf(result, rlen, "%s", try); | | 1420 | snprintf(result, rlen, "%s", try); |
1418 | return 1; | | 1421 | return 1; |
1419 | } | | 1422 | } |
1420 | | | 1423 | |
1421 | /* | | 1424 | /* |
1422 | * nope, we didn't find it. if we used up dirbase we've | | 1425 | * nope, we didn't find it. if we used up dirbase we've |
1423 | * reached the root and failed. | | 1426 | * reached the root and failed. |
1424 | */ | | 1427 | */ |
1425 | if (db_end == dirbase) | | 1428 | if (db_end == dirbase) |
1426 | break; /* failed! */ | | 1429 | break; /* failed! */ |
1427 | | | 1430 | |
1428 | /* | | 1431 | /* |
1429 | * truncate dirbase from the end to move up a dir | | 1432 | * truncate dirbase from the end to move up a dir |
1430 | */ | | 1433 | */ |
1431 | while (db_end > dirbase && *db_end != '/') | | 1434 | while (db_end > dirbase && *db_end != '/') |
1432 | db_end--; | | 1435 | db_end--; |
1433 | *db_end = 0; /* chop! */ | | 1436 | *db_end = 0; /* chop! */ |
1434 | | | 1437 | |
1435 | } /* while (1) */ | | 1438 | } /* while (1) */ |
1436 | | | 1439 | |
1437 | /* | | 1440 | /* |
1438 | * we failed... | | 1441 | * we failed... |
1439 | */ | | 1442 | */ |
1440 | return 0; | | 1443 | return 0; |
1441 | } | | 1444 | } |
1442 | | | 1445 | |
1443 | /*- | | 1446 | /*- |
1444 | *----------------------------------------------------------------------- | | 1447 | *----------------------------------------------------------------------- |
1445 | * Dir_MTime -- | | 1448 | * Dir_MTime -- |
1446 | * Find the modification time of the file described by gn along the | | 1449 | * Find the modification time of the file described by gn along the |
1447 | * search path dirSearchPath. | | 1450 | * search path dirSearchPath. |
1448 | * | | 1451 | * |
1449 | * Input: | | 1452 | * Input: |
1450 | * gn the file whose modification time is desired | | 1453 | * gn the file whose modification time is desired |
1451 | * | | 1454 | * |
1452 | * Results: | | 1455 | * Results: |
1453 | * The modification time or 0 if it doesn't exist | | 1456 | * The modification time or 0 if it doesn't exist |
1454 | * | | 1457 | * |
1455 | * Side Effects: | | 1458 | * Side Effects: |
1456 | * The modification time is placed in the node's mtime slot. | | 1459 | * The modification time is placed in the node's mtime slot. |
1457 | * If the node didn't have a path entry before, and Dir_FindFile | | 1460 | * If the node didn't have a path entry before, and Dir_FindFile |
1458 | * found one for it, the full name is placed in the path slot. | | 1461 | * found one for it, the full name is placed in the path slot. |
1459 | *----------------------------------------------------------------------- | | 1462 | *----------------------------------------------------------------------- |
1460 | */ | | 1463 | */ |
1461 | int | | 1464 | int |
1462 | Dir_MTime(GNode *gn, Boolean recheck) | | 1465 | Dir_MTime(GNode *gn, Boolean recheck) |
1463 | { | | 1466 | { |
1464 | char *fullName; /* the full pathname of name */ | | 1467 | char *fullName; /* the full pathname of name */ |
1465 | struct stat stb; /* buffer for finding the mod time */ | | 1468 | struct stat stb; /* buffer for finding the mod time */ |
1466 | | | 1469 | |
1467 | if (gn->type & OP_ARCHV) { | | 1470 | if (gn->type & OP_ARCHV) { |
1468 | return Arch_MTime(gn); | | 1471 | return Arch_MTime(gn); |
1469 | } else if (gn->type & OP_PHONY) { | | 1472 | } else if (gn->type & OP_PHONY) { |
1470 | gn->mtime = 0; | | 1473 | gn->mtime = 0; |
1471 | return 0; | | 1474 | return 0; |
1472 | } else if (gn->path == NULL) { | | 1475 | } else if (gn->path == NULL) { |
1473 | if (gn->type & OP_NOPATH) | | 1476 | if (gn->type & OP_NOPATH) |
1474 | fullName = NULL; | | 1477 | fullName = NULL; |
1475 | else { | | 1478 | else { |
1476 | fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); | | 1479 | fullName = Dir_FindFile(gn->name, Suff_FindPath(gn)); |
1477 | if (fullName == NULL && gn->flags & FROM_DEPEND && | | 1480 | if (fullName == NULL && gn->flags & FROM_DEPEND && |
1478 | !Lst_IsEmpty(gn->iParents)) { | | 1481 | !Lst_IsEmpty(gn->iParents)) { |
1479 | char *cp; | | 1482 | char *cp; |
1480 | | | 1483 | |
1481 | cp = strrchr(gn->name, '/'); | | 1484 | cp = strrchr(gn->name, '/'); |
1482 | if (cp) { | | 1485 | if (cp) { |
1483 | /* | | 1486 | /* |
1484 | * This is an implied source, and it may have moved, | | 1487 | * This is an implied source, and it may have moved, |
1485 | * see if we can find it via the current .PATH | | 1488 | * see if we can find it via the current .PATH |
1486 | */ | | 1489 | */ |
1487 | cp++; | | 1490 | cp++; |
1488 | | | 1491 | |
1489 | fullName = Dir_FindFile(cp, Suff_FindPath(gn)); | | 1492 | fullName = Dir_FindFile(cp, Suff_FindPath(gn)); |
1490 | if (fullName) { | | 1493 | if (fullName) { |
1491 | /* | | 1494 | /* |
1492 | * Put the found file in gn->path | | 1495 | * Put the found file in gn->path |
1493 | * so that we give that to the compiler. | | 1496 | * so that we give that to the compiler. |
1494 | */ | | 1497 | */ |
1495 | gn->path = bmake_strdup(fullName); | | 1498 | gn->path = bmake_strdup(fullName); |
1496 | if (!Job_RunTarget(".STALE", gn->fname)) | | 1499 | if (!Job_RunTarget(".STALE", gn->fname)) |
1497 | fprintf(stdout, | | 1500 | fprintf(stdout, |
1498 | "%s: %s, %d: ignoring stale %s for %s, " | | 1501 | "%s: %s, %d: ignoring stale %s for %s, " |
1499 | "found %s\n", progname, gn->fname, | | 1502 | "found %s\n", progname, gn->fname, |
1500 | gn->lineno, | | 1503 | gn->lineno, |
1501 | makeDependfile, gn->name, fullName); | | 1504 | makeDependfile, gn->name, fullName); |
1502 | } | | 1505 | } |
1503 | } | | 1506 | } |
1504 | } | | 1507 | } |
1505 | DIR_DEBUG2("Found '%s' as '%s'\n", | | 1508 | DIR_DEBUG2("Found '%s' as '%s'\n", |
1506 | gn->name, fullName ? fullName : "(not found)"); | | 1509 | gn->name, fullName ? fullName : "(not found)"); |
1507 | } | | 1510 | } |
1508 | } else { | | 1511 | } else { |
1509 | fullName = gn->path; | | 1512 | fullName = gn->path; |
1510 | } | | 1513 | } |
1511 | | | 1514 | |
1512 | if (fullName == NULL) { | | 1515 | if (fullName == NULL) { |
1513 | fullName = bmake_strdup(gn->name); | | 1516 | fullName = bmake_strdup(gn->name); |
1514 | } | | 1517 | } |
1515 | | | 1518 | |
1516 | if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) { | | 1519 | if (cached_stats(&mtimes, fullName, &stb, recheck ? CST_UPDATE : 0) < 0) { |
1517 | if (gn->type & OP_MEMBER) { | | 1520 | if (gn->type & OP_MEMBER) { |
1518 | if (fullName != gn->path) | | 1521 | if (fullName != gn->path) |
1519 | free(fullName); | | 1522 | free(fullName); |
1520 | return Arch_MemMTime(gn); | | 1523 | return Arch_MemMTime(gn); |
1521 | } else { | | 1524 | } else { |
1522 | stb.st_mtime = 0; | | 1525 | stb.st_mtime = 0; |
1523 | } | | 1526 | } |
1524 | } | | 1527 | } |
1525 | | | 1528 | |
1526 | if (fullName && gn->path == NULL) { | | 1529 | if (fullName && gn->path == NULL) { |
1527 | gn->path = fullName; | | 1530 | gn->path = fullName; |
1528 | } | | 1531 | } |
1529 | | | 1532 | |
1530 | gn->mtime = stb.st_mtime; | | 1533 | gn->mtime = stb.st_mtime; |
1531 | return gn->mtime; | | 1534 | return gn->mtime; |
1532 | } | | 1535 | } |
1533 | | | 1536 | |
1534 | /*- | | 1537 | /*- |
1535 | *----------------------------------------------------------------------- | | 1538 | *----------------------------------------------------------------------- |
1536 | * Dir_AddDir -- | | 1539 | * Dir_AddDir -- |
1537 | * Add the given name to the end of the given path. The order of | | 1540 | * Add the given name to the end of the given path. The order of |
1538 | * the arguments is backwards so ParseDoDependency can do a | | 1541 | * the arguments is backwards so ParseDoDependency can do a |
1539 | * Lst_ForEach of its list of paths... | | 1542 | * Lst_ForEach of its list of paths... |
1540 | * | | 1543 | * |
1541 | * Input: | | 1544 | * Input: |
1542 | * path the path to which the directory should be | | 1545 | * path the path to which the directory should be |
1543 | * added | | 1546 | * added |
1544 | * name the name of the directory to add | | 1547 | * name the name of the directory to add |
1545 | * | | 1548 | * |
1546 | * Results: | | 1549 | * Results: |
1547 | * none | | 1550 | * none |
1548 | * | | 1551 | * |
1549 | * Side Effects: | | 1552 | * Side Effects: |
1550 | * A structure is added to the list and the directory is | | 1553 | * A structure is added to the list and the directory is |
1551 | * read and hashed. | | 1554 | * read and hashed. |
1552 | *----------------------------------------------------------------------- | | 1555 | *----------------------------------------------------------------------- |
1553 | */ | | 1556 | */ |
1554 | Path * | | 1557 | Path * |
1555 | Dir_AddDir(Lst path, const char *name) | | 1558 | Dir_AddDir(Lst path, const char *name) |
1556 | { | | 1559 | { |
1557 | LstNode ln = NULL; /* node in case Path structure is found */ | | 1560 | LstNode ln = NULL; /* node in case Path structure is found */ |
1558 | Path *p = NULL; /* pointer to new Path structure */ | | 1561 | Path *p = NULL; /* pointer to new Path structure */ |
1559 | DIR *d; /* for reading directory */ | | 1562 | DIR *d; /* for reading directory */ |
1560 | struct dirent *dp; /* entry in directory */ | | 1563 | struct dirent *dp; /* entry in directory */ |
1561 | | | 1564 | |
1562 | if (strcmp(name, ".DOTLAST") == 0) { | | 1565 | if (strcmp(name, ".DOTLAST") == 0) { |
1563 | ln = Lst_Find(path, DirFindName, name); | | 1566 | ln = Lst_Find(path, DirFindName, name); |
1564 | if (ln != NULL) | | 1567 | if (ln != NULL) |
1565 | return Lst_DatumS(ln); | | 1568 | return Lst_DatumS(ln); |
1566 | else { | | 1569 | else { |
1567 | /* XXX: It is wrong to increment the refCount if dotLast is not | | 1570 | /* XXX: It is wrong to increment the refCount if dotLast is not |
1568 | * used afterwards. */ | | 1571 | * used afterwards. */ |
1569 | dotLast->refCount += 1; | | 1572 | dotLast->refCount += 1; |
1570 | if (path != NULL) | | 1573 | if (path != NULL) |
1571 | Lst_PrependS(path, dotLast); | | 1574 | Lst_PrependS(path, dotLast); |
1572 | } | | 1575 | } |
1573 | } | | 1576 | } |
1574 | | | 1577 | |
1575 | if (path) | | 1578 | if (path) |
1576 | ln = Lst_Find(openDirectories, DirFindName, name); | | 1579 | ln = Lst_Find(openDirectories, DirFindName, name); |
1577 | if (ln != NULL) { | | 1580 | if (ln != NULL) { |
1578 | p = Lst_DatumS(ln); | | 1581 | p = Lst_DatumS(ln); |
1579 | if (path && Lst_MemberS(path, p) == NULL) { | | 1582 | if (path && Lst_MemberS(path, p) == NULL) { |
1580 | p->refCount += 1; | | 1583 | p->refCount += 1; |
1581 | Lst_AppendS(path, p); | | 1584 | Lst_AppendS(path, p); |
1582 | } | | 1585 | } |
1583 | } else { | | 1586 | } else { |
1584 | DIR_DEBUG1("Caching %s ...", name); | | 1587 | DIR_DEBUG1("Caching %s ...", name); |
1585 | | | 1588 | |
1586 | if ((d = opendir(name)) != NULL) { | | 1589 | if ((d = opendir(name)) != NULL) { |
1587 | p = bmake_malloc(sizeof(Path)); | | 1590 | p = bmake_malloc(sizeof(Path)); |
1588 | p->name = bmake_strdup(name); | | 1591 | p->name = bmake_strdup(name); |
1589 | p->hits = 0; | | 1592 | p->hits = 0; |
1590 | p->refCount = 1; | | 1593 | p->refCount = 1; |
1591 | Hash_InitTable(&p->files, -1); | | 1594 | Hash_InitTable(&p->files, -1); |
1592 | | | 1595 | |
1593 | while ((dp = readdir(d)) != NULL) { | | 1596 | while ((dp = readdir(d)) != NULL) { |
1594 | #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ | | 1597 | #if defined(sun) && defined(d_ino) /* d_ino is a sunos4 #define for d_fileno */ |
1595 | /* | | 1598 | /* |
1596 | * The sun directory library doesn't check for a 0 inode | | 1599 | * The sun directory library doesn't check for a 0 inode |
1597 | * (0-inode slots just take up space), so we have to do | | 1600 | * (0-inode slots just take up space), so we have to do |
1598 | * it ourselves. | | 1601 | * it ourselves. |
1599 | */ | | 1602 | */ |
1600 | if (dp->d_fileno == 0) { | | 1603 | if (dp->d_fileno == 0) { |
1601 | continue; | | 1604 | continue; |
1602 | } | | 1605 | } |
1603 | #endif /* sun && d_ino */ | | 1606 | #endif /* sun && d_ino */ |
1604 | (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); | | 1607 | (void)Hash_CreateEntry(&p->files, dp->d_name, NULL); |
1605 | } | | 1608 | } |
1606 | (void)closedir(d); | | 1609 | (void)closedir(d); |
1607 | Lst_AppendS(openDirectories, p); | | 1610 | Lst_AppendS(openDirectories, p); |
1608 | if (path != NULL) | | 1611 | if (path != NULL) |
1609 | Lst_AppendS(path, p); | | 1612 | Lst_AppendS(path, p); |
1610 | } | | 1613 | } |
1611 | DIR_DEBUG0("done\n"); | | 1614 | DIR_DEBUG0("done\n"); |
1612 | } | | 1615 | } |
1613 | return p; | | 1616 | return p; |
1614 | } | | 1617 | } |
1615 | | | 1618 | |
1616 | /*- | | 1619 | /*- |
1617 | *----------------------------------------------------------------------- | | 1620 | *----------------------------------------------------------------------- |
1618 | * Dir_CopyDir -- | | 1621 | * Dir_CopyDir -- |
1619 | * Callback function for duplicating a search path via Lst_CopyS. | | 1622 | * Callback function for duplicating a search path via Lst_CopyS. |
1620 | * Ups the reference count for the directory. | | 1623 | * Ups the reference count for the directory. |
1621 | * | | 1624 | * |
1622 | * Results: | | 1625 | * Results: |
1623 | * Returns the Path it was given. | | 1626 | * Returns the Path it was given. |
1624 | *----------------------------------------------------------------------- | | 1627 | *----------------------------------------------------------------------- |
1625 | */ | | 1628 | */ |
1626 | void * | | 1629 | void * |
1627 | Dir_CopyDir(void *p) | | 1630 | Dir_CopyDir(void *p) |
1628 | { | | 1631 | { |
1629 | ((Path *)p)->refCount += 1; | | 1632 | ((Path *)p)->refCount += 1; |
1630 | | | 1633 | |
1631 | return p; | | 1634 | return p; |
1632 | } | | 1635 | } |
1633 | | | 1636 | |
1634 | /*- | | 1637 | /*- |
1635 | *----------------------------------------------------------------------- | | 1638 | *----------------------------------------------------------------------- |
1636 | * Dir_MakeFlags -- | | 1639 | * Dir_MakeFlags -- |
1637 | * Make a string by taking all the directories in the given search | | 1640 | * Make a string by taking all the directories in the given search |
1638 | * path and preceding them by the given flag. Used by the suffix | | 1641 | * path and preceding them by the given flag. Used by the suffix |
1639 | * module to create variables for compilers based on suffix search | | 1642 | * module to create variables for compilers based on suffix search |
1640 | * paths. | | 1643 | * paths. |
1641 | * | | 1644 | * |
1642 | * Input: | | 1645 | * Input: |
1643 | * flag flag which should precede each directory | | 1646 | * flag flag which should precede each directory |
1644 | * path list of directories | | 1647 | * path list of directories |
1645 | * | | 1648 | * |
1646 | * Results: | | 1649 | * Results: |
1647 | * The string mentioned above. Note that there is no space between | | 1650 | * The string mentioned above. Note that there is no space between |
1648 | * the given flag and each directory. The empty string is returned if | | 1651 | * the given flag and each directory. The empty string is returned if |
1649 | * Things don't go well. | | 1652 | * Things don't go well. |
1650 | * | | 1653 | * |
1651 | * Side Effects: | | 1654 | * Side Effects: |
1652 | * None | | 1655 | * None |
1653 | *----------------------------------------------------------------------- | | 1656 | *----------------------------------------------------------------------- |
1654 | */ | | 1657 | */ |
1655 | char * | | 1658 | char * |
1656 | Dir_MakeFlags(const char *flag, Lst path) | | 1659 | Dir_MakeFlags(const char *flag, Lst path) |
1657 | { | | 1660 | { |
1658 | Buffer buf; | | 1661 | Buffer buf; |
1659 | LstNode ln; /* the node of the current directory */ | | 1662 | LstNode ln; /* the node of the current directory */ |
1660 | | | 1663 | |
1661 | Buf_Init(&buf, 0); | | 1664 | Buf_Init(&buf, 0); |
1662 | | | 1665 | |
1663 | if (Lst_Open(path) == SUCCESS) { | | 1666 | if (path != NULL) { |
| | | 1667 | Lst_OpenS(path); |
1664 | while ((ln = Lst_NextS(path)) != NULL) { | | 1668 | while ((ln = Lst_NextS(path)) != NULL) { |
1665 | Path *p = Lst_DatumS(ln); | | 1669 | Path *p = Lst_DatumS(ln); |
1666 | Buf_AddStr(&buf, " "); | | 1670 | Buf_AddStr(&buf, " "); |
1667 | Buf_AddStr(&buf, flag); | | 1671 | Buf_AddStr(&buf, flag); |
1668 | Buf_AddStr(&buf, p->name); | | 1672 | Buf_AddStr(&buf, p->name); |
1669 | } | | 1673 | } |
1670 | Lst_CloseS(path); | | 1674 | Lst_CloseS(path); |
1671 | } | | 1675 | } |
1672 | | | 1676 | |
1673 | return Buf_Destroy(&buf, FALSE); | | 1677 | return Buf_Destroy(&buf, FALSE); |
1674 | } | | 1678 | } |
1675 | | | 1679 | |
1676 | /*- | | 1680 | /*- |
1677 | *----------------------------------------------------------------------- | | 1681 | *----------------------------------------------------------------------- |
1678 | * Dir_Destroy -- | | 1682 | * Dir_Destroy -- |
1679 | * Nuke a directory descriptor, if possible. Callback procedure | | 1683 | * Nuke a directory descriptor, if possible. Callback procedure |
1680 | * for the suffixes module when destroying a search path. | | 1684 | * for the suffixes module when destroying a search path. |
1681 | * | | 1685 | * |
1682 | * Input: | | 1686 | * Input: |
1683 | * pp The directory descriptor to nuke | | 1687 | * pp The directory descriptor to nuke |
1684 | * | | 1688 | * |
1685 | * Results: | | 1689 | * Results: |
1686 | * None. | | 1690 | * None. |
1687 | * | | 1691 | * |
1688 | * Side Effects: | | 1692 | * Side Effects: |
1689 | * If no other path references this directory (refCount == 0), | | 1693 | * If no other path references this directory (refCount == 0), |
1690 | * the Path and all its data are freed. | | 1694 | * the Path and all its data are freed. |
1691 | * | | 1695 | * |
1692 | *----------------------------------------------------------------------- | | 1696 | *----------------------------------------------------------------------- |
1693 | */ | | 1697 | */ |
1694 | void | | 1698 | void |
1695 | Dir_Destroy(void *pp) | | 1699 | Dir_Destroy(void *pp) |
1696 | { | | 1700 | { |
1697 | Path *p = (Path *)pp; | | 1701 | Path *p = (Path *)pp; |
1698 | p->refCount -= 1; | | 1702 | p->refCount -= 1; |
1699 | | | 1703 | |
1700 | if (p->refCount == 0) { | | 1704 | if (p->refCount == 0) { |
1701 | LstNode ln; | | 1705 | LstNode ln; |
1702 | | | 1706 | |
1703 | ln = Lst_MemberS(openDirectories, p); | | 1707 | ln = Lst_MemberS(openDirectories, p); |
1704 | Lst_RemoveS(openDirectories, ln); | | 1708 | Lst_RemoveS(openDirectories, ln); |
1705 | | | 1709 | |
1706 | Hash_DeleteTable(&p->files); | | 1710 | Hash_DeleteTable(&p->files); |
1707 | free(p->name); | | 1711 | free(p->name); |
1708 | free(p); | | 1712 | free(p); |
1709 | } | | 1713 | } |
1710 | } | | 1714 | } |
1711 | | | 1715 | |
1712 | /*- | | 1716 | /*- |
1713 | *----------------------------------------------------------------------- | | 1717 | *----------------------------------------------------------------------- |
1714 | * Dir_ClearPath -- | | 1718 | * Dir_ClearPath -- |
1715 | * Clear out all elements of the given search path. This is different | | 1719 | * Clear out all elements of the given search path. This is different |
1716 | * from destroying the list, notice. | | 1720 | * from destroying the list, notice. |
1717 | * | | 1721 | * |
1718 | * Input: | | 1722 | * Input: |
1719 | * path Path to clear | | 1723 | * path Path to clear |
1720 | * | | 1724 | * |
1721 | * Results: | | 1725 | * Results: |
1722 | * None. | | 1726 | * None. |
1723 | * | | 1727 | * |
1724 | * Side Effects: | | 1728 | * Side Effects: |
1725 | * The path is set to the empty list. | | 1729 | * The path is set to the empty list. |
1726 | * | | 1730 | * |
1727 | *----------------------------------------------------------------------- | | 1731 | *----------------------------------------------------------------------- |
1728 | */ | | 1732 | */ |
1729 | void | | 1733 | void |
1730 | Dir_ClearPath(Lst path) | | 1734 | Dir_ClearPath(Lst path) |
1731 | { | | 1735 | { |
1732 | while (!Lst_IsEmpty(path)) { | | 1736 | while (!Lst_IsEmpty(path)) { |
1733 | Path *p = Lst_DequeueS(path); | | 1737 | Path *p = Lst_DequeueS(path); |
1734 | Dir_Destroy(p); | | 1738 | Dir_Destroy(p); |
1735 | } | | 1739 | } |
1736 | } | | 1740 | } |
1737 | | | 1741 | |
1738 | | | 1742 | |
1739 | /*- | | 1743 | /*- |
1740 | *----------------------------------------------------------------------- | | 1744 | *----------------------------------------------------------------------- |
1741 | * Dir_Concat -- | | 1745 | * Dir_Concat -- |
1742 | * Concatenate two paths, adding the second to the end of the first. | | 1746 | * Concatenate two paths, adding the second to the end of the first. |
1743 | * Makes sure to avoid duplicates. | | 1747 | * Makes sure to avoid duplicates. |
1744 | * | | 1748 | * |
1745 | * Input: | | 1749 | * Input: |
1746 | * path1 Dest | | 1750 | * path1 Dest |
1747 | * path2 Source | | 1751 | * path2 Source |
1748 | * | | 1752 | * |
1749 | * Results: | | 1753 | * Results: |
1750 | * None | | 1754 | * None |
1751 | * | | 1755 | * |
1752 | * Side Effects: | | 1756 | * Side Effects: |
1753 | * Reference counts for added dirs are upped. | | 1757 | * Reference counts for added dirs are upped. |
1754 | * | | 1758 | * |
1755 | *----------------------------------------------------------------------- | | 1759 | *----------------------------------------------------------------------- |
1756 | */ | | 1760 | */ |
1757 | void | | 1761 | void |
1758 | Dir_Concat(Lst path1, Lst path2) | | 1762 | Dir_Concat(Lst path1, Lst path2) |
1759 | { | | 1763 | { |
1760 | LstNode ln; | | 1764 | LstNode ln; |
1761 | Path *p; | | 1765 | Path *p; |
1762 | | | 1766 | |
1763 | for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { | | 1767 | for (ln = Lst_First(path2); ln != NULL; ln = Lst_Succ(ln)) { |
1764 | p = Lst_DatumS(ln); | | 1768 | p = Lst_DatumS(ln); |
1765 | if (Lst_MemberS(path1, p) == NULL) { | | 1769 | if (Lst_MemberS(path1, p) == NULL) { |
1766 | p->refCount += 1; | | 1770 | p->refCount += 1; |
1767 | Lst_AppendS(path1, p); | | 1771 | Lst_AppendS(path1, p); |
1768 | } | | 1772 | } |
1769 | } | | 1773 | } |
1770 | } | | 1774 | } |
1771 | | | 1775 | |
1772 | static int | | 1776 | static int |
1773 | percentage(int num, int den) | | 1777 | percentage(int num, int den) |
1774 | { | | 1778 | { |
1775 | return den != 0 ? num * 100 / den : 0; | | 1779 | return den != 0 ? num * 100 / den : 0; |
1776 | } | | 1780 | } |
1777 | | | 1781 | |
1778 | /********** DEBUG INFO **********/ | | 1782 | /********** DEBUG INFO **********/ |
1779 | void | | 1783 | void |
1780 | Dir_PrintDirectories(void) | | 1784 | Dir_PrintDirectories(void) |
1781 | { | | 1785 | { |
1782 | LstNode ln; | | 1786 | LstNode ln; |
1783 | Path *p; | | | |
1784 | | | 1787 | |
1785 | fprintf(debug_file, "#*** Directory Cache:\n"); | | 1788 | fprintf(debug_file, "#*** Directory Cache:\n"); |
1786 | fprintf(debug_file, | | 1789 | fprintf(debug_file, |
1787 | "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", | | 1790 | "# Stats: %d hits %d misses %d near misses %d losers (%d%%)\n", |
1788 | hits, misses, nearmisses, bigmisses, | | 1791 | hits, misses, nearmisses, bigmisses, |
1789 | percentage(hits, hits + bigmisses + nearmisses)); | | 1792 | percentage(hits, hits + bigmisses + nearmisses)); |
1790 | fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); | | 1793 | fprintf(debug_file, "# %-20s referenced\thits\n", "directory"); |
1791 | if (Lst_Open(openDirectories) == SUCCESS) { | | 1794 | |
1792 | while ((ln = Lst_NextS(openDirectories)) != NULL) { | | 1795 | Lst_OpenS(openDirectories); |
1793 | p = Lst_DatumS(ln); | | 1796 | while ((ln = Lst_NextS(openDirectories)) != NULL) { |
1794 | fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, | | 1797 | Path *p = Lst_DatumS(ln); |
1795 | p->hits); | | 1798 | fprintf(debug_file, "# %-20s %10d\t%4d\n", p->name, p->refCount, |
1796 | } | | 1799 | p->hits); |
1797 | Lst_CloseS(openDirectories); | | | |
1798 | } | | 1800 | } |
| | | 1801 | Lst_CloseS(openDirectories); |
1799 | } | | 1802 | } |
1800 | | | 1803 | |
1801 | static int | | 1804 | static int |
1802 | DirPrintDir(void *p, void *dummy MAKE_ATTR_UNUSED) | | 1805 | DirPrintDir(void *p, void *dummy MAKE_ATTR_UNUSED) |
1803 | { | | 1806 | { |
1804 | fprintf(debug_file, "%s ", ((Path *)p)->name); | | 1807 | fprintf(debug_file, "%s ", ((Path *)p)->name); |
1805 | return 0; | | 1808 | return 0; |
1806 | } | | 1809 | } |
1807 | | | 1810 | |
1808 | void | | 1811 | void |
1809 | Dir_PrintPath(Lst path) | | 1812 | Dir_PrintPath(Lst path) |
1810 | { | | 1813 | { |
1811 | Lst_ForEach(path, DirPrintDir, NULL); | | 1814 | Lst_ForEach(path, DirPrintDir, NULL); |
1812 | } | | 1815 | } |