1 /* walker.c -- nifty file-tree walker
2    Copyright (C) 1986, 1995-1996, 1999-2000, 2008-2012 Free Software
3    Foundation, Inc.
4    Written by Greg McGary <gkm@gnu.ai.mit.edu>
5 
6    This program is free software; you can redistribute it and/or modify
7    it under the terms of the GNU General Public License as published by
8    the Free Software Foundation; either version 3, or (at your option)
9    any later version.
10 
11    This program is distributed in the hope that it will be useful,
12    but WITHOUT ANY WARRANTY; without even the implied warranty of
13    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14    GNU General Public License for more details.
15 
16    You should have received a copy of the GNU General Public License
17    along with this program.  If not, see <http://www.gnu.org/licenses/>.
18 */
19 
20 #include <config.h>
21 #include <stdio.h>
22 #include <stdlib.h>
23 #include <unistd.h>
24 #include <string.h>
25 #include <fnmatch.h>
26 #include <dirent.h>
27 #include <errno.h>
28 #include <alloca.h>
29 #include <xalloc.h>
30 #include <error.h>
31 #include <pathmax.h>
32 #include <sys/stat.h>
33 
34 #include "xnls.h"
35 #include "idfile.h"
36 #include "dynvec.h"
37 #include "scanners.h"
38 #include "iduglobal.h"
39 
40 int walker_verbose_flag = 0;
41 off_t largest_member_file = 0;
42 
43 static char **vectorize_string (char *string, char const *delimiter_class);
44 static int walk_dir (struct file_link *dir_link);
45 static struct member_file *get_member_file (struct file_link *flink);
46 static struct lang_args *get_lang_args (struct file_link const *flink);
47 static void print_member_file (struct member_file *member);
48 static int walk_sub_dirs (struct dynvec *sub_dirs_vec);
49 static void reparent_children (struct file_link *dlink, struct file_link *slink);
50 static int classify_link (struct file_link *flink, struct stat *stp);
51 static struct file_link *get_link_from_dirent (struct dirent *dirent,
52 					       struct file_link *parent);
53 static struct file_link *make_link_from_dirent (struct dirent *dirent,
54 						struct file_link *parent);
55 static struct file_link *get_link_from_string (char const *name,
56 					       struct file_link *parent);
57 static struct file_link *make_link_from_string (char const *name,
58 						struct file_link *parent);
59 static int lang_wanted (char const *lang_name);
60 static char **append_strings_to_vector (char **vector_0, char *string,
61 					char const *delimiter_class);
62 static int string_in_vector (char const *string, char **vector);
63 static int same_as_dot (char const *cwd);
64 static struct file_link const
65   **fill_link_vector (struct file_link const **vec_buf,
66 		      struct file_link const *flink);
67 static struct file_link const
68   **fill_link_vector_1 (struct file_link const **vec_buf,
69 			struct file_link const *flink);
70 static char *fill_dot_dots (char *buf, int levels);
71 static char *absolute_file_name_1 (char *buffer, struct file_link const *flink);
72 static unsigned long member_file_hash_1 (void const *key);
73 static unsigned long member_file_hash_2 (void const *key);
74 static int member_file_hash_compare (void const *x, void const *y);
75 static unsigned long file_link_hash_1 (void const *key);
76 static unsigned long file_link_hash_2 (void const *key);
77 static int file_link_hash_compare (void const *x, void const *y);
78 static unsigned long dev_ino_hash_1 (void const *key);
79 static unsigned long dev_ino_hash_2 (void const *key);
80 static int dev_ino_hash_compare (void const *x, void const *y);
81 static int symlink_ancestry (struct file_link *flink);
82 
83 #if HAVE_LINK
84 
85 static struct file_link *find_alias_link (struct file_link *flink,
86 					  struct stat *stp);
87 static struct member_file *maybe_get_member_file (struct file_link *flink,
88 						  struct stat *stp);
89 
90 #endif
91 
92 #define IS_DOT(s) ((s)[0] == '.' && (s)[1] == '\0')
93 #define IS_DOT_DOT(s) ((s)[0] == '.' && (s)[1] == '.' && (s)[2] == '\0')
94 #define IS_DOT_or_DOT_DOT(s) \
95     (((s)[0] == '.') && (((s)[1] == '\0') || ((s)[1] == '.' && (s)[2] == '\0')))
96 
97 static struct file_link *current_dir_link = 0;
98 
99 static char const white_space[] = " \t\r\n\v\f";
100 
101 char* xgetcwd (void);
102 
103 
104 /****************************************************************************/
105 /* Walk the file-system tree rooted at `dir_link', looking for files
106    that are eligible for scanning.  */
107 
108 static int
walk_dir(struct file_link * dir_link)109 walk_dir (struct file_link *dir_link)
110 {
111   int scannable_files;
112   struct dynvec *sub_dirs_vec;
113   DIR *dirp;
114 
115   if (!chdir_to_link (dir_link))
116     return 0;
117   dirp = opendir (".");
118   if (dirp == 0)
119     {
120       char *file_name = alloca (PATH_MAX);
121       absolute_file_name (file_name, dir_link);
122       error (0, errno, _("can't read directory `%s' (`.' from `%s')"), file_name, xgetcwd ());
123       return 0;
124     }
125   sub_dirs_vec = make_dynvec (32);
126   scannable_files = 0;
127   for (;;)
128     {
129       struct file_link *flink;
130       struct dirent *dirent = readdir (dirp);
131 
132       if (dirent == 0)
133 	break;
134       if (IS_DOT_or_DOT_DOT (dirent->d_name))
135 	continue;
136 
137       flink = get_link_from_dirent (dirent, dir_link);
138       if (!(flink->fl_flags & FL_PRUNE))
139 	walk_flink (flink, sub_dirs_vec);
140     }
141   closedir (dirp);
142 
143   scannable_files += walk_sub_dirs (sub_dirs_vec);
144   dynvec_free (sub_dirs_vec);
145   return scannable_files;
146 }
147 
148 /* Walk the directories found by walk_dir, calling walk_dir
149    recursively for each directory. */
150 
151 static int
walk_sub_dirs(struct dynvec * sub_dirs_vec)152 walk_sub_dirs (struct dynvec *sub_dirs_vec)
153 {
154   struct file_link **sub_dirs;
155   struct file_link **sub_dirs_end;
156   int total_scannable_files = 0;
157 
158   dynvec_freeze (sub_dirs_vec);
159   sub_dirs_end = (struct file_link **)
160     &sub_dirs_vec->dv_vec[sub_dirs_vec->dv_fill];
161   sub_dirs = (struct file_link **) sub_dirs_vec->dv_vec;
162   for ( ; sub_dirs < sub_dirs_end; sub_dirs++)
163     {
164       struct file_link *sub_dir_link = *sub_dirs;
165       int scannable_files = walk_dir (sub_dir_link);
166       if (scannable_files)
167 	total_scannable_files += scannable_files;
168     }
169   return total_scannable_files;
170 }
171 
172 void
walk_flink(struct file_link * flink,struct dynvec * sub_dirs_vec)173 walk_flink (struct file_link *flink, struct dynvec *sub_dirs_vec)
174 {
175   struct stat st;
176   unsigned int old_flags;
177   unsigned int new_flags;
178 
179   new_flags = classify_link (flink, &st);
180   if (new_flags == 0)
181     return;
182 
183   old_flags = flink->fl_flags;
184   if ((old_flags & FL_TYPE_MASK)
185       && (old_flags & FL_TYPE_MASK) != (new_flags & FL_TYPE_MASK))
186     {
187       char *file_name = alloca (PATH_MAX);
188       absolute_file_name (file_name, flink);
189       error (0, 0, _("notice: `%s' was a %s, but is now a %s!"), file_name,
190 	     (FL_IS_FILE (old_flags) ? _("file") : _("directory")),
191 	     (FL_IS_FILE (new_flags) ? _("file") : _("directory")));
192     }
193 
194   flink->fl_flags = (old_flags & ~(FL_TYPE_MASK|FL_SYM_LINK)) | new_flags;
195   if (FL_IS_DIR (new_flags))
196     {
197 
198       struct file_link *alias_link;
199 #if HAVE_LINK
200       alias_link = find_alias_link (flink, &st);
201 #else
202       alias_link = 0;
203 #endif /* HAVE_LINK */
204 
205       if (alias_link)
206 	{
207 	  if (!(new_flags & FL_SYM_LINK))
208 	    reparent_children (flink, alias_link);
209 	}
210       else if (sub_dirs_vec == 0)
211 	walk_dir (flink);
212       else
213 	dynvec_append (sub_dirs_vec, flink);
214     }
215   else
216     {
217       struct member_file *member;
218 #if HAVE_LINK
219       member = maybe_get_member_file (flink, &st);
220 #else
221       member = get_member_file (flink);
222 #endif
223       if (member)
224 	{
225 	  if (st.st_size > largest_member_file)
226 	    largest_member_file = st.st_size;
227 	  if (walker_verbose_flag)
228 	    print_member_file (member);
229 	}
230     }
231 }
232 
233 /* Take child file_link nodes from a symlinked directory and give them
234    to a hard linked directory.  This is something of a pain since a
235    file_link's parent node is part of its hash-table key.  We must
236    search the entire hash-table for the children.  With each child, we
237    must delete it, reset its parent link, then reinsert.  */
238 
239 static void
reparent_children(struct file_link * dlink,struct file_link * slink)240 reparent_children (struct file_link *dlink, struct file_link *slink)
241 {
242   void **slot = idh.idh_file_link_table.ht_vec;
243   void **end = &idh.idh_file_link_table.ht_vec[idh.idh_file_link_table.ht_size];
244 
245   for ( ; slot < end; slot++)
246     {
247       if (!HASH_VACANT (*slot))
248 	{
249 	  struct file_link *child = (struct file_link *) *slot;
250 	  if (child->fl_parent == slink)
251 	    {
252 	      void **new_slot;
253 	      *slot = hash_deleted_item;
254 	      child->fl_parent = dlink;
255 	      new_slot = hash_find_slot (&idh.idh_file_link_table, child);
256 	      *new_slot = child;
257 	    }
258 	}
259     }
260 }
261 
262 
263 /****************************************************************************/
264 /* Separate the wheat from the chaff.  Mark those file_links that are
265    components in member files.  */
266 
267 void
mark_member_file_links(struct idhead * idhp)268 mark_member_file_links (struct idhead *idhp)
269 {
270   struct member_file **members_0
271     = (struct member_file **) hash_dump (&idhp->idh_member_file_table,
272 					 0, member_file_qsort_compare);
273   struct member_file **end = &members_0[idhp->idh_member_file_table.ht_fill];
274   struct member_file **members;
275   long new_index = 0;
276 
277   for (members = members_0; members < end; members++)
278     {
279       struct member_file *member = *members;
280       struct file_link *flink;
281       member->mf_index = new_index++;
282       for (flink = member->mf_link;
283 	   !(flink->fl_flags & FL_USED); flink = flink->fl_parent)
284 	flink->fl_flags |= FL_USED;
285     }
286   free (members_0);
287 }
288 
289 
290 #if HAVE_LINK
291 
292 /****************************************************************************/
293 /* Return a `member_file' for this `flink' *if* the filename matches
294    some scan pattern, and no alias for the file takes precedence ([1]
295    hard-links dominate symbolic-links; [2] for two hard-links: first
296    come, first served).  */
297 
298 static struct member_file *
maybe_get_member_file(struct file_link * flink,struct stat * stp)299 maybe_get_member_file (struct file_link *flink, struct stat *stp)
300 {
301   struct file_link *alias_link;
302   struct member_file *member;
303   struct member_file *alias_member = 0;
304 
305   member = get_member_file (flink);
306   alias_link = find_alias_link (flink, stp);
307   if (alias_link)
308     alias_member = find_member_file (alias_link);
309 
310   if (member && alias_member)
311     {
312       int ancestry = symlink_ancestry (flink);
313       int alias_ancestry = symlink_ancestry (alias_link);
314       if (member->mf_lang_args != alias_member->mf_lang_args)
315 	{
316 	  char *file_name = alloca (PATH_MAX);
317 	  char *alias_file_name = alloca (PATH_MAX);
318 	  absolute_file_name (file_name, flink);
319 	  absolute_file_name (alias_file_name, alias_link);
320 	  error (0, 0, _("warning: `%s' and `%s' are the same file, but yield different scans!"),
321 		 file_name, alias_file_name);
322 	}
323       else if (alias_ancestry > ancestry)
324 	{
325 	  hash_delete (&idh.idh_member_file_table, member);
326 	  member->mf_link->fl_flags &= ~FL_MEMBER;
327 	  return 0;
328 	}
329       else
330 	{
331 	  hash_delete (&idh.idh_member_file_table, alias_member);
332 	  alias_member->mf_link->fl_flags &= ~FL_MEMBER;
333 	}
334     }
335   return member;
336 }
337 
338 /* Return a previously registered alias for `flink', if any.  */
339 
340 static struct file_link *
find_alias_link(struct file_link * flink,struct stat * stp)341 find_alias_link (struct file_link *flink, struct stat *stp)
342 {
343   struct dev_ino *dev_ino;
344   struct dev_ino **slot;
345 
346   dev_ino = (struct dev_ino *) obstack_alloc (&idh.idh_dev_ino_obstack, sizeof (struct dev_ino));
347   dev_ino->di_dev = stp->st_dev;
348   dev_ino->di_ino = stp->st_ino;
349   slot = (struct dev_ino **) hash_find_slot (&idh.idh_dev_ino_table, dev_ino);
350   if (HASH_VACANT (*slot))
351     {
352       dev_ino->di_link = flink;
353       hash_insert_at (&idh.idh_dev_ino_table, dev_ino, slot);
354       return 0;
355     }
356   else
357     {
358       obstack_free (&idh.idh_dev_ino_obstack, dev_ino);
359       return (*slot)->di_link;
360     }
361 }
362 
363 /* Return the distance from `flink' to a symbolic-link ancestor
364    directory.  PATH_MAX is considered an infinite distance (e.g.,
365    there are no symlinks between `flink' and the root).  */
366 
367 static int _GL_ATTRIBUTE_PURE
symlink_ancestry(struct file_link * flink)368 symlink_ancestry (struct file_link *flink)
369 {
370   int ancestry = 0;
371   while (!IS_ROOT_FILE_LINK (flink))
372     {
373       if (flink->fl_flags & FL_SYM_LINK)
374 	return ancestry;
375       ancestry++;
376       flink = flink->fl_parent;
377     }
378   return PATH_MAX;
379 }
380 
381 #endif /* HAVE_LINK */
382 
383 static struct member_file *
get_member_file(struct file_link * flink)384 get_member_file (struct file_link *flink)
385 {
386   struct member_file *member;
387   struct member_file **slot;
388   struct lang_args const *args;
389 
390   args = get_lang_args (flink);
391   if (args == 0)
392     return 0;
393   if (!lang_wanted (args->la_language->lg_name))
394     return 0;
395 
396   member = (struct member_file *) obstack_alloc (&idh.idh_member_file_obstack,
397 						 sizeof (struct member_file));
398   member->mf_link = flink;
399   slot = (struct member_file **) hash_find_slot (&idh.idh_member_file_table, member);
400   if (HASH_VACANT (*slot))
401     {
402       member->mf_index = -1;
403       hash_insert_at (&idh.idh_member_file_table, member, slot);
404       flink->fl_flags |= FL_MEMBER;
405     }
406   else
407     {
408       obstack_free (&idh.idh_member_file_obstack, member);
409 #if 0
410       if (member->mf_lang_args != args)
411 	{
412 	  char *file_name = alloca (PATH_MAX);
413 	  absolute_file_name (file_name, flink);
414 	  error (0, 0, _("notice: scan parameters changed for `%s'"), file_name);
415 	  member->mf_old_index = -1;
416 	}
417 #endif
418       member = *slot;
419     }
420   member->mf_lang_args = args;
421   return member;
422 }
423 
424 struct member_file *
find_member_file(struct file_link const * flink)425 find_member_file (struct file_link const *flink)
426 {
427   struct member_file key;
428   struct member_file **slot;
429 
430   key.mf_link = (struct file_link *) flink;
431   slot = (struct member_file **) hash_find_slot (&idh.idh_member_file_table, &key);
432   if (HASH_VACANT (*slot))
433     return 0;
434   return *slot;
435 }
436 
437 /* March down the list of lang_args, and return the first one whose
438    pattern matches FLINK.  Return the matching lang_args, if a
439    scanner exists for that language, otherwise return 0.  */
440 
441 static struct lang_args *
get_lang_args(struct file_link const * flink)442 get_lang_args (struct file_link const *flink)
443 {
444   struct lang_args *args = lang_args_list;
445   char *file_name = alloca (PATH_MAX);
446 
447   while (args)
448     {
449       if (strchr (args->la_pattern, SLASH_CHAR))
450 	{
451 	  absolute_file_name (file_name, flink);
452 	  if (fnmatch (args->la_pattern, file_name, MAYBE_FNM_CASEFOLD | FNM_FILE_NAME) == 0)
453 	    return (args->la_language ? args : 0);
454 	}
455       else
456 	{
457 	  if (fnmatch (args->la_pattern, flink->fl_name, MAYBE_FNM_CASEFOLD) == 0)
458 	    return (args->la_language ? args : 0);
459 	}
460       args = args->la_next;
461     }
462   return ((lang_args_default && lang_args_default->la_language)
463 	  ? lang_args_default : 0);
464 }
465 
466 static void
print_member_file(struct member_file * member)467 print_member_file (struct member_file *member)
468 {
469   char *file_name = alloca (PATH_MAX);
470   absolute_file_name (file_name, member->mf_link);
471   printf ("%ld: %s: %s\n", idh.idh_member_file_table.ht_fill - 1,
472 	  member->mf_lang_args->la_language->lg_name, file_name);
473 }
474 
475 
476 /****************************************************************************/
477 
478 static char **langs_included;
479 static char **langs_excluded;
480 
481 static int
lang_wanted(char const * lang_name)482 lang_wanted (char const *lang_name)
483 {
484   if (langs_excluded)
485     return !string_in_vector (lang_name, langs_excluded);
486   else if (langs_included)
487     return string_in_vector (lang_name, langs_included);
488   else
489     return 1;
490 }
491 
492 void
include_languages(char * lang_names)493 include_languages (char *lang_names)
494 {
495   if (langs_excluded)
496     error (EXIT_FAILURE, 0, _("can't mix --include and --exclude options"));
497   langs_included = append_strings_to_vector (langs_included, lang_names, white_space);
498 }
499 
500 void
exclude_languages(char * lang_names)501 exclude_languages (char *lang_names)
502 {
503   if (langs_included)
504     error (EXIT_FAILURE, 0, _("can't mix --include and --exclude options"));
505   langs_excluded = append_strings_to_vector (langs_excluded, lang_names, white_space);
506 }
507 
508 static int _GL_ATTRIBUTE_PURE
vector_length(char ** vector)509 vector_length (char **vector)
510 {
511   int length = 0;
512   while (*vector++)
513     length++;
514   return length;
515 }
516 
517 static char **
append_strings_to_vector(char ** vector_0,char * string,char const * delimiter_class)518 append_strings_to_vector (char **vector_0, char *string,
519 			  char const *delimiter_class)
520 {
521   char **vector;
522   if (vector_0)
523     {
524       int length = vector_length (vector_0);
525       vector_0 = xnrealloc (vector_0, length + 2 + strlen (string) / 2,
526 			    sizeof *vector_0);
527       vector = &vector_0[length];
528     }
529   else
530     vector = vector_0 = xnmalloc (2 + strlen (string) / 2, sizeof *vector);
531 
532   do
533     *vector = strsep (&string, delimiter_class);
534   while (*vector++);
535   return xnrealloc (vector_0, vector - vector_0, sizeof *vector_0);
536 }
537 
538 static int _GL_ATTRIBUTE_PURE
string_in_vector(char const * string,char ** vector)539 string_in_vector (char const *string, char **vector)
540 {
541   while (*vector)
542     if (strequ (string, *vector++))
543       return 1;
544   return 0;
545 }
546 
547 
548 /****************************************************************************/
549 /* Convert a file name string to an absolute chain of `file_link's.  */
550 
551 struct file_link *
parse_file_name(char * file_name,struct file_link * relative_dir_link)552 parse_file_name (char *file_name, struct file_link *relative_dir_link)
553 {
554   struct file_link *flink;
555   char **links_0;
556   char **links;
557 
558   if (IS_ABSOLUTE (file_name))
559     {
560 #if HAVE_LINK
561       flink = get_link_from_string (SLASH_STRING, 0);
562 #else
563       flink = 0;
564 #endif
565     }
566   else if (relative_dir_link)
567     flink = relative_dir_link;
568   else if (current_dir_link)
569     flink = current_dir_link;
570   else
571     flink = get_current_dir_link ();
572 
573   links = links_0 = vectorize_string (file_name, SLASH_STRING);
574   while (*links)
575     {
576       char const* link_name = *links++;
577       if (*link_name == '\0' || IS_DOT (link_name))
578 	;
579       else if (IS_DOT_DOT (link_name))
580 	flink = flink->fl_parent;
581       else
582 	{
583 	  struct stat st;
584 	  flink = get_link_from_string (link_name, flink);
585 	  if (!flink->fl_flags)
586 	    {
587 	      flink->fl_flags = classify_link (flink, &st);
588 	      if (!flink->fl_flags)
589 		return 0;
590 	    }
591 	}
592     }
593   free (links_0);
594   return flink;
595 }
596 
597 /* Return an absolute chain of `file_link's representing the current
598    working directory.  */
599 
600 struct file_link *
get_current_dir_link(void)601 get_current_dir_link (void)
602 {
603   struct file_link *dir_link;
604   char *cwd;
605   char **links_0;
606   char **links;
607 
608   if (current_dir_link)
609     return current_dir_link;
610 
611   cwd = getenv ("PWD");
612   cwd = same_as_dot (cwd) ? xstrdup (cwd) : xgetcwd ();
613   if (cwd == NULL)
614     error (EXIT_FAILURE, errno, _("can't get working directory"));
615 #if HAVE_LINK
616   dir_link = get_link_from_string (SLASH_STRING, 0);
617   dir_link->fl_flags = (dir_link->fl_flags & ~FL_TYPE_MASK) | FL_TYPE_DIR;
618 #else
619   dir_link = 0;
620 #endif
621   links = links_0 = vectorize_string (cwd, SLASH_STRING);
622   while (*links)
623     {
624       struct stat st;
625       char const* link_name = *links++;
626       dir_link = get_link_from_string (link_name, dir_link);
627       if (!dir_link->fl_flags)
628 	dir_link->fl_flags = classify_link (dir_link, &st);
629     }
630   chdir_to_link (dir_link);
631   free (links_0);
632   free (cwd);
633   current_dir_link = dir_link;
634   return dir_link;
635 }
636 
637 static int
same_as_dot(char const * cwd)638 same_as_dot (char const *cwd)
639 {
640   struct stat cwd_st;
641   struct stat dot_st;
642 
643   if (cwd == 0 || *cwd != '/'
644       || stat (cwd, &cwd_st) < 0
645       || stat (".", &dot_st) < 0)
646     return 0;
647   return ((cwd_st.st_ino == dot_st.st_ino) && (cwd_st.st_dev == dot_st.st_dev));
648 }
649 
650 /* Change the working directory to the directory represented by
651    `dir_link'.  Choose the shortest path to the destination based on
652    the cached value of the current directory.  */
653 
654 int
chdir_to_link(struct file_link * dir_link)655 chdir_to_link (struct file_link *dir_link)
656 {
657   char *to_dir_name = alloca (PATH_MAX);
658 
659   if (current_dir_link == dir_link)
660     return 1;
661 
662   if (current_dir_link)
663     maybe_relative_file_name (to_dir_name, dir_link, current_dir_link);
664   else
665     absolute_file_name (to_dir_name, dir_link);
666   if (chdir (to_dir_name) < 0)
667     {
668       if (IS_ABSOLUTE (to_dir_name))
669 	error (0, errno, _("can't chdir to `%s'"), to_dir_name);
670       else
671 	{
672 	  char *from_dir_name = alloca (PATH_MAX);
673 	  absolute_file_name (from_dir_name, current_dir_link);
674 	  error (0, errno, _("can't chdir to `%s' from `%s'"), to_dir_name, from_dir_name);
675 	}
676       return 0;
677     }
678   else
679     {
680       current_dir_link = dir_link;
681       return 1;
682     }
683 }
684 
685 static char **
vectorize_string(char * string,char const * delimiter_class)686 vectorize_string (char *string, char const *delimiter_class)
687 {
688   char **vector_0 = xmalloc (sizeof(char *) * (2 + strlen (string) / 2));
689   char **vector = vector_0;
690 
691   *vector++ = strsep (&string, delimiter_class);
692   if (vector[-1])
693     {
694       /* Toss first field if empty */
695       if (vector[-1][0] == '\0')
696 	vector--;
697       do
698 	*vector = strsep (&string, delimiter_class);
699       while (*vector++);
700     }
701   return xnrealloc (vector_0, vector - vector_0, sizeof *vector_0);
702 }
703 
704 void
prune_file_names(char * str,struct file_link * from_link)705 prune_file_names (char *str, struct file_link *from_link)
706 {
707   char **file_names_0 = vectorize_string (str, white_space);
708   char **file_names = file_names_0;
709 
710   while (*file_names)
711     {
712       struct file_link *flink = parse_file_name (*file_names++, from_link);
713       if (flink)
714 	flink->fl_flags |= FL_PRUNE;
715     }
716   free (file_names_0);
717 }
718 
719 
720 /****************************************************************************/
721 /* Gather information about the link at `flink'.  If it's a good
722    file or directory, return its mod-time and type.  */
723 
724 static int
classify_link(struct file_link * flink,struct stat * stp)725 classify_link (struct file_link *flink, struct stat *stp)
726 {
727   unsigned int flags = 0;
728 
729   if (!chdir_to_link (flink->fl_parent))
730     return 0;
731 
732 #ifdef S_IFLNK
733   if (lstat (flink->fl_name, stp) < 0)
734     {
735       error (0, errno, _("can't lstat `%s' from `%s'"), flink->fl_name, xgetcwd ());
736       return 0;
737     }
738   if (S_ISLNK (stp->st_mode))
739     {
740 #endif
741       if (stat (flink->fl_name, stp) < 0)
742 	{
743 	  error (0, errno, _("can't stat `%s' from `%s'"), flink->fl_name, xgetcwd ());
744 	  return 0;
745 	}
746 #ifdef S_IFLNK
747       flags |= FL_SYM_LINK;
748     }
749 #endif
750   if (S_ISDIR (stp->st_mode))
751     flags |= FL_TYPE_DIR;
752   else if (stp->st_size == 0)
753     return 0;
754   else if (S_ISREG (stp->st_mode))
755     flags |= FL_TYPE_FILE;
756   else
757     return 0;
758   return flags;
759 }
760 
761 
762 /****************************************************************************/
763 /* Retrieve an existing flink; or if none exists, create one. */
764 
765 static struct file_link *
get_link_from_dirent(struct dirent * dirent,struct file_link * parent)766 get_link_from_dirent (struct dirent *dirent, struct file_link *parent)
767 {
768   struct file_link **slot;
769   struct file_link *new_link;
770 
771   new_link = make_link_from_dirent (dirent, parent);
772   slot = (struct file_link **) hash_find_slot (&idh.idh_file_link_table, new_link);
773   if (HASH_VACANT (*slot))
774     slot = (struct file_link **) hash_insert_at (&idh.idh_file_link_table,
775 						 new_link, slot);
776   else
777     obstack_free (&idh.idh_file_link_obstack, new_link);
778   return *slot;
779 }
780 
781 static struct file_link *
get_link_from_string(char const * name,struct file_link * parent)782 get_link_from_string (char const *name, struct file_link *parent)
783 {
784   struct file_link **slot;
785   struct file_link *new_link;
786 
787   new_link = make_link_from_string (name, parent);
788   slot = (struct file_link **) hash_find_slot (&idh.idh_file_link_table, new_link);
789   if (HASH_VACANT (*slot))
790     slot = (struct file_link **) hash_insert_at (&idh.idh_file_link_table,
791 						 new_link, slot);
792   else
793     obstack_free (&idh.idh_file_link_obstack, new_link);
794   return *slot;
795 }
796 
797 struct file_link *
make_link_from_dirent(struct dirent * dirent,struct file_link * parent)798 make_link_from_dirent (struct dirent* dirent, struct file_link *parent)
799 {
800   struct file_link *flink;
801 
802   flink = (struct file_link *) obstack_alloc (&idh.idh_file_link_obstack,
803 					      sizeof (struct file_link) + strlen (dirent->d_name));
804   strcpy (flink->fl_name, dirent->d_name);
805   flink->fl_parent = parent ? parent : flink;
806   flink->fl_flags = 0;
807 
808   return flink;
809 }
810 
811 static struct file_link *
make_link_from_string(char const * name,struct file_link * parent)812 make_link_from_string (char const* name, struct file_link *parent)
813 {
814   struct file_link *flink;
815 
816   flink = (struct file_link *) obstack_alloc (&idh.idh_file_link_obstack,
817 					      sizeof (struct file_link) + strlen (name));
818   strcpy (flink->fl_name, name);
819   flink->fl_parent = parent ? parent : flink;
820   flink->fl_flags = 0;
821 
822   return flink;
823 }
824 
825 
826 /****************************************************************************/
827 /* Convert a `file_link' chain to a vector of component `file_link's,
828    with the root at [0].  Return a pointer beyond the final component.  */
829 
830 static struct file_link const **
fill_link_vector(struct file_link const ** vec_buf,struct file_link const * flink)831 fill_link_vector (struct file_link const **vec_buf,
832 		  struct file_link const *flink)
833 {
834   vec_buf = fill_link_vector_1 (vec_buf, flink);
835   *vec_buf = 0;
836   return vec_buf;
837 }
838 
839 static struct file_link const **
fill_link_vector_1(struct file_link const ** vec_buf,struct file_link const * flink)840 fill_link_vector_1 (struct file_link const **vec_buf,
841 		    struct file_link const *flink)
842 {
843   if (!IS_ROOT_FILE_LINK (flink))
844     vec_buf = fill_link_vector_1 (vec_buf, flink->fl_parent);
845   *vec_buf++ = flink;
846   return vec_buf;
847 }
848 
849 
850 /****************************************************************************/
851 /* Fill BUF_0 with a path to TO_LINK.  If a relative path from
852    FROM_LINK is possible (i.e., no intervening symbolic-links) and
853    shorter, return the relative path; otherwise, return an absolute
854    path.  */
855 
856 char *
maybe_relative_file_name(char * buf_0,struct file_link const * to_link,struct file_link const * from_link)857 maybe_relative_file_name (char *buf_0, struct file_link const *to_link, struct file_link const *from_link)
858 {
859   struct file_link const **to_link_vec_0 = alloca (sizeof (struct file_link const *) * (PATH_MAX/2));
860   struct file_link const **from_link_vec_0 = alloca (sizeof (struct file_link const *) * (PATH_MAX/2));
861   struct file_link const **to_link_vec = to_link_vec_0;
862   struct file_link const **from_link_vec = from_link_vec_0;
863   struct file_link const **from_link_end;
864   struct file_link const **from_links;
865   int alloc_me = (buf_0 == 0);
866   char *buf;
867   int levels;
868 
869   if (from_link == 0)
870     from_link = current_dir_link;
871 
872   if (alloc_me)
873     buf_0 = xmalloc (PATH_MAX);
874 
875   /* Optimize common cases.  */
876   if (to_link == from_link)
877     strcpy (buf_0, ".");
878   else if (to_link->fl_parent == from_link)
879     strcpy (buf_0, to_link->fl_name);
880   else if (from_link->fl_flags & FL_SYM_LINK)
881     absolute_file_name (buf_0, to_link);
882   else if (to_link == from_link->fl_parent)
883     strcpy (buf_0, "..");
884   else if (to_link->fl_parent == from_link->fl_parent)
885     {
886       strcpy (buf_0, DOT_DOT_SLASH);
887       strcpy (&buf_0[3], to_link->fl_name);
888     }
889   else
890     {
891       from_link_end = fill_link_vector (from_link_vec, from_link);
892       fill_link_vector (to_link_vec, to_link);
893       while (*to_link_vec == *from_link_vec)
894 	{
895 	  if (*to_link_vec == 0)
896 	    {
897 	      strcpy (buf_0, ".");
898 	      goto out;
899 	    }
900 	  to_link_vec++;
901 	  from_link_vec++;
902 	}
903       levels = from_link_end - from_link_vec;
904       if (levels >= (from_link_vec - from_link_vec_0))
905 	{
906 	  absolute_file_name (buf_0, to_link);
907 	  goto out;
908 	}
909       for (from_links = from_link_vec; *from_links; from_links++)
910 	if ((*from_links)->fl_flags & FL_SYM_LINK)
911 	  {
912 	    absolute_file_name (buf_0, to_link);
913 	    goto out;
914 	  }
915       buf = fill_dot_dots (buf_0, levels);
916       if (*to_link_vec == 0)
917 	*--buf = '\0';
918       else
919 	{
920 	  do
921 	    {
922 	      strcpy (buf, (*to_link_vec)->fl_name);
923 	      buf += strlen (buf);
924 	      if ((*to_link_vec)->fl_name[0] != SLASH_CHAR && *++to_link_vec)
925 		*buf++ = SLASH_CHAR;
926 	    }
927 	  while (*to_link_vec);
928 	}
929     }
930 out:
931   if (alloc_me)
932     buf_0 = xrealloc (buf_0, (1 + strlen (buf_0)));
933   return buf_0;
934 }
935 
936 /* Fill `buf' with sequences of "../" in order to ascend so many
937    `levels' in a directory tree.  */
938 
939 static char *
fill_dot_dots(char * buf,int levels)940 fill_dot_dots (char *buf, int levels)
941 {
942   while (levels--)
943     {
944       strcpy (buf, DOT_DOT_SLASH);
945       buf += 3;
946     }
947   return buf;
948 }
949 
950 
951 /****************************************************************************/
952 /* Fill `buffer' with the absolute path to `flink'.  */
953 
954 char *
absolute_file_name(char * buf_0,struct file_link const * flink)955 absolute_file_name (char *buf_0, struct file_link const *flink)
956 {
957   char *end;
958   int alloc_me = (buf_0 == 0);
959 
960   if (alloc_me)
961     buf_0 = xmalloc (PATH_MAX);
962   end = absolute_file_name_1 (buf_0, flink);
963   /* Clip the trailing slash.  */
964 #if HAVE_LINK
965   if (end > &buf_0[1])
966     end--;
967 #else
968   if (end > &buf_0[3])
969     end--;
970 #endif
971   *end++ = '\0';
972   if (alloc_me)
973     buf_0 = xrealloc (buf_0, (size_t)(end - buf_0));
974   return buf_0;
975 }
976 
977 static char *
absolute_file_name_1(char * buf,struct file_link const * flink)978 absolute_file_name_1 (char *buf, struct file_link const *flink)
979 {
980   char *end;
981   if (IS_ROOT_FILE_LINK (flink))
982     end = buf;
983   else
984     end = absolute_file_name_1 (buf, flink->fl_parent);
985   strcpy (end, flink->fl_name);
986   if (*end == SLASH_CHAR)
987     end++;
988   else
989     {
990       end += strlen (end);
991       *end++ = SLASH_CHAR;
992     }
993   return end;
994 }
995 
996 
997 /****************************************************************************/
998 /* Hash stuff for `struct member_file'.  */
999 
1000 static unsigned long
member_file_hash_1(void const * key)1001 member_file_hash_1 (void const *key)
1002 {
1003   return_ADDRESS_HASH_1 (((struct member_file const *) key)->mf_link);
1004 }
1005 
1006 static unsigned long
member_file_hash_2(void const * key)1007 member_file_hash_2 (void const *key)
1008 {
1009   return_ADDRESS_HASH_2 (((struct member_file const *) key)->mf_link);
1010 }
1011 
1012 static int
member_file_hash_compare(void const * x,void const * y)1013 member_file_hash_compare (void const *x, void const *y)
1014 {
1015   return_ADDRESS_COMPARE (((struct member_file const *) x)->mf_link,
1016 			  ((struct member_file const *) y)->mf_link);
1017 }
1018 
1019 /* Collating sequence:
1020    - language.map index
1021    - mf_link: breadth-first, alphabetical */
1022 
1023 int
member_file_qsort_compare(void const * x,void const * y)1024 member_file_qsort_compare (void const *x, void const *y)
1025 {
1026   struct member_file const *mfx = *(struct member_file const *const *) x;
1027   struct member_file const *mfy = *(struct member_file const *const *) y;
1028   int result;
1029 
1030   INTEGER_COMPARE (mfx->mf_lang_args->la_index, mfy->mf_lang_args->la_index, result);
1031   if (result)
1032     return result;
1033   else
1034     {
1035       struct file_link const *flx = mfx->mf_link;
1036       struct file_link const *fly = mfy->mf_link;
1037       if (flx->fl_parent == fly->fl_parent)
1038 	return strcmp (flx->fl_name, fly->fl_name);
1039       result = (links_depth (flx) - links_depth (fly));
1040       if (result)
1041 	return result;
1042       while (flx->fl_parent != fly->fl_parent)
1043 	{
1044 	  flx = flx->fl_parent;
1045 	  fly = fly->fl_parent;
1046 	}
1047       return strcmp (flx->fl_name, fly->fl_name);
1048     }
1049 }
1050 
1051 /****************************************************************************/
1052 /* Hash stuff for `struct file_link'.  */
1053 
1054 static unsigned long _GL_ATTRIBUTE_PURE
file_link_hash_1(void const * key)1055 file_link_hash_1 (void const *key)
1056 {
1057   unsigned long result = 0;
1058   struct file_link const *parent = (IS_ROOT_FILE_LINK (((struct file_link const *) key))
1059 				    ? 0 : ((struct file_link const *) key)->fl_parent);
1060   STRING_HASH_1 (((struct file_link const *) key)->fl_name, result);
1061   ADDRESS_HASH_1 (parent, result);
1062   return result;
1063 }
1064 
1065 static unsigned long _GL_ATTRIBUTE_PURE
file_link_hash_2(void const * key)1066 file_link_hash_2 (void const *key)
1067 {
1068   unsigned long result = 0;
1069   struct file_link const *parent = (IS_ROOT_FILE_LINK (((struct file_link const *) key))
1070 				    ? 0 : ((struct file_link const *) key)->fl_parent);
1071   STRING_HASH_2 (((struct file_link const *) key)->fl_name, result);
1072   ADDRESS_HASH_2 (parent, result);
1073   return result;
1074 }
1075 
1076 static int _GL_ATTRIBUTE_PURE
file_link_hash_compare(void const * x,void const * y)1077 file_link_hash_compare (void const *x, void const *y)
1078 {
1079   int result;
1080   struct file_link const *x_parent = (IS_ROOT_FILE_LINK (((struct file_link const *) x))
1081 				      ? 0 : ((struct file_link const *) x)->fl_parent);
1082   struct file_link const *y_parent = (IS_ROOT_FILE_LINK (((struct file_link const *) y))
1083 				      ? 0 : ((struct file_link const *) y)->fl_parent);
1084   ADDRESS_COMPARE (x_parent, y_parent, result);
1085   if (result)
1086     return result;
1087   STRING_COMPARE (((struct file_link const *) x)->fl_name,
1088 		  ((struct file_link const *) y)->fl_name, result);
1089   return result;
1090 }
1091 
1092 /* Count directory components between flink and its root.  */
1093 
1094 int
links_depth(struct file_link const * flink)1095 links_depth (struct file_link const *flink)
1096 {
1097   int depth = 0;
1098   while (!IS_ROOT_FILE_LINK (flink))
1099     {
1100       depth++;
1101       flink = flink->fl_parent;
1102     }
1103   return depth;
1104 }
1105 
1106 #if HAVE_LINK
1107 
1108 /****************************************************************************/
1109 /* Hash stuff for `struct dev_ino'.  */
1110 
1111 static unsigned long
dev_ino_hash_1(void const * key)1112 dev_ino_hash_1 (void const *key)
1113 {
1114   unsigned long result = 0;
1115   INTEGER_HASH_1 (((struct dev_ino const *) key)->di_dev, result);
1116   INTEGER_HASH_1 (((struct dev_ino const *) key)->di_ino, result);
1117   return result;
1118 }
1119 
1120 static unsigned long
dev_ino_hash_2(void const * key)1121 dev_ino_hash_2 (void const *key)
1122 {
1123   unsigned long result = 0;
1124   INTEGER_HASH_2 (((struct dev_ino const *) key)->di_dev, result);
1125   INTEGER_HASH_2 (((struct dev_ino const *) key)->di_ino, result);
1126   return result;
1127 }
1128 
1129 static int
dev_ino_hash_compare(void const * x,void const * y)1130 dev_ino_hash_compare (void const *x, void const *y)
1131 {
1132   int result;
1133   INTEGER_COMPARE (((struct dev_ino const *) x)->di_ino,
1134 		   ((struct dev_ino const *) y)->di_ino, result);
1135   if (result)
1136     return result;
1137   INTEGER_COMPARE (((struct dev_ino const *) x)->di_dev,
1138 		   ((struct dev_ino const *) y)->di_dev, result);
1139   return result;
1140 }
1141 
1142 #endif
1143 
1144 /*******************************************************************/
1145 
1146 struct file_link *
init_walker(struct idhead * idhp)1147 init_walker (struct idhead *idhp)
1148 {
1149   init_idh_obstacks (idhp);
1150   init_idh_tables (idhp);
1151   return get_current_dir_link ();
1152 }
1153 
1154 void
init_idh_obstacks(struct idhead * idhp)1155 init_idh_obstacks (struct idhead *idhp)
1156 {
1157   obstack_init (&idhp->idh_member_file_obstack);
1158   obstack_init (&idhp->idh_file_link_obstack);
1159 #if HAVE_LINK
1160   obstack_init (&idhp->idh_dev_ino_obstack);
1161 #endif
1162 }
1163 
1164 void
init_idh_tables(struct idhead * idhp)1165 init_idh_tables (struct idhead *idhp)
1166 {
1167   hash_init (&idhp->idh_member_file_table, 16*1024,
1168 	     member_file_hash_1, member_file_hash_2, member_file_hash_compare);
1169   hash_init (&idhp->idh_file_link_table, 16*1024,
1170 	     file_link_hash_1, file_link_hash_2, file_link_hash_compare);
1171 #if HAVE_LINK
1172   hash_init (&idhp->idh_dev_ino_table, 16*1024,
1173 	     dev_ino_hash_1, dev_ino_hash_2, dev_ino_hash_compare);
1174 #endif
1175 }
1176