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

Revision 23666, 25.7 KB checked in by broder, 16 years ago (diff)
In delete: * Apply patches from jik: - Don't try to use symbolic links on platforms that don't have them.
RevLine 
[1691]1/*
[12350]2 * $Id: pattern.c,v 1.28 1999-01-22 23:09:03 ghudson Exp $
[1691]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.
[4505]9 * For copying and distribution information, see the file "mit-copying.h."
[1691]10 */
11
12#if (!defined(lint) && !defined(SABER))
[12350]13     static char rcsid_pattern_c[] = "$Id: pattern.c,v 1.28 1999-01-22 23:09:03 ghudson Exp $";
[1691]14#endif
15
16#include <stdio.h>
17#include <sys/types.h>
[5048]18#include <dirent.h>
[1691]19#include <sys/param.h>
[3049]20#include <string.h>
[2175]21#include <errno.h>
22#include <com_err.h>
[1691]23#include "pattern.h"
24#include "util.h"
[4415]25#include "directories.h"
[1691]26#include "undelete.h"
[2175]27#include "shell_regexp.h"
[4505]28#include "mit-copying.h"
[2175]29#include "delete_errs.h"
30#include "errors.h"
[2221]31#include "stack.h"
[1691]32
[2175]33extern char *whoami;
[1691]34
[2221]35void free_list();
[1691]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 */ 
[2175]43int add_arrays(array1, num1, array2, num2)
[1691]44char ***array1, ***array2;
45int *num1, *num2;
46{
47     int counter;
48     
[2175]49     *array1 = (char **) realloc((char *) *array1, (unsigned)
50                                 (sizeof(char *) * (*num1 + *num2)));
[5048]51     if ((! *array1) && (*num1 + *num2))
52     {
[2175]53          set_error(errno);
54          error("realloc");
55          return error_code;
[1691]56     }
57     for (counter = *num1; counter < *num1 + *num2; counter++)
58          *(*array1 + counter) = *(*array2 + counter - *num1);
[2175]59     free ((char *) *array2);
[1691]60     *num1 += *num2;
[2175]61     return 0;
[1691]62}
63
64
65
66
[1705]67
68
[2175]69/*
70 * Add a string to a list of strings.
71 */
72int add_str(strs, num, str)
73char ***strs;
[1705]74int num;
75char *str;
76{
[2175]77     char **ary;
78
79     ary = *strs = (char **) realloc((char *) *strs, (unsigned)
80                                     (sizeof(char *) * (num + 1)));
[2221]81     if (! *strs) {
[2175]82          set_error(errno);
83          error("realloc");
84          return error_code;
[1705]85     }
[2365]86     ary[num] = Malloc((unsigned) (strlen(str) + 1));
[2175]87     if (! ary[num]) {
88          set_error(errno);
[2365]89          error("Malloc");
[2175]90          return error_code;
[1705]91     }
[2175]92     (void) strcpy(ary[num], str);
93     return 0;
[1705]94}
95
96
97
98
99
[2221]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
[2365]159 *   pointer according to Malloc() and can be released using free()
[2221]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;
[1691]168int *num_found;
[2175]169char ***found;
[2221]170int options;
[1691]171{
[2221]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;
[2377]177#ifdef DEBUG
178     int        j;
179#endif
[2221]180     int        match_options = 0;
181
[2377]182#ifdef DEBUG
183     fprintf(stderr, "Entering find_matches, name = %s, options = %d.\n",
184             name, options);
185#endif
186     
[2221]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;
[2175]191     
[2221]192     if (! match_options) {
193          set_error(PAT_NO_FILES_REQUESTED);
194          error("find_matches");
[2175]195          return error_code;
196     }
[2221]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;
[2175]204     }
[2221]205     if (num_matched_files == 0) {
206          *num_found = num_matched_files;
207          *found = matched_files;
[2377]208#ifdef DEBUG
209          fprintf(stderr, "No matches found, returning.\n");
210#endif
[2221]211          return 0;
212     }
[1705]213
[2377]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     
[2221]220     if (options & RECURS) {
[2365]221          return_files = (char **) Malloc(0);
222          num_return_files = 0;
223
[2221]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]);
[2175]230                    return retval;
[1691]231               }
[2221]232
[2365]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                    }
[2377]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
[1691]247               }
[2365]248               
[2377]249               if (is_deleted(lastpart(matched_files[i]))) {
[2221]250                    if (options & FIND_DELETED) {
251                         retval = add_str(&return_files, num_return_files,
252                                          matched_files[i]);
253                         if (retval) {
[2175]254                              error("add_str");
255                              return retval;
256                         }
[2221]257                         num_return_files++;
[2377]258#ifdef DEBUG
259                         fprintf(stderr, "Just added %s to return_files.\n",
260                                 return_files[num_return_files-1]);
261#endif
[2175]262                    }
263               }
[2221]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++;
[2377]272#ifdef DEBUG
273                    fprintf(stderr, "Just added %s to return_files.\n",
274                            return_files[num_return_files-1]);
275#endif
[2221]276               }
[2175]277          }
[2221]278          free_list(matched_files, num_matched_files);
279          *num_found = num_return_files;
280          *found = return_files;
[1691]281     }
[2221]282     else {
283          *num_found = num_matched_files;
284          *found = matched_files;
285     }
286
[2175]287     return 0;
[1705]288}
289
290
291
292
[2221]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         
[1705]315
[2221]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
[2365]340 *   in *found is a valid pointer according to Malloc() and can be
[2221]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
[2482]353 *   dirp = Opendir(base)
[2221]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;
[1705]395int *num_found;
[2175]396char ***found;
[2221]397Boolean match_undeleted, match_deleted;
[1705]398{
[2221]399     char base[MAXPATHLEN];
[5138]400     struct dirent *dp;
[1705]401     DIR *dirp;
402     char first[MAXNAMLEN], rest[MAXPATHLEN];
[2175]403     int retval;
[2221]404     int strsize;
[3618]405     struct stat statbuf;
[3061]406#ifdef PATTERN_DEBUG
407     int j;
408#endif
[1705]409     
[1732]410#ifdef DEBUG
[2221]411     printf("do_match: looking for %s\n", name);
[1732]412#endif
[2221]413
414     /* start: */
415     
416     if (*name == '/') {
417          *base = '/';
418          *(base + 1) = '\0';
419          name++;
420     }
421     else
422          *base = '\0';
423
[2365]424     *found = (char **) Malloc(0);
[2221]425     *num_found = 0;
426     
[2482]427     dirp = Opendir(base);
[2175]428     if (! dirp) {
429          set_error(errno);
430          error(base);
[3061]431#ifdef PATTERN_DEBUG
432          fprintf(stderr, "do_match: return 2.\n");
433#endif
[2175]434          return error_code;
435     }
[2221]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();
[3061]442#ifdef PATTERN_DEBUG
443               fprintf(stderr, "do_match: return 3.\n");
444#endif
[2221]445               return retval;
446          }
447          (*num_found)++;
[3061]448#ifdef PATTERN_DEBUG
449          fprintf(stderr, "do_match: return 4.\n");
450#endif
[2221]451          return 0;
452     }
453     
454     while (1) {
455          dp = readdir(dirp);
456          if (! dp) goto updir;
[1691]457
[2175]458          retval = reg_cmp(first, dp->d_name);
[3061]459#ifdef PATTERN_DEBUG
460        fprintf(stderr, "do_match: comparing %s to %s returns %d.\n",
461                first, dp->d_name, retval);
462#endif
[2175]463          if (retval < 0) {
464               error("reg_cmp");
[2221]465               goto updir;
[2175]466          }
467
[2221]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]);
[3061]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
[2221]477               if (retval < 0) {
478                    error("reg_cmp");
479                    goto updir;
[1691]480               }
[2175]481
[2221]482               if (retval == REGEXP_MATCH)
483                    goto downdir;
484               else
485                    continue;
486          }
487          else
488               continue;
489
490     downdir:
[3061]491#ifdef PATTERN_DEBUG
492          fprintf(stderr, "do_match: downdir\n");
493#endif
[2221]494          retval = push(&dirp, sizeof(DIR *));
495          if (retval) {
496               error("push");
497               (void) popall();
[3061]498#ifdef PATTERN_DEBUG
499               fprintf(stderr, "do_match: return 5.\n");
500#endif
[2221]501               return retval;
502          }
[3061]503#ifdef PATTERN_DEBUG
504          fprintf(stderr, "do_match: pushing %s, %s, %s\n", first, rest, base);
505#endif
[2221]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();
[3061]518#ifdef PATTERN_DEBUG
519                              fprintf(stderr, "do_match: return 6.\n");
520#endif
[2221]521                              return retval;
522                         }
523                         (*num_found)++;
[2175]524                    }
[2221]525               }
526               else if (match_undeleted) {
527                    retval = add_str(found, *num_found, base);
528                    if (retval) {
[2175]529                         error("add_str");
[2221]530                         (void) popall();
[3061]531#ifdef PATTERN_DEBUG
532                         fprintf(stderr, "do_match: return 7.\n");
533#endif
[2175]534                         return retval;
535                    }
[2221]536                    (*num_found)++;
[1691]537               }
[2221]538               string_pop(base);
539               string_pop(rest);
540               string_pop(first);
[3061]541#ifdef PATTERN_DEBUG
542               fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
543                       rest, base);
544#endif
[2221]545               (void) pop(&dirp, sizeof(DIR *));
546               continue;
[1705]547          }
[3618]548
[6420]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);
[2221]585          if (! dirp) {
[6420]586               if (retval || ((statbuf.st_mode & S_IFMT) == S_IFDIR)) {
587                    set_error(errno);
588                    error(base);
589               }
[2221]590               string_pop(base);
591               string_pop(rest);
592               string_pop(first);
[3061]593#ifdef PATTERN_DEBUG
594               fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
595                       rest, base);
596#endif
[2221]597               (void) pop(&dirp, sizeof(DIR *));
598               continue;
599          }
600          else
601               continue;
[2175]602
[2221]603     updir:
[3061]604#ifdef PATTERN_DEBUG
605          fprintf(stderr, "do_match: updir\n");
606#endif
[2221]607          closedir(dirp);
608          string_pop(base);
[3061]609#ifdef PATTERN_DEBUG
610          fprintf(stderr, "do_match: popped %s\n", base);
611#endif
[2221]612          if (retval) {
613               if (retval != STACK_EMPTY) {
614                    error("pop");
615                    (void) popall();
[3061]616#ifdef PATTERN_DEBUG
617                    fprintf(stderr, "do_match: return 8.\n");
618#endif
[2221]619                    return retval;
[1691]620               }
[3061]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
[2221]629               return 0;
[1691]630          }
[2221]631          string_pop(rest);
632          string_pop(first);
[3061]633#ifdef PATTERN_DEBUG
634          fprintf(stderr, "do_match: popped %s, %s\n", rest, first);
635#endif
[2221]636          retval = pop(&dirp, sizeof(DIR *));
637          if (retval) {
638               error("pop");
639               (void) popall();
[3061]640#ifdef PATTERN_DEBUG
641               fprintf(stderr, "do_match: return 10.\n");
642#endif
[2221]643               return retval;
644          }
645          continue;
[1691]646     }
647}
648
[1705]649
650
651
652
653
[2221]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
[4404]659 *   filename; points to a valid int memory storage location; found
660 *   points to a valid char ** memory storage location.
[2221]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)
[3618]675 *   check if we just opened a deleted symlink and return if we did
[2482]676 *   dirp = Opendir(base)
[2221]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;
[1705]737int *num_found;
[2175]738char ***found;
[2221]739int options;
[1691]740{
[2221]741     char base[MAXPATHLEN];
[5138]742     struct dirent *dp;
[1705]743     DIR *dirp;
[2175]744     int retval;
[2221]745     int strsize;
746     struct stat statbuf;
747     int use_stat;
[4404]748
[1732]749#ifdef DEBUG
[2377]750     fprintf(stderr, "do_recurs: opening %s\n", name);
[1732]751#endif
[2221]752
753     /* start: */
754     
[2365]755     *found = (char **) Malloc(0);
[2221]756     *num_found = 0;
757     strcpy(base, name);
[3618]758
[4404]759     if (lstat(base, &statbuf)) {
[3618]760          set_error(errno);
761          error(base);
762          return error_code;
763     }
764         
[23666]765#ifdef S_IFLNK
[4404]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          }
[2221]801     }
[23666]802#endif
[3618]803
804     if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
805          return 0;
[2221]806     
[2482]807     dirp = Opendir(base);
[2175]808     if (! dirp) {
[2377]809#ifdef DEBUG
[3618]810          fprintf(stderr, "Couldn't open %s.\n", base);
[2377]811#endif
[3618]812          set_error(errno);
813          error(base);
814          return error_code;
[1705]815     }
816
[2221]817     if (options & (RECURS_FIND_DELETED | RECURS_DELETED))
818          options |= FIND_DELETED;
819     if (options & RECURS_FIND_UNDELETED)
820          options |= FIND_UNDELETED;
[1691]821     
[2221]822     while (1) {
823          dp = readdir(dirp);
824          if (! dp) goto updir;
[1691]825
[2221]826          if (is_deleted(dp->d_name)) {
827               if (options & FIND_DELETED) {
828                    retval = add_str(found, *num_found,
829                                     append(base, dp->d_name));
830                    if (retval) {
831                         error("add_str");
832                         (void) popall();
833                         return retval;
834                    }
835                    (*num_found)++;
836                    if (options & RECURS_DELETED)
837                         goto downdir;
838                    else
839                         continue;
840               }
841               else
842                    continue;
843          }
[1705]844
[2221]845          if (options & FIND_UNDELETED) {
846               if (is_dotfile(dp->d_name)) {
847                    if (options & FIND_DOTFILES) {
848                         retval = add_str(found, *num_found,
849                                          append(base, dp->d_name));
850                         if (retval) {
851                              error("add_str");
852                              (void) popall();
853                              return retval;
854                         }
855                    }
856                    continue;
857               }
858               else {
859                    retval = add_str(found, *num_found,
860                                     append(base, dp->d_name));
861                    if (retval) {
862                         error("add_str");
863                         (void) popall();
864                         return retval;
865                    }
866                    (*num_found)++;
867               }
868          }
[2175]869
[2221]870          if (! is_dotfile(dp->d_name)) {
871               if (options & RECURS_FIND_DELETED)
872                    goto downdir;
873               if (options & RECURS_FIND_UNDELETED)
874                    goto downdir;
[2175]875          }
[1705]876         
[2221]877          continue;
878         
879               
880     downdir:
881          retval = push(&dirp, sizeof(DIR *));
882          if (retval) {
883               error("push");
884               (void) popall();
885               return retval;
[1691]886          }
[2221]887          string_push(base);
888          (void) strcpy(base, append(base, dp->d_name));
889
890          /*
[2482]891           * Originally, I did an Opendir() right at the start and
892           * then only checked things if the Opendir resulted in an
[2221]893           * error.  However, this is inefficient, because the
[2482]894           * Opendir() procedure works by first calling open() on the
[2221]895           * file, and *then* calling fstat on the file descriptor
896           * that is returned.  since most of the time we will be
897           * trying to open things that are not directory, it is much
898           * more effecient to do the stat first here and to do the
[2482]899           * Opendir only if the stat results are satisfactory.
[2221]900           */
901          use_stat = (options & FOLLW_LINKS) && (! is_deleted(lastpart(base)));
902          if (use_stat)
903               retval = stat(base, &statbuf);
904          else
905               retval = lstat(base, &statbuf);
906          if (retval == -1) {
[2175]907               set_error(errno);
[2221]908               error(base);
909               string_pop(base);
910               (void) pop(&dirp, sizeof(DIR *));
[1691]911               continue;
[1705]912          }
[2221]913          /* It's not a directory, so punt it and continue. */
914          if ((statbuf.st_mode & S_IFMT) != S_IFDIR) {
915               string_pop(base);
916               (void) pop(&dirp, sizeof(DIR *));
917               continue;
918          }
919
920          /* Actually try to open it. */
[2482]921          dirp = Opendir(base);
[2221]922          if (! dirp) {
923               set_error(errno);
924               error(base);
925               string_pop(base);
926               (void) pop(&dirp, sizeof(DIR *));
927               continue;
928          }
929         
930          if (! (options & FOLLW_MOUNTPOINTS)) {
931               if (is_mountpoint(base, use_stat ? (struct stat *) NULL :
932                                 &statbuf)) {
[2175]933                    closedir(dirp);
[2221]934                    set_warning(PAT_IS_MOUNT);
935                    error(base);
936                    string_pop(base);
937                    (void) pop(&dirp, sizeof(DIR *));
938                    continue;
[2175]939               }
[2582]940#ifdef DEBUG
941               else {
942                    fprintf(stderr,
943                            "do_recurs: %s isn't a mountpoint, following.\n",
944                            base);
945               }
946#endif
[1691]947          }
[1732]948#ifdef DEBUG
[2582]949          printf("do_recurs: opening %s\n", base);
[1732]950#endif
[2221]951          continue;
952         
953     updir:
954          closedir(dirp);
955          string_pop(base);
956          if (retval) {
957               if (retval != STACK_EMPTY) {
958                    error("pop");
959                    (void) popall();
960                    return retval;
961               }
962               return 0;
[2175]963          }
[2221]964          retval = pop(&dirp, sizeof(DIR *));
965          if (retval) {
966               error("pop");
967               (void) popall();
[2175]968               return retval;
969          }
[2221]970          continue;
[1705]971     }
972}
973
974
[2221]975void free_list(list, num)
976char **list;
977int num;
[1705]978{
[2221]979     int i;
[1705]980
[2221]981     for (i = 0; i < num; i++)
982          free(list[i]);
[2175]983
[2221]984     free((char *) list);
[1705]985}
986
987
988
989
[1756]990
991
992/*
[2175]993 * returns true if the filename has no globbing wildcards in it.  That
994 * means no non-quoted question marks, asterisks, or open square
995 * braces.  Assumes a null-terminated string, and a valid globbing
[1756]996 */
997int no_wildcards(name)
998char *name;
999{
1000     do {
1001          switch (*name) {
1002          case '\\':
1003               name++;
1004               break;
[2175]1005          case '?':
[1756]1006               return(0);
1007          case '*':
1008               return(0);
[2175]1009          case '[':
1010               return(0);
[1756]1011          }
1012     } while (*++name);
1013     return(1);
1014}
Note: See TracBrowser for help on using the repository browser.