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

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