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

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