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