source: trunk/athena/bin/delete/pattern.c @ 23667

Revision 23667, 26.3 KB checked in by broder, 15 years ago (diff)
In delete: * Apply patches from jik: - When adding to an array, leave some extra space at the end so that we don't have to keep growing it every time.
Line 
1/*
2 * $Id: pattern.c,v 1.28 1999-01-22 23:09:03 ghudson Exp $
3 *
4 * This program is part of a package including delete, undelete,
5 * lsdel, expunge and purge.  The software suite is meant as a
6 * replacement for rm which allows for file recovery.
7 *
8 * Copyright (c) 1989 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file "mit-copying.h."
10 */
11
12#if (!defined(lint) && !defined(SABER))
13     static char rcsid_pattern_c[] = "$Id: pattern.c,v 1.28 1999-01-22 23:09:03 ghudson Exp $";
14#endif
15
16#include <stdio.h>
17#include <sys/types.h>
18#include <dirent.h>
19#include <sys/param.h>
20#include <string.h>
21#include <errno.h>
22#include <com_err.h>
23#include "pattern.h"
24#include "util.h"
25#include "directories.h"
26#include "undelete.h"
27#include "shell_regexp.h"
28#include "mit-copying.h"
29#include "delete_errs.h"
30#include "errors.h"
31#include "stack.h"
32
33extern char *whoami;
34
35void free_list();
36
37
38/*
39 * add_arrays() takes pointers to two arrays of char **'s and their
40 * lengths, merges the two into the first by realloc'ing the first and
41 * then free's the second's memory usage.
42 */ 
43int add_arrays(array1, num1, size1, array2, num2)
44char ***array1, ***array2;
45int *num1, *size1, *num2;
46{
47     int counter;
48
49     if (! *size1) {
50       if (*array1)
51         free(*array1);
52       *array1 = *array2;
53       *num1 = *num2;
54       *size1 = *num2;
55       return 0;
56     }
57
58     while (*size1 < (*num1 + *num2)) {
59       *size1 *= 2;
60     }
61
62     *array1 = (char **) realloc((char *) *array1, (unsigned)
63                                 (sizeof(char *) * *size1));
64     if (! *array1) {
65          set_error(errno);
66          error("realloc");
67          return error_code;
68     }
69     for (counter = *num1; counter < *num1 + *num2; counter++)
70          *(*array1 + counter) = *(*array2 + counter - *num1);
71     free ((char *) *array2);
72     *num1 += *num2;
73     return 0;
74}
75
76
77
78
79
80
81/*
82 * Add a string to a list of strings.
83 */
84int add_str(strs, num, size, str)
85char ***strs;
86int num, *size;
87char *str;
88{
89     char **ary;
90
91     if (! *size) {
92       num = 0;
93       *size = 1;
94       ary = *strs = (char **) malloc((unsigned) (sizeof(char *)));
95     }
96     else if (num == *size) {
97       *size *= 2;
98       ary = *strs = (char **) realloc((char *) *strs, (unsigned)
99                                       (sizeof(char *) * *size));
100     }
101     else {
102       ary = *strs;
103     }
104
105     if (! *strs) {
106          set_error(errno);
107          error("realloc");
108          return error_code;
109     }
110     ary[num] = Malloc((unsigned) (strlen(str) + 1));
111     if (! ary[num]) {
112          set_error(errno);
113          error("Malloc");
114          return error_code;
115     }
116     (void) strcpy(ary[num], str);
117     return 0;
118}
119
120
121
122
123
124/*
125 * Find_matches will behave unpredictably if you try to use it to find
126 * very strange combinations of file types, for example only searching
127 * for undeleted files in the top-level directory, while searching
128 * recursively for deleted files.  Basically, there are some conflicts
129 * between options that I don't list here because I don't think I'll
130 * ever need to use those combinations.
131 */
132/*
133 * Function: find_matches(char *name, int *num_found, char ***found,
134 *                        int options)
135 *
136 * Requires: name points to a NULL-terminated string, representing a
137 *   filename pattern with regular filename characters, path
138 *   separators and shell wildcard characters []*?; num_found points
139 *   to a valid int memory storage location; found points to a valid
140 *   char ** memory storage location.
141 *
142 * Effects: Returns a list of all the files in the file hierarchy that
143 *   match the options specified in options and that match name.
144 *   Options are:
145 *
146 *   FIND_UNDELETED search for and return undeleted files
147 *
148 *   FIND_DELETED search for and return deleted files
149 *
150 *   FIND_CONTENTS means that if matches are directories (or links to
151 *     directories), the contents of the directory should be matched
152 *     in addition to the directory itself
153 *
154 *   RECURS_FIND_DELETED to search all undeleted subdirectories
155 *     recursively of matched directories looking for deleted files
156 *
157 *   RECURS_FIND_UNDELETED to search all undeleted subdirectories
158 *     recursively of matched directories looking for undeleted files
159 *
160 *   RECURS_DELETED to recursively return all contents of deleted
161 *     directories in addition to the directories themselves
162 *   
163 *   FOLLW_LINKS to pursue symlinks to directories and continue down
164 *     the referenced directories when searching recursively (if the
165 *     initial string is an undeleted symlink it is always traversed;
166 *     deleted symlinks are never traversed)
167 *   
168 *   FOLLW_MOUNTPOINTS to traverse mount points when searching
169 *     recursively (if the initial string is a mountpoint it is always
170 *     traversed)
171 *
172 *   FIND_DOTFILES forces the system to recognize dot files instead of
173 *     discarding them when looking for files
174 *
175 *   If the first character of name is '/', the search is conducted
176 *   absolutely from the root of the hierarchy; else, it is conducted
177 *   relative to the current working directory.  The number of
178 *   matching files is returned in *num_found, and a list of file
179 *   names is returned in *found.  If there are no errors, the return
180 *   value is 0; else the return value represents the error code of
181 *   the error which occurred.  No matter how many file names are
182 *   returned, the memory location addressed in *found is a valid
183 *   pointer according to Malloc() and can be released using free()
184 *   safely.  However, if an error value is returned, the caller
185 *   should not attempt to use the values stored in *num_found or
186 *   *found.
187 *
188 * Modifies: *num_found, *found.
189 */
190int find_matches(name, num_found, found, options)
191char *name;
192int *num_found;
193char ***found;
194int options;
195{
196     char       **matched_files, **return_files, **recurs_files;
197     int        num_matched_files = 0, num_return_files = 0,
198                num_recurs_files = 0, return_files_size = 0;
199     int        retval;
200     int        i;
201#ifdef DEBUG
202     int        j;
203#endif
204     int        match_options = 0;
205
206#ifdef DEBUG
207     fprintf(stderr, "Entering find_matches, name = %s, options = %d.\n",
208             name, options);
209#endif
210     
211     match_options = options & (FIND_DELETED | FIND_UNDELETED);
212     if (options & (RECURS_FIND_DELETED | RECURS_FIND_UNDELETED |
213                    FIND_CONTENTS))
214          match_options |= FIND_UNDELETED;
215     
216     if (! match_options) {
217          set_error(PAT_NO_FILES_REQUESTED);
218          error("find_matches");
219          return error_code;
220     }
221     
222     retval = do_match(name, &num_matched_files, &matched_files,
223                       match_options & FIND_UNDELETED,
224                       match_options & FIND_DELETED);
225     if (retval) {
226          error(name);
227          return retval;
228     }
229     if (num_matched_files == 0) {
230          *num_found = num_matched_files;
231          *found = matched_files;
232#ifdef DEBUG
233          fprintf(stderr, "No matches found, returning.\n");
234#endif
235          return 0;
236     }
237
238#ifdef DEBUG
239     fprintf(stderr, "The following matches were found:\n");
240     for (i = 0; i < num_matched_files; i++)
241          fprintf(stderr, "  %s\n", matched_files[i]);
242#endif
243     
244     if (options & RECURS) {
245          return_files = (char **) Malloc(0);
246          num_return_files = 0;
247
248          for (i = 0; i < num_matched_files; i++) {
249
250               retval = do_recurs(matched_files[i], &num_recurs_files,
251                                  &recurs_files, options);
252               if (retval) {
253                    error(matched_files[i]);
254                    return retval;
255               }
256
257               if (num_recurs_files) {
258                    retval = add_arrays(&return_files, &num_return_files,
259                                        &return_files_size, &recurs_files,
260                                        &num_recurs_files);
261                    if (retval) {
262                         error("add_arrays");
263                         return retval;
264                    }
265#ifdef DEBUG
266                    fprintf(stderr,
267                            "Just added the following to return_files:\n");
268                    for (j = num_return_files - num_recurs_files;
269                         j < num_return_files; j++)
270                         fprintf(stderr, "  %s\n", return_files[j]);
271#endif
272               }
273               
274               if (is_deleted(lastpart(matched_files[i]))) {
275                    if (options & FIND_DELETED) {
276                         retval = add_str(&return_files, num_return_files,
277                                          &return_files_size,
278                                          matched_files[i]);
279                         if (retval) {
280                              error("add_str");
281                              return retval;
282                         }
283                         num_return_files++;
284#ifdef DEBUG
285                         fprintf(stderr, "Just added %s to return_files.\n",
286                                 return_files[num_return_files-1]);
287#endif
288                    }
289               }
290               else if (options & FIND_UNDELETED) {
291                    retval = add_str(&return_files, num_return_files,
292                                     &return_files_size, matched_files[i]);
293                    if (retval) {
294                         error("add_str");
295                         return retval;
296                    }
297                    num_return_files++;
298#ifdef DEBUG
299                    fprintf(stderr, "Just added %s to return_files.\n",
300                            return_files[num_return_files-1]);
301#endif
302               }
303          }
304          free_list(matched_files, num_matched_files);
305          *num_found = num_return_files;
306          *found = return_files;
307     }
308     else {
309          *num_found = num_matched_files;
310          *found = matched_files;
311     }
312
313     return 0;
314}
315
316
317
318
319         
320         
321                   
322               
323#define string_push(str)\
324          strsize = strlen(str);\
325          retval = push(str, strsize);\
326          if (! retval)\
327               retval |= push(&strsize, sizeof(int));\
328          if (retval) {\
329               error("push");\
330               (void) popall();\
331               return retval;\
332          }
333#define string_pop(str)\
334          retval = pop(&strsize, sizeof(int));\
335          if (! retval)\
336               retval = pop(str, strsize);\
337          if (! retval)\
338               str[strsize] = '\0'
339         
340         
341
342
343
344
345/*
346 * Function: do_match(char *name, int *num_found, char ***found,
347 *                    Boolean match_undeleted, Boolean match_deleted)
348 *
349 * Requires: name points to a NULL-terminated string, representing a
350 *   filename pattern with regular filename characters, path
351 *   separators and shell wildcard characters []*?; num_found points
352 *   to a valid int memory storage location; found points to a valid
353 *   char ** memory storage location.
354 *
355 * Effects: Returns a list of all the files in the file hierarchy that
356 *   match name.  If match_undeleted is true, will return undeleted
357 *   files that match; if match_deleted is true, will return
358 *   deleted_files that match.  If the first character of name is '/',
359 *   the search is conducted absolutely from the root of the
360 *   hierarchy; else, it is conducted relative to the current working
361 *   directory.  The number of matching files is returned in
362 *   *num_found, and a list of file names is returned in *found.  If
363 *   there are no errors, the return value is 0; else the return value
364 *   represents the error code of the error which occurred.  No matter
365 *   how many file names are returned, the memory location addressed
366 *   in *found is a valid pointer according to Malloc() and can be
367 *   released using free() safely.  However, if an error value is
368 *   returned, the caller should not attempt to use the values stored
369 *   in *num_found or *found.
370 *
371 * Modifies: *num_found, *found.
372 *
373 * Algorithm:
374 *
375 * start:
376 *   base = "" or "/",
377 *   name = name or name + 1
378 *   initialze found and num_found
379 *   dirp = Opendir(base)
380 *   first = firstpart(name, rest) (assigns rest as side-effect)
381 *   if (! *first) {
382 *     add string to list if appropriate
383 *     return
384 *
385 * loop:
386 *   dp = readdir(dirp)
387 *   if (! dp) goto updir
388 *   compare dp->d_name to first -- match?
389 *     yes - goto downdir
390 *     no - are we looking for deleted and is dp->d_name deleted?
391 *       yes - compare undeleted dp->d_name to first -- match?
392 *         yes - goto downdir
393 *         no - goto loop
394 *       no - goto loop
395 *
396 * downdir:
397 *   save dirp, rest, first and base on stack
398 *   first = firstpart(rest, rest)
399 *   base = dp->d_name appended to base
400 *   is first an empty string?
401 *      yes - put back dirp, rest, first, base
402 *            goto loop
403 *   try to open dir base - opens?
404 *      yes - goto loop
405 *      no - is the error ENOTDIR?
406 *             yes - don't worry about it
407 *             no - report the error
408 *           restore dirp, rest, first, base from stack
409 *           goto loop
410 *
411 * updir:
412 *   close dirp
413 *   restore base, rest, first from stack
414 *   STACK_EMPTY?
415 *     yes - return from procedure with results
416 *   restore dirp from stack
417 *   goto loop
418 */
419int do_match(name, num_found, found, match_undeleted, match_deleted)
420char *name;
421int *num_found;
422char ***found;
423Boolean match_undeleted, match_deleted;
424{
425     char base[MAXPATHLEN];
426     struct dirent *dp;
427     DIR *dirp;
428     char first[MAXNAMLEN], rest[MAXPATHLEN];
429     int retval;
430     int strsize;
431     struct stat statbuf;
432#ifdef PATTERN_DEBUG
433     int j;
434#endif
435     int found_size = 0;
436     
437#ifdef DEBUG
438     printf("do_match: looking for %s\n", name);
439#endif
440
441     /* start: */
442     
443     if (*name == '/') {
444          *base = '/';
445          *(base + 1) = '\0';
446          name++;
447     }
448     else
449          *base = '\0';
450
451     *found = (char **) Malloc(0);
452     *num_found = 0;
453     
454     dirp = Opendir(base);
455     if (! dirp) {
456          set_error(errno);
457          error(base);
458#ifdef PATTERN_DEBUG
459          fprintf(stderr, "do_match: return 2.\n");
460#endif
461          return error_code;
462     }
463     (void) strcpy(first, firstpart(name, rest));
464     if ((! *first) && (match_undeleted)) {
465          retval = add_str(found, *num_found, &found_size, base);
466          if (retval) {
467               error("add_str");
468               (void) popall();
469#ifdef PATTERN_DEBUG
470               fprintf(stderr, "do_match: return 3.\n");
471#endif
472               return retval;
473          }
474          (*num_found)++;
475#ifdef PATTERN_DEBUG
476          fprintf(stderr, "do_match: return 4.\n");
477#endif
478          return 0;
479     }
480     
481     while (1) {
482          dp = readdir(dirp);
483          if (! dp) goto updir;
484
485          retval = reg_cmp(first, dp->d_name);
486#ifdef PATTERN_DEBUG
487        fprintf(stderr, "do_match: comparing %s to %s returns %d.\n",
488                first, dp->d_name, retval);
489#endif
490          if (retval < 0) {
491               error("reg_cmp");
492               goto updir;
493          }
494
495          if (retval == REGEXP_MATCH) goto downdir;
496
497          if (is_deleted(dp->d_name) && match_deleted) {
498               retval = reg_cmp(first, &dp->d_name[2]);
499#ifdef PATTERN_DEBUG
500               fprintf(stderr,
501                       "do_match: deleted compare of %s to %s returns %d.\n",
502                       first, &dp->d_name[2], retval);
503#endif
504               if (retval < 0) {
505                    error("reg_cmp");
506                    goto updir;
507               }
508
509               if (retval == REGEXP_MATCH)
510                    goto downdir;
511               else
512                    continue;
513          }
514          else
515               continue;
516
517     downdir:
518#ifdef PATTERN_DEBUG
519          fprintf(stderr, "do_match: downdir\n");
520#endif
521          retval = push(&dirp, sizeof(DIR *));
522          if (retval) {
523               error("push");
524               (void) popall();
525#ifdef PATTERN_DEBUG
526               fprintf(stderr, "do_match: return 5.\n");
527#endif
528               return retval;
529          }
530#ifdef PATTERN_DEBUG
531          fprintf(stderr, "do_match: pushing %s, %s, %s\n", first, rest, base);
532#endif
533          string_push(first);
534          string_push(rest);
535          string_push(base);
536          (void) strcpy(base, append(base, dp->d_name));
537          (void) strcpy(first, firstpart(rest, rest));
538          if (! *first) {
539               if (is_deleted(lastpart(base))) {
540                    if (match_deleted) {
541                         retval = add_str(found, *num_found, &found_size,
542                                          base);
543                         if (retval) {
544                              error("add_str");
545                              (void) popall();
546#ifdef PATTERN_DEBUG
547                              fprintf(stderr, "do_match: return 6.\n");
548#endif
549                              return retval;
550                         }
551                         (*num_found)++;
552                    }
553               }
554               else if (match_undeleted) {
555                    retval = add_str(found, *num_found, &found_size, base);
556                    if (retval) {
557                         error("add_str");
558                         (void) popall();
559#ifdef PATTERN_DEBUG
560                         fprintf(stderr, "do_match: return 7.\n");
561#endif
562                         return retval;
563                    }
564                    (*num_found)++;
565               }
566               string_pop(base);
567               string_pop(rest);
568               string_pop(first);
569#ifdef PATTERN_DEBUG
570               fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
571                       rest, base);
572#endif
573               (void) pop(&dirp, sizeof(DIR *));
574               continue;
575          }
576
577          /*
578           * The logic here in this attempt to descend is as follows:
579           *
580           * Try to stat base.  Succeeds?
581           * Yes:
582           *   Is it a directory?
583           *   Yes:
584           *     Try to open it.
585           *     Does the open succeed?
586           *     Yes:
587           *       Continue the loop.
588           *     No:
589           *       Print an error, and pop up to the last directory.
590           *   No:
591           *     Pop up to the last directory.
592           * No:
593           *   Try to lstat base.  Succeeds?
594           *   Yes:
595           *     Is it a directory?
596           *     Yes: see above.  *** this should never happen ***
597           *     No:
598           *       Pop up to the last directory.
599           *   No:
600           *     Print an error, and pop up to the last directory.
601           *
602           * The reason for the lstat is that we don't want to print
603           * errors when we can't descend because we're trying to go
604           * into a symlink pointing nowhere; a symlink pointing
605           * nowhere is not an error when matching, it just means that
606           * we can't descend.
607           */
608          dirp = NULL;
609          if (((! (retval = stat(base, &statbuf))) ||
610               (! (retval = lstat(base, &statbuf)))) &&
611              ((statbuf.st_mode & S_IFMT) == S_IFDIR))
612               dirp = Opendir(base);
613          if (! dirp) {
614               if (retval || ((statbuf.st_mode & S_IFMT) == S_IFDIR)) {
615                    set_error(errno);
616                    error(base);
617               }
618               string_pop(base);
619               string_pop(rest);
620               string_pop(first);
621#ifdef PATTERN_DEBUG
622               fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
623                       rest, base);
624#endif
625               (void) pop(&dirp, sizeof(DIR *));
626               continue;
627          }
628          else
629               continue;
630
631     updir:
632#ifdef PATTERN_DEBUG
633          fprintf(stderr, "do_match: updir\n");
634#endif
635          closedir(dirp);
636          string_pop(base);
637#ifdef PATTERN_DEBUG
638          fprintf(stderr, "do_match: popped %s\n", base);
639#endif
640          if (retval) {
641               if (retval != STACK_EMPTY) {
642                    error("pop");
643                    (void) popall();
644#ifdef PATTERN_DEBUG
645                    fprintf(stderr, "do_match: return 8.\n");
646#endif
647                    return retval;
648               }
649#ifdef PATTERN_DEBUG
650               fprintf(stderr, "Returning %d word%s from do_match:\n",
651                       *num_found,
652                       *num_found == 1 ? "" : "s");
653               for (j = 0; j < *num_found; j++)
654                    fprintf(stderr, "\t%s\n", (*found)[j]);
655               fprintf(stderr, "do_match: return 9.\n");
656#endif
657               return 0;
658          }
659          string_pop(rest);
660          string_pop(first);
661#ifdef PATTERN_DEBUG
662          fprintf(stderr, "do_match: popped %s, %s\n", rest, first);
663#endif
664          retval = pop(&dirp, sizeof(DIR *));
665          if (retval) {
666               error("pop");
667               (void) popall();
668#ifdef PATTERN_DEBUG
669               fprintf(stderr, "do_match: return 10.\n");
670#endif
671               return retval;
672          }
673          continue;
674     }
675}
676
677
678
679
680
681
682/*
683 * Function: do_recurs(char *name, int *num_found, char ***found,
684 *                     int options)
685 *
686 * Requires: name points to a NULL-terminated string, representing a
687 *   filename; points to a valid int memory storage location; found
688 *   points to a valid char ** memory storage location.
689 *
690 * Effects: Returns a list of all the files in the file hierarchy that
691 *   are underneath the specified file, governed by the options set in
692 *   options.  Options are as described in the find_matches() description.
693 *   RECURS_FIND_DELETED and RECURS_DELETED imply FIND_DELETED.
694 *   RECURS_FIND_UNDELETED implies FIND_UNDELETED.
695 *
696 * Modifies: *num_found, *found.
697 *
698 * Algorithm:
699 *
700 * start:
701 *   initialze found and num_found
702 *   strcopy(base, name)
703 *   check if we just opened a deleted symlink and return if we did
704 *   dirp = Opendir(base)
705 *   check RECURS options and set FIND options as appropriate
706 *
707 * loop:
708 *   dp = readdir(dirp)
709 *   if (! dp) goto updir
710 *   is dp deleted?
711 *     yes - is FIND_DELETED set?
712 *             yes - add to list
713 *                   is RECURS_DELETED set?
714 *                     yes - goto downdir
715 *                     no - goto loop
716 *             no - goto loop
717 *     no - is FIND_UNDELETED set?
718 *            yes - is the file a dotfile?
719 *                    yes - is FIND_DOTFILES set?
720 *                            yes - add to list
721 *                          goto loop
722 *                    no - add to list
723 *                  are RECURS_FIND_DELETED and FIND_DELETED set?
724 *                    yes - goto downdir
725 *                  is RECURS_FIND_UNDELETED set?
726 *                    yes - goto downdir
727 *                    no - goto loop
728 *            no - goto loop
729 *             
730 * downdir:
731 *   save dirp, base on stack
732 *   base = dp->d_name appended to base
733 *   try to open base -- opens?
734 *     yes - is FOLLW_LINKS set?
735 *             yes - is it deleted?
736 *                   yes - is it a link?
737 *                         yes - close the directory
738 *                               restore base and dirp
739 *                               goto loop
740 *             no - is it a link?
741 *                     yes - close the directory
742 *                           restore base and dirp
743 *                           goto loop
744 *           is FOLLW_MOUNTPOINTS set?
745 *             no - is it a mountpoint?
746 *                     yes - close the directory
747 *                           restore base and dirp
748 *                           goto loop
749 *     no - is the error ENOTDIR?
750 *            yes - don't worry about it
751 *            no - report the error
752 *          restore base and dirp
753 *          goto loop
754 *
755 * updir:
756 *   close dirp
757 *   restore base from stack
758 *   STACK_EMPTY?
759 *     yes - return from procedure with results
760 *   restore dirp from stack
761 *   goto loop
762 */
763int do_recurs(name, num_found, found, options)
764char *name;
765int *num_found;
766char ***found;
767int options;
768{
769     char base[MAXPATHLEN];
770     struct dirent *dp;
771     DIR *dirp;
772     int retval;
773     int strsize;
774     struct stat statbuf;
775     int use_stat;
776     int found_size = 0;
777
778#ifdef DEBUG
779     fprintf(stderr, "do_recurs: opening %s\n", name);
780#endif
781
782     /* start: */
783     
784     *found = (char **) Malloc(0);
785     *num_found = 0;
786     strcpy(base, name);
787
788     if (lstat(base, &statbuf)) {
789          set_error(errno);
790          error(base);
791          return error_code;
792     }
793         
794#ifdef S_IFLNK
795     if (is_link(base, &statbuf)) {
796          /* Never follow deleted symlinks */
797          if (is_deleted(lastpart(base))) {
798               return 0;
799          }
800          if (stat(base, &statbuf)) {
801               if (errno == ENOENT) {
802                    extern int readlink();
803                    char pathbuf[MAXPATHLEN];
804                    int cc;
805                   
806                    /* What the link is pointing to does not exist; */
807                    /* this is a warning, not an error.             */
808                    set_warning(errno);
809                    cc = readlink(base, pathbuf, MAXPATHLEN);
810                    if (cc > 0) {
811                         char error_buf[2*MAXPATHLEN+20];
812
813                         pathbuf[(cc == MAXPATHLEN) ? (cc - 1) : cc] = '\0';
814                         sprintf(error_buf, "%s (pointed to by %s)", pathbuf,
815                                 base);
816                         error(error_buf);
817                    }
818                    else {
819                         error(base);
820                    }
821
822                    return 0;
823               }
824               else {
825                    set_error(errno);
826                    error(base);
827                    return error_code;
828               }
829          }
830     }
831#endif
832
833     if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
834          return 0;
835     
836     dirp = Opendir(base);
837     if (! dirp) {
838#ifdef DEBUG
839          fprintf(stderr, "Couldn't open %s.\n", base);
840#endif
841          set_error(errno);
842          error(base);
843          return error_code;
844     }
845
846     if (options & (RECURS_FIND_DELETED | RECURS_DELETED))
847          options |= FIND_DELETED;
848     if (options & RECURS_FIND_UNDELETED)
849          options |= FIND_UNDELETED;
850     
851     while (1) {
852          dp = readdir(dirp);
853          if (! dp) goto updir;
854
855          if (is_deleted(dp->d_name)) {
856               if (options & FIND_DELETED) {
857                    retval = add_str(found, *num_found, &found_size,
858                                     append(base, dp->d_name));
859                    if (retval) {
860                         error("add_str");
861                         (void) popall();
862                         return retval;
863                    }
864                    (*num_found)++;
865                    if (options & RECURS_DELETED)
866                         goto downdir;
867                    else
868                         continue;
869               }
870               else
871                    continue;
872          }
873
874          if (options & FIND_UNDELETED) {
875               if (is_dotfile(dp->d_name)) {
876                    if (options & FIND_DOTFILES) {
877                         retval = add_str(found, *num_found, &found_size,
878                                          append(base, dp->d_name));
879                         if (retval) {
880                              error("add_str");
881                              (void) popall();
882                              return retval;
883                         }
884                    }
885                    continue;
886               }
887               else {
888                    retval = add_str(found, *num_found, &found_size,
889                                     append(base, dp->d_name));
890                    if (retval) {
891                         error("add_str");
892                         (void) popall();
893                         return retval;
894                    }
895                    (*num_found)++;
896               }
897          }
898
899          if (! is_dotfile(dp->d_name)) {
900               if (options & RECURS_FIND_DELETED)
901                    goto downdir;
902               if (options & RECURS_FIND_UNDELETED)
903                    goto downdir;
904          }
905         
906          continue;
907         
908               
909     downdir:
910          retval = push(&dirp, sizeof(DIR *));
911          if (retval) {
912               error("push");
913               (void) popall();
914               return retval;
915          }
916          string_push(base);
917          (void) strcpy(base, append(base, dp->d_name));
918
919          /*
920           * Originally, I did an Opendir() right at the start and
921           * then only checked things if the Opendir resulted in an
922           * error.  However, this is inefficient, because the
923           * Opendir() procedure works by first calling open() on the
924           * file, and *then* calling fstat on the file descriptor
925           * that is returned.  since most of the time we will be
926           * trying to open things that are not directory, it is much
927           * more effecient to do the stat first here and to do the
928           * Opendir only if the stat results are satisfactory.
929           */
930          use_stat = (options & FOLLW_LINKS) && (! is_deleted(lastpart(base)));
931          if (use_stat)
932               retval = stat(base, &statbuf);
933          else
934               retval = lstat(base, &statbuf);
935          if (retval == -1) {
936               set_error(errno);
937               error(base);
938               string_pop(base);
939               (void) pop(&dirp, sizeof(DIR *));
940               continue;
941          }
942          /* It's not a directory, so punt it and continue. */
943          if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
944               string_pop(base);
945               (void) pop(&dirp, sizeof(DIR *));
946               continue;
947          }
948
949          /* Actually try to open it. */
950          dirp = Opendir(base);
951          if (! dirp) {
952               set_error(errno);
953               error(base);
954               string_pop(base);
955               (void) pop(&dirp, sizeof(DIR *));
956               continue;
957          }
958         
959          if (! (options & FOLLW_MOUNTPOINTS)) {
960               if (is_mountpoint(base, use_stat ? (struct stat *) NULL :
961                                 &statbuf)) {
962                    closedir(dirp);
963                    set_warning(PAT_IS_MOUNT);
964                    error(base);
965                    string_pop(base);
966                    (void) pop(&dirp, sizeof(DIR *));
967                    continue;
968               }
969#ifdef DEBUG
970               else {
971                    fprintf(stderr,
972                            "do_recurs: %s isn't a mountpoint, following.\n",
973                            base);
974               }
975#endif
976          }
977#ifdef DEBUG
978          printf("do_recurs: opening %s\n", base);
979#endif
980          continue;
981         
982     updir:
983          closedir(dirp);
984          string_pop(base);
985          if (retval) {
986               if (retval != STACK_EMPTY) {
987                    error("pop");
988                    (void) popall();
989                    return retval;
990               }
991               return 0;
992          }
993          retval = pop(&dirp, sizeof(DIR *));
994          if (retval) {
995               error("pop");
996               (void) popall();
997               return retval;
998          }
999          continue;
1000     }
1001}
1002
1003
1004void free_list(list, num)
1005char **list;
1006int num;
1007{
1008     int i;
1009
1010     for (i = 0; i < num; i++)
1011          free(list[i]);
1012
1013     free((char *) list);
1014}
1015
1016
1017
1018
1019
1020
1021/*
1022 * returns true if the filename has no globbing wildcards in it.  That
1023 * means no non-quoted question marks, asterisks, or open square
1024 * braces.  Assumes a null-terminated string, and a valid globbing
1025 */
1026int no_wildcards(name)
1027char *name;
1028{
1029     do {
1030          switch (*name) {
1031          case '\\':
1032               name++;
1033               break;
1034          case '?':
1035               return(0);
1036          case '*':
1037               return(0);
1038          case '[':
1039               return(0);
1040          }
1041     } while (*++name);
1042     return(1);
1043}
Note: See TracBrowser for help on using the repository browser.