1 /*
2 * pathwalk.c -- functions for ooking for files, reading files etc.
3 *
4 * Copyright (c) 1988, 89, 90, 91, 92, 93 Miguel Santana
5 * Copyright (c) 1995, 96, 97, 98, 99 Akim Demaille, Miguel Santana
6 */
7
8 /*
9 * This program is free software; you can redistribute it and/or modify
10 * it under the terms of the GNU General Public License as published by
11 * the Free Software Foundation; either version 2, or (at your option)
12 * any later version.
13 *
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU General Public License for more details.
18 *
19 * You should have received a copy of the GNU General Public License
20 * along with this program; see the file COPYING. If not, write to
21 * the Free Software Foundation, 59 Temple Place - Suite 330,
22 * Boston, MA 02111-1307, USA.
23 */
24
25 /* I know this file would need a full rewrite, nevertheless, since
26 some day we should use kpathsea, it would be a waste of time.
27 There are other files to rewrite :). */
28
29 #include "a2ps.h"
30 #include "pathwalk.h"
31 #include "darray.h"
32 #include "message.h"
33 #include "routines.h"
34 #include "filtdir.h"
35 #include "lister.h"
36 #include "strverscmp.h"
37 #include "quotearg.h"
38 #include "dirname.h"
39
40 /*---------------------------------.
41 | Alloca D to contain "DIR/FILE". |
42 `---------------------------------*/
43 #define apathconcat(D,Dir,File) \
44 do { \
45 char * tmp1 = (char *) (Dir); \
46 const char * tmp2 = (const char *) (File); \
47 D = ALLOCA (char, (strlen (tmp1) \
48 + strlen (tmp2) + 2)); \
49 tmp1 = stpcpy (D, tmp1); \
50 (*tmp1++) = DIRECTORY_SEPARATOR; \
51 stpcpy (tmp1, tmp2); \
52 } while (0)
53
54 /************************************************************************/
55 /* Handling the path: an array, NULL terminated of char * */
56 /************************************************************************/
57 static char **
pw_internal_string_to_path(const char * path,char sep,int * length)58 pw_internal_string_to_path (const char * path, char sep, int * length)
59 {
60 char **res = NULL;
61 int allocated = 5; /* num of entries yet allocated in res */
62 int entries = 0;
63 const char *cp, *cp2;
64 int len;
65
66 res = XCALLOC (char *, allocated);
67 for (cp = path; cp; cp = strchr (cp, sep))
68 {
69 if (cp != path)
70 cp++;
71
72 cp2 = strchr (cp, sep);
73 if (cp2)
74 len = cp2 - cp;
75 else
76 len = strlen (cp);
77
78 if (len == 0)
79 {
80 /* Skip empty entries */
81 cp++;
82 continue;
83 }
84 else
85 {
86 /* Make sure _not_ to include that last DIRECTORY_SEPARATOR */
87 if (cp [len] == DIRECTORY_SEPARATOR)
88 len --;
89 }
90
91 res [ entries ] = XMALLOC (char, len + 1);
92 strncpy (res [entries], cp, len);
93 res [entries] [len] = '\0';
94
95 entries++;
96 if (entries >= allocated)
97 {
98 allocated *= 2;
99 res = XREALLOC (res, char *, allocated);
100 }
101 }
102 *length = entries;
103
104 /* Make it null-terminated, and exactely that size */
105 res [*length] = NULL;
106 res = XREALLOC (res, char *, *length + 1);
107 return res;
108 }
109
110 /*
111 * Length of a path
112 */
113 static inline int
pw_path_length(char ** path)114 pw_path_length (char ** path)
115 {
116 int res;
117
118 if (!path)
119 return 0;
120
121 for (res = 0 ; path [res] ; res ++)
122 /* Nada */;
123 return res;
124 }
125
126 /*-----------------------------------------------------------------.
127 | Build a path as array from a PATH as string, given the separator |
128 | PATH is read only. |
129 `-----------------------------------------------------------------*/
130
131 char **
pw_string_to_path(const char * path)132 pw_string_to_path (const char * path)
133 {
134 int dummy;
135 return pw_internal_string_to_path (path, PATH_SEPARATOR, &dummy);
136 }
137
138 /*
139 * Concat PATH2 to PATH1, and return the result. Free PATH2
140 */
141
142 static inline char **
pw_path_concat(char ** path1,int len1,char ** path2,int len2)143 pw_path_concat (char ** path1, int len1, char ** path2, int len2)
144 {
145 int i;
146
147 if (path2)
148 {
149 path1 = XREALLOC (path1, char *, len1 + len2 + 1);
150 for (i = 0 ; i <= len2 ; i++)
151 path1 [len1 + i] = path2 [i];
152 free (path2);
153 }
154
155 return path1;
156 }
157
158 /*-------------------------------------.
159 | Append a string-path DIR2 to PATH1. |
160 `-------------------------------------*/
161
162 char **
pw_append_string_to_path(char ** path1,const char * dir2)163 pw_append_string_to_path (char ** path1, const char * dir2)
164 {
165 int len1, len2;
166 char ** path2;
167
168 len1 = pw_path_length (path1);
169 path2 = pw_internal_string_to_path (dir2, PATH_SEPARATOR, &len2);
170
171 return pw_path_concat (path1, len1, path2, len2);
172 }
173
174 /*--------------------------------------.
175 | Prepend a string-path DIR2 to PATH1. |
176 `--------------------------------------*/
177 char **
pw_prepend_string_to_path(char ** path1,const char * dir2)178 pw_prepend_string_to_path (char ** path1, const char * dir2)
179 {
180 int len1, len2;
181 char ** path2;
182
183 len1 = pw_path_length (path1);
184 path2 = pw_internal_string_to_path (dir2, PATH_SEPARATOR, &len2);
185
186 return pw_path_concat (path2, len2, path1, len1);
187 }
188
189 /*
190 * Free a path array, and its content
191 */
192 void
pw_free_path(char ** path)193 pw_free_path (char ** path)
194 {
195 int i;
196 if (path)
197 for (i = 0 ; path[i] ; i++)
198 free (path[i]);
199 XFREE (path);
200 }
201
202 void
pw_fprintf_path(FILE * stream,const char * format,char * const * path)203 pw_fprintf_path (FILE * stream, const char * format, char * const * path)
204 {
205 if (path)
206 while (*path) {
207 fprintf (stream, format, *path);
208 path++;
209 }
210 }
211
212 /*-------------------------------------------------------------------.
213 | Return the index+1 in PATH of the directory that contains the file |
214 | concat(NAME, SUFFIX). |
215 `-------------------------------------------------------------------*/
216
217 static int
pw_find_file_index(char * const * path,const char * name,const char * suffix)218 pw_find_file_index (char * const * path,
219 const char *name, const char *suffix)
220 {
221 int i;
222 struct stat stat_st;
223 char * filename, * fullpath;
224
225 if (suffix)
226 astrcat2 (filename, name, suffix);
227 else
228 filename = (char *) name;
229
230 message (msg_pw,
231 (stderr, "pw: looking for `%s'\n", filename));
232
233 if (path)
234 for (i = 0 ; path [i] ; i ++)
235 {
236 apathconcat (fullpath, path [i], filename);
237 if (stat (fullpath, &stat_st) == 0)
238 {
239 /* File exists */
240 message (msg_pw, (stderr, "pw: success in %s\n", path[i]));
241 return i + 1;
242 }
243 }
244
245 if (msg_test (msg_pw))
246 {
247 fprintf (stderr, "pw: did not find `%s' in path\n", filename);
248 pw_fprintf_path (stderr, "pw: %s\n", path);
249 }
250
251 return 0;
252 }
253
254 /*--------------------------------.
255 | Return non 0 if the file exists |
256 `--------------------------------*/
257
258 int
pw_file_exists_p(char * const * path,const char * name,const char * suffix)259 pw_file_exists_p (char * const * path,
260 const char *name, const char * suffix)
261 {
262 return pw_find_file_index (path, name, suffix);
263 }
264
265 /*------------------------------------------------------------------.
266 | Return the malloc'd full path of existing file named concat(NAME, |
267 | SUFFIX). |
268 `------------------------------------------------------------------*/
269
270 static inline char *
_pw_find_file(char * const * path,const char * name,const char * suffix)271 _pw_find_file (char * const * path,
272 const char * name, const char * suffix)
273 {
274 char * res;
275 int i;
276
277 i = pw_find_file_index (path, name, suffix);
278
279 if (i)
280 {
281 /* Return a malloc'ed full file name */
282 if (suffix)
283 {
284 res = XMALLOC (char,
285 strlen (path[i-1]) + 2
286 + strlen (name) + strlen (suffix));
287 sprintf (res, "%s%c%s%s", path [i-1], DIRECTORY_SEPARATOR,
288 name, suffix);
289 }
290 else
291 {
292 res = XMALLOC (char,
293 strlen (path[i-1]) + 2
294 + strlen (name));
295 sprintf (res, "%s%c%s", path [i-1], DIRECTORY_SEPARATOR,
296 name);
297 }
298 return res;
299 }
300 else
301 return NULL;
302 }
303
304 /*
305 * Inline wrapper
306 */
307 char *
pw_find_file(char * const * path,const char * name,const char * suffix)308 pw_find_file (char * const * path,
309 const char * name, const char * suffix)
310 {
311 return _pw_find_file (path, name, suffix);
312 }
313
314 /* Return the malloc'd full path of existing file named concat(NAME,
315 SUFFIX), exits on failure. */
316
317 char *
xpw_find_file(char * const * path,const char * name,const char * suffix)318 xpw_find_file (char * const * path,
319 const char * name, const char * suffix)
320 {
321 char * res = _pw_find_file (path, name, suffix);
322
323 if (!res)
324 {
325 char *file;
326 file = ALLOCA (char, strlen (name) + (suffix ? strlen (suffix) : 0) + 1);
327 sprintf (file, "%s%s", name, UNNULL (suffix));
328 error (1, errno, _("cannot find file `%s'"), quotearg (file));
329 }
330 return res;
331 }
332
333 /* Idem, but look first around the given INCLUDING_FILE. */
334
335 char *
xpw_find_included_file(char * const * path,const char * including_file,const char * name,const char * suffix)336 xpw_find_included_file (char * const *path,
337 const char *including_file,
338 const char *name, const char *suffix)
339 {
340 char *dir; /* Of the including file. */
341 char *res;
342 struct stat statbuf;
343
344 if (*name == DIRECTORY_SEPARATOR)
345 /* Path is absolute */
346 dir = NULL;
347 else
348 /* Relative. Give its root. */
349 dir = dir_name (including_file);
350
351 res = ALLOCA (char, (strlen (dir)
352 + strlen (name)
353 + (suffix ? strlen (suffix) : 0)
354 + 2));
355 sprintf (res, "%s%c%s%s", dir, DIRECTORY_SEPARATOR,
356 name, suffix ? suffix : "");
357 XFREE (dir);
358 if (stat (res, &statbuf) == 0)
359 return xstrdup (res);
360
361 /* Find in the library. */
362 return xpw_find_file (path, name, suffix);
363 }
364
365 /*
366 * Dump a library file content
367 */
368 int
pw_paste_file(char * const * path,const char * name,const char * suffix)369 pw_paste_file (char * const * path,
370 const char * name, const char * suffix)
371 {
372 char buf[512];
373 char * fullpath;
374 FILE * fp;
375 int line = 0;
376
377 message (msg_pw,
378 (stderr, "pw: pasting `%s%s'\n", name, suffix ? suffix : ""));
379
380 fullpath = _pw_find_file (path, name, suffix);
381
382 if (!fullpath)
383 return 0;
384
385 fp = fopen (fullpath, "r");
386
387 if (fp == NULL)
388 return 0;
389
390 /* Find the end of the header. */
391 #define HDR_TAG "% -- code follows this line --"
392 while ((fgets (buf, sizeof (buf), fp)))
393 {
394 line++;
395 if (strnequ (buf, HDR_TAG, strlen (HDR_TAG)))
396 break;
397 }
398
399 /* Dump rest of file. */
400 #define INCL_TAG "% -- include file:"
401 while ((fgets (buf, sizeof (buf), fp)))
402 {
403 line++;
404 if (strnequ (buf, INCL_TAG, strlen (INCL_TAG)))
405 {
406 char * file = buf + strlen (INCL_TAG);
407 file = strtok (file, " \n\t");
408 message (msg_pw,
409 (stderr,
410 "pw: including file '%s' upon request given in '%s':%d\n",
411 file, fullpath, line));
412 if (!pw_paste_file (path, file, NULL))
413 error_at_line (1, errno, fullpath, line,
414 _("cannot find file `%s'"), quotearg (file));
415 continue;
416 }
417 fputs (buf, stdout);
418 }
419
420 fclose (fp);
421 free (fullpath);
422 return 1;
423 }
424
425 /* Helping functions for pw_glob. */
426
427 static bool
pw_filter_fnmatch(PARAM_UNUSED const char * dir,const char * file,const char * pattern)428 pw_filter_fnmatch (PARAM_UNUSED const char * dir, const char *file,
429 const char *pattern)
430 {
431 return !fnmatch (pattern, file, 0);
432 }
433
434 static void
pw_filter_da_append(PARAM_UNUSED const char * dir,const char * file,struct darray * da)435 pw_filter_da_append (PARAM_UNUSED const char * dir, const char *file,
436 struct darray *da)
437 {
438 da_append (da, xstrdup (file));
439 }
440
441 static void
pw_filter_print(const char * dir,const char * file,FILE * stream)442 pw_filter_print (const char * dir, const char *file, FILE *stream)
443 {
444 fprintf (stream, "%s%c%s\n", dir, DIRECTORY_SEPARATOR, file);
445 }
446
447 static void
pw_filterdir(char * const * path,filterdir_filter_t filter,void * filtarg,filterdir_fun_t fun,void * arg)448 pw_filterdir (char * const * path,
449 filterdir_filter_t filter, void *filtarg,
450 filterdir_fun_t fun, void *arg)
451 {
452 for (/* Nothing */ ; *path ; path++)
453 filterdir (*path, filter, filtarg, fun, arg);
454 }
455
456
457 /*------------------------------------------------------------------.
458 | Call glob on PATTERN in each dir of PATH. Return a malloc'd char |
459 | ** (char * malloc'ed too). |
460 `------------------------------------------------------------------*/
461
462 struct darray *
pw_glob(char * const * path,const char * pattern)463 pw_glob (char * const * path, const char * pattern)
464 {
465 struct darray * res;
466
467 if (msg_test (msg_pw))
468 {
469 fprintf (stderr, "pw: globbing `%s'\n", pattern);
470 pw_fprintf_path (stderr, "\t-> %s\n", path);
471 }
472
473 res = da_new ("Dir entries", 20,
474 da_geometrical, 2,
475 (da_print_func_t) da_str_print,
476 (da_cmp_func_t) strverscmp);
477
478 pw_filterdir (path,
479 (filterdir_filter_t) pw_filter_fnmatch, (void *) pattern,
480 (filterdir_fun_t) pw_filter_da_append, res);
481
482 da_qsort (res);
483 da_unique (res, (da_map_func_t) free);
484
485 return res;
486 }
487
488 void
pw_glob_print(char * const * path,const char * pattern,FILE * stream)489 pw_glob_print (char * const * path, const char * pattern, FILE *stream)
490 {
491 pw_filterdir (path,
492 (filterdir_filter_t) pw_filter_fnmatch, (void *) pattern,
493 (filterdir_fun_t) pw_filter_print, stream);
494 }
495
496 /*
497 * Cut the suffix of a string (i.e. cut at last `.')
498 */
499
500 static void
da_str_cut_suffix(char * string)501 da_str_cut_suffix (char * string)
502 {
503 *strrchr (string, '.') = '\0';
504 }
505
506 /*----------------------------------------------------------------.
507 | Return malloc'd array of malloc'ed char * of the prefix part of |
508 | the file having SUFFIX as suffix in PATH. |
509 `----------------------------------------------------------------*/
510
511 struct darray *
pw_glob_on_suffix(char * const * path,const char * suffix)512 pw_glob_on_suffix (char * const * path, const char * suffix)
513 {
514 struct darray * res;
515 char * pattern;
516
517 /* Build the pattern and glob */
518 astrcat2 (pattern, "*", suffix);
519 res = pw_glob (path, pattern);
520
521 /* Cut the suffixes */
522 da_map (res, (da_map_func_t) da_str_cut_suffix);
523
524 return res;
525 }
526
527 /* Use lister to report on STREAM the list of files in PATH that end
528 by SUFFIX. */
529
530 void
pw_lister_on_suffix(FILE * stream,char * const * path,const char * suffix)531 pw_lister_on_suffix (FILE * stream, char * const * path, const char * suffix)
532 {
533 struct darray * entries;
534
535 entries = pw_glob_on_suffix (path, suffix);
536 lister_fprint_vertical (NULL, stream,
537 (void *) entries->content, entries->len,
538 (lister_width_t) strlen,
539 (lister_print_t) fputs);
540 da_free (entries, (da_map_func_t) free);
541 }
542