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