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