1268
|
1 /* pathsearch.c: look up a filename in a path. |
|
2 |
|
3 Copyright (C) 1993, 94 Karl Berry. |
|
4 |
|
5 This program is free software; you can redistribute it and/or modify |
|
6 it under the terms of the GNU General Public License as published by |
|
7 the Free Software Foundation; either version 2, or (at your option) |
|
8 any later version. |
|
9 |
|
10 This program is distributed in the hope that it will be useful, |
|
11 but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
12 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|
13 GNU General Public License for more details. |
|
14 |
|
15 You should have received a copy of the GNU General Public License |
|
16 along with this program; if not, write to the Free Software |
|
17 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ |
|
18 |
|
19 #include <kpathsea/config.h> |
|
20 |
|
21 #include <kpathsea/c-fopen.h> |
|
22 #include <kpathsea/absolute.h> |
|
23 #include <kpathsea/expand.h> |
|
24 #include <kpathsea/db.h> |
|
25 #include <kpathsea/pathsearch.h> |
|
26 #include <kpathsea/readable.h> |
|
27 #include <kpathsea/str-list.h> |
|
28 #include <kpathsea/str-llist.h> |
|
29 #include <kpathsea/variable.h> |
|
30 |
|
31 #include <time.h> /* for `time' */ |
|
32 |
|
33 /* This function is called after every search to record the filename(s) |
|
34 found in $TEXMFLOG or if debugging. */ |
|
35 |
|
36 static void |
|
37 log_search P1C(str_list_type, filenames) |
|
38 { |
|
39 static FILE *log_file = NULL; |
|
40 static boolean first_time = true; |
|
41 |
|
42 if (first_time) |
|
43 { |
|
44 string log_name = kpse_var_expand ("$TEXMFLOG"); |
|
45 first_time = false; |
|
46 /* Get name from either envvar or config file. */ |
|
47 if (log_name && *log_name) |
|
48 { |
|
49 log_file = fopen (log_name, FOPEN_A_MODE); |
|
50 if (!log_file) |
|
51 perror (log_name); |
|
52 free (log_name); |
|
53 } |
|
54 } |
|
55 |
|
56 if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH) || log_file) |
|
57 { |
|
58 unsigned e; |
|
59 |
|
60 /* FILENAMES should never be null, but safety doesn't hurt. */ |
|
61 for (e = 0; |
|
62 e < STR_LIST_LENGTH (filenames) && STR_LIST_ELT (filenames, e); |
|
63 e++) |
|
64 { |
|
65 string filename = STR_LIST_ELT (filenames, e); |
|
66 |
|
67 /* Only record absolute filenames, for privacy. */ |
|
68 if (log_file && kpse_absolute_p (filename, false)) |
|
69 fprintf (log_file, "%u %s\n", time (NULL), filename); |
|
70 |
|
71 /* And show them online, if debugging. We've already started |
|
72 the debugging line in `search', where this is called, so |
|
73 just print the filename here, don't use DEBUGF. */ |
|
74 if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) |
|
75 fputs (filename, stderr); |
|
76 } |
|
77 } |
|
78 } |
|
79 |
|
80 /* Concatenate each element in DIRS with NAME (assume each ends with a |
|
81 /, to save time). If SEARCH_ALL is false, return the first readable |
|
82 regular file. Else continue to search for more. In any case, if |
|
83 none, return a list containing just NULL. |
|
84 |
|
85 We keep a single buffer for the potential filenames and reallocate |
|
86 only when necessary. I'm not sure it's noticeably faster, but it |
|
87 does seem cleaner. (We do waste a bit of space in the return |
|
88 value, though, since we don't shrink it to the final size returned.) */ |
|
89 |
|
90 #define INIT_ALLOC 75 /* Doesn't much matter what this number is. */ |
|
91 |
|
92 static str_list_type |
|
93 dir_list_search P3C(str_llist_type *, dirs, const_string, name, |
|
94 boolean, search_all) |
|
95 { |
|
96 str_llist_elt_type *elt; |
|
97 str_list_type ret; |
|
98 unsigned name_len = strlen (name); |
|
99 unsigned allocated = INIT_ALLOC; |
|
100 string potential = xmalloc (allocated); |
|
101 |
|
102 ret = str_list_init (); |
|
103 |
|
104 for (elt = *dirs; elt; elt = STR_LLIST_NEXT (*elt)) |
|
105 { |
|
106 const_string dir = STR_LLIST (*elt); |
|
107 unsigned dir_len = strlen (dir); |
|
108 |
|
109 while (dir_len + name_len + 1 > allocated) |
|
110 { |
|
111 allocated += allocated; |
|
112 XRETALLOC (potential, allocated, char); |
|
113 } |
|
114 |
|
115 strcpy (potential, dir); |
|
116 strcat (potential, name); |
|
117 |
|
118 if (kpse_readable_file (potential)) |
|
119 { |
|
120 str_list_add (&ret, potential); |
|
121 |
|
122 /* Move this element towards the top of the list. */ |
|
123 str_llist_float (dirs, elt); |
|
124 |
|
125 /* If caller only wanted one file returned, no need to |
|
126 terminate the list with NULL; the caller knows to only look |
|
127 at the first element. */ |
|
128 if (!search_all) |
|
129 return ret; |
|
130 |
|
131 /* Start new filename. */ |
|
132 allocated = INIT_ALLOC; |
|
133 potential = xmalloc (allocated); |
|
134 } |
|
135 } |
|
136 |
|
137 /* If we get here, either we didn't find any files, or we were finding |
|
138 all the files. But we're done with the last filename, anyway. */ |
|
139 free (potential); |
|
140 |
|
141 return ret; |
|
142 } |
|
143 |
|
144 /* This is called when NAME is absolute or explicitly relative; if it's |
|
145 readable, return (a list containing) it; otherwise, return NULL. */ |
|
146 |
|
147 static str_list_type |
|
148 absolute_search P1C(string, name) |
|
149 { |
|
150 str_list_type ret_list; |
|
151 string found = kpse_readable_file (name); |
|
152 |
|
153 /* Some old compilers can't initialize structs. */ |
|
154 ret_list = str_list_init (); |
|
155 |
|
156 /* If NAME wasn't found, free the expansion. */ |
|
157 if (name != found) |
|
158 free (name); |
|
159 |
|
160 /* Add `found' to the return list even if it's null; that tells |
|
161 the caller we didn't find anything. */ |
|
162 str_list_add (&ret_list, found); |
|
163 |
|
164 return ret_list; |
|
165 } |
|
166 |
|
167 /* If DB_DIR is a prefix of PATH_ELT, return true; otherwise false. |
|
168 That is, the question is whether to try the db for a file looked up |
|
169 in PATH_ELT. If PATH_ELT == ".", for example, the answer is no. If |
|
170 PATH_ELT == "/usr/local/lib/texmf/fonts//tfm", the answer is yes. |
|
171 |
|
172 In practice, ls-R is only needed for lengthy subdirectory |
|
173 comparisons, but there's no gain to checking PATH_ELT to see if it is |
|
174 a subdir match, since the only way to do that is to do a string |
|
175 search in it, which is all we do anyway. |
|
176 |
|
177 In fact, we do a simple string compare, ignoring // complications, |
|
178 since in practice I believe //'s will always be after `kpse_db_dir', |
|
179 i.e., we would never want to find ls-R in /usr//texmf. */ |
|
180 |
|
181 static boolean |
|
182 elt_in_db P1C(const_string, path_elt) |
|
183 { |
|
184 boolean found = false; |
|
185 |
|
186 /* If `kpse_db_dir' is not set, we're being called from `read_files' |
|
187 for the very first time -- for cnf file initialization. We can't |
|
188 use ls-R for that. */ |
|
189 if (kpse_db_dir) |
|
190 { |
|
191 string db_temp = kpse_db_dir; |
|
192 |
|
193 while (!found && *db_temp++ == *path_elt++) |
|
194 { /* If we've matched the entire db directory, it's good. */ |
|
195 if (*db_temp == 0) |
|
196 found = true; |
|
197 /* If we've reached the end of PATH_ELT, but not the end of the db |
|
198 directory, it's no good. */ |
|
199 else if (*path_elt == 0) |
|
200 break; |
|
201 } |
|
202 } |
|
203 |
|
204 return found; |
|
205 } |
|
206 |
|
207 |
|
208 /* This is the hard case -- look for NAME in PATH. If |
|
209 ALL is false, just return the first file found. Otherwise, |
|
210 search all elements of PATH. */ |
|
211 |
|
212 static str_list_type |
|
213 path_search P4C(const_string, path, string, name, |
|
214 boolean, must_exist, boolean, all) |
|
215 { |
|
216 string elt; |
|
217 str_list_type ret_list; |
|
218 boolean done = false; |
|
219 ret_list = str_list_init (); /* some compilers lack struct initialization */ |
|
220 |
|
221 for (elt = kpse_path_element (path); !done && elt; |
|
222 elt = kpse_path_element (NULL)) |
|
223 { |
|
224 boolean try_db; |
|
225 boolean allow_disk_search = true; |
|
226 str_list_type *found = NULL; |
|
227 |
|
228 if (*elt == '!' && *(elt + 1) == '!') |
|
229 { /* Magic leading chars in a path element means don't search the |
|
230 disk regardless. And move past the magic to get to the name. */ |
|
231 allow_disk_search = false; |
|
232 elt += 2; |
|
233 } |
|
234 |
|
235 /* Try the prebuilt db only if it's relevant to this path element. */ |
|
236 try_db = elt_in_db (elt); |
|
237 found = try_db ? kpse_db_search (name, elt, all) : NULL; |
|
238 |
|
239 /* Search the filesystem if (1) the path spec allows it, and either |
|
240 (2a) the db was irrelevant to ELT (try_db == false); or |
|
241 (2b) no db exists (kpse_db_search returns NULL); or |
|
242 (3) NAME was not in the db (kpse_db_search returns an empty list) |
|
243 and MUST_EXIST. |
|
244 In (2a) and (2b), `found' will be NULL. */ |
|
245 if (allow_disk_search && (!found || (!STR_LIST (*found) && must_exist))) |
|
246 { |
|
247 str_llist_type *dirs = kpse_element_dirs (elt); |
|
248 if (dirs && *dirs) |
|
249 { |
|
250 if (!found) |
|
251 found = XTALLOC1 (str_list_type); |
|
252 *found = dir_list_search (dirs, name, all); |
|
253 } |
|
254 } |
|
255 |
|
256 /* Did we find anything anywhere? */ |
|
257 if (found && STR_LIST (*found)) |
|
258 if (all) |
|
259 str_list_concat (&ret_list, *found); |
|
260 else |
|
261 { |
|
262 str_list_add (&ret_list, STR_LIST_ELT (*found, 0)); |
|
263 done = true; |
|
264 } |
|
265 |
|
266 /* Free the list space, if any (but not the elements). */ |
|
267 if (found) |
1269
|
268 { |
|
269 str_list_free (found); |
|
270 free (found); |
|
271 } |
1268
|
272 } |
|
273 |
|
274 /* Free the expanded name we were passed. It can't be in the return |
|
275 list, since the path directories got unconditionally prepended. */ |
|
276 free (name); |
|
277 |
|
278 return ret_list; |
|
279 } |
|
280 |
|
281 /* Search PATH for ORIGINAL_NAME. If ALL is false, or ORIGINAL_NAME is |
|
282 absolute_p, check ORIGINAL_NAME itself. Otherwise, look at each |
|
283 element of PATH for the first readable ORIGINAL_NAME. |
|
284 |
|
285 Always return a list; if no files are found, the list will |
|
286 contain just NULL. If ALL is true, the list will be |
|
287 terminated with NULL. */ |
|
288 |
|
289 static string * |
|
290 search P4C(const_string, path, const_string, original_name, |
|
291 boolean, must_exist, boolean, all) |
|
292 { |
|
293 str_list_type ret_list; |
|
294 |
|
295 /* Make a leading ~ count as an absolute filename, and expand $FOO's. */ |
|
296 string name = kpse_expand (original_name); |
|
297 |
|
298 /* If the first name is absolute or explicitly relative, no need to |
|
299 consider PATH at all. */ |
|
300 boolean absolute_p = kpse_absolute_p (name, true); |
|
301 |
|
302 if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) |
|
303 DEBUGF4 ("search(file=%s, must_exist=%d, find_all=%d, path=%s).\n", |
|
304 name, must_exist, all, path); |
|
305 |
|
306 /* Find the file(s). */ |
|
307 ret_list = absolute_p ? absolute_search (name) |
|
308 : path_search (path, name, must_exist, all); |
|
309 |
|
310 /* Append NULL terminator if we didn't find anything at all, or we're |
|
311 supposed to find ALL and the list doesn't end in NULL now. */ |
|
312 if (STR_LIST_LENGTH (ret_list) == 0 |
|
313 || (all && STR_LIST_LAST_ELT (ret_list) != NULL)) |
|
314 str_list_add (&ret_list, NULL); |
|
315 |
|
316 /* Record the filenames we found, if desired. And wrap them in a |
|
317 debugging line if we're doing that. */ |
|
318 if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) |
|
319 DEBUGF1 ("search(%s) =>", original_name); |
|
320 log_search (ret_list); |
|
321 if (KPSE_DEBUG_P (KPSE_DEBUG_SEARCH)) |
|
322 putc ('\n', stderr); |
|
323 |
|
324 return STR_LIST (ret_list); |
|
325 } |
|
326 |
|
327 /* Search PATH for the first NAME. */ |
|
328 |
|
329 string |
|
330 kpse_path_search P3C(const_string, path, const_string, name, |
|
331 boolean, must_exist) |
|
332 { |
1272
|
333 static string *ret_list = 0 |
1269
|
334 |
|
335 if (ret_list) |
1272
|
336 { |
|
337 free (ret_list); |
|
338 ret_list = 0; /* Don't let an interrupt in search() cause trouble */ |
|
339 } |
1269
|
340 |
|
341 ret_list = search (path, name, must_exist, false); |
|
342 |
|
343 return *ret_list; /* Freeing this is caller's responsibility */ |
1268
|
344 } |
|
345 |
|
346 |
|
347 /* Search all elements of PATH for files named NAME. Not sure if it's |
|
348 right to assert `must_exist' here, but it suffices now. */ |
|
349 |
|
350 string * |
|
351 kpse_all_path_search P2C(const_string, path, const_string, name) |
|
352 { |
|
353 string *ret = search (path, name, true, true); |
|
354 return ret; |
|
355 } |
|
356 |
|
357 #ifdef TEST |
|
358 |
|
359 void |
|
360 test_path_search (const_string path, const_string file) |
|
361 { |
|
362 string answer; |
|
363 string *answer_list; |
|
364 |
|
365 printf ("\nSearch %s for %s:\t", path, file); |
|
366 answer = kpse_path_search (path, file); |
|
367 puts (answer ? answer : "(null)"); |
|
368 |
|
369 printf ("Search %s for all %s:\t", path, file); |
|
370 answer_list = kpse_all_path_search (path, file); |
|
371 putchar ('\n'); |
|
372 while (*answer_list) |
|
373 { |
|
374 putchar ('\t'); |
|
375 puts (*answer_list); |
|
376 answer_list++; |
|
377 } |
|
378 } |
|
379 |
|
380 #define TEXFONTS "/usr/local/lib/tex/fonts" |
|
381 |
|
382 int |
|
383 main () |
|
384 { |
|
385 /* All lists end with NULL. */ |
|
386 test_path_search (".", "nonexistent"); |
|
387 test_path_search (".", "/nonexistent"); |
|
388 test_path_search ("/k:.", "kpathsea.texi"); |
|
389 test_path_search ("/k:.", "/etc/fstab"); |
|
390 test_path_search (".:" TEXFONTS "//", "cmr10.tfm"); |
|
391 test_path_search (".:" TEXFONTS "//", "logo10.tfm"); |
|
392 test_path_search (TEXFONTS "//times:.::", "ptmr.vf"); |
|
393 test_path_search (TEXFONTS "/adobe//:" |
|
394 "/usr/local/src/TeX+MF/typefaces//", "plcr.pfa"); |
|
395 |
|
396 test_path_search ("~karl", ".bashrc"); |
|
397 test_path_search ("/k", "~karl/.bashrc"); |
|
398 |
|
399 xputenv ("NONEXIST", "nonexistent"); |
|
400 test_path_search (".", "$NONEXISTENT"); |
|
401 xputenv ("KPATHSEA", "kpathsea"); |
|
402 test_path_search ("/k:.", "$KPATHSEA.texi"); |
|
403 test_path_search ("/k:.", "${KPATHSEA}.texi"); |
|
404 test_path_search ("$KPATHSEA:.", "README"); |
|
405 test_path_search (".:$KPATHSEA", "README"); |
|
406 |
|
407 return 0; |
|
408 } |
|
409 |
|
410 #endif /* TEST */ |
|
411 |
|
412 |
|
413 /* |
|
414 Local variables: |
|
415 test-compile-command: "gcc -posix -g -I. -I.. -DTEST pathsearch.c kpathsea.a" |
|
416 End: |
|
417 */ |