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

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