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.
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#include <stdio.h>
13#include <sys/types.h>
[5048]14#include <dirent.h>
[1691]15#include <sys/param.h>
[3049]16#include <string.h>
[2175]17#include <errno.h>
18#include <com_err.h>
[1691]19#include "pattern.h"
[4415]20#include "directories.h"
[1691]21#include "undelete.h"
[2175]22#include "shell_regexp.h"
[4505]23#include "mit-copying.h"
[2175]24#include "delete_errs.h"
25#include "errors.h"
[2221]26#include "stack.h"
[24908]27#include "util.h"
[1691]28
[2175]29extern char *whoami;
[1691]30
[24908]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);
[1691]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 */ 
[23667]41int add_arrays(array1, num1, size1, array2, num2)
[1691]42char ***array1, ***array2;
[23667]43int *num1, *size1, *num2;
[1691]44{
45     int counter;
[23667]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
[2175]60     *array1 = (char **) realloc((char *) *array1, (unsigned)
[23667]61                                 (sizeof(char *) * *size1));
62     if (! *array1) {
[2175]63          set_error(errno);
64          error("realloc");
65          return error_code;
[1691]66     }
67     for (counter = *num1; counter < *num1 + *num2; counter++)
68          *(*array1 + counter) = *(*array2 + counter - *num1);
[2175]69     free ((char *) *array2);
[1691]70     *num1 += *num2;
[2175]71     return 0;
[1691]72}
73
74
75
76
[1705]77
78
[2175]79/*
80 * Add a string to a list of strings.
81 */
[24908]82int add_str(char ***strs, int num, int *size, char *str)
[1705]83{
[2175]84     char **ary;
85
[23667]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
[2221]100     if (! *strs) {
[2175]101          set_error(errno);
102          error("realloc");
103          return error_code;
[1705]104     }
[2365]105     ary[num] = Malloc((unsigned) (strlen(str) + 1));
[2175]106     if (! ary[num]) {
107          set_error(errno);
[2365]108          error("Malloc");
[2175]109          return error_code;
[1705]110     }
[2175]111     (void) strcpy(ary[num], str);
112     return 0;
[1705]113}
114
115
116
117
118
[2221]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
[2365]178 *   pointer according to Malloc() and can be released using free()
[2221]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;
[1691]187int *num_found;
[2175]188char ***found;
[2221]189int options;
[1691]190{
[2221]191     char       **matched_files, **return_files, **recurs_files;
192     int        num_matched_files = 0, num_return_files = 0,
[23667]193                num_recurs_files = 0, return_files_size = 0;
[2221]194     int        retval;
195     int        i;
[2377]196#ifdef DEBUG
197     int        j;
198#endif
[2221]199     int        match_options = 0;
200
[2377]201#ifdef DEBUG
202     fprintf(stderr, "Entering find_matches, name = %s, options = %d.\n",
203             name, options);
204#endif
205     
[2221]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;
[2175]210     
[2221]211     if (! match_options) {
212          set_error(PAT_NO_FILES_REQUESTED);
213          error("find_matches");
[2175]214          return error_code;
215     }
[2221]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;
[2175]223     }
[2221]224     if (num_matched_files == 0) {
225          *num_found = num_matched_files;
226          *found = matched_files;
[2377]227#ifdef DEBUG
228          fprintf(stderr, "No matches found, returning.\n");
229#endif
[2221]230          return 0;
231     }
[1705]232
[2377]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     
[2221]239     if (options & RECURS) {
[2365]240          return_files = (char **) Malloc(0);
241          num_return_files = 0;
242
[2221]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]);
[2175]249                    return retval;
[1691]250               }
[2221]251
[2365]252               if (num_recurs_files) {
253                    retval = add_arrays(&return_files, &num_return_files,
[23667]254                                        &return_files_size, &recurs_files,
255                                        &num_recurs_files);
[2365]256                    if (retval) {
257                         error("add_arrays");
258                         return retval;
259                    }
[2377]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
[1691]267               }
[2365]268               
[2377]269               if (is_deleted(lastpart(matched_files[i]))) {
[2221]270                    if (options & FIND_DELETED) {
271                         retval = add_str(&return_files, num_return_files,
[23667]272                                          &return_files_size,
[2221]273                                          matched_files[i]);
274                         if (retval) {
[2175]275                              error("add_str");
276                              return retval;
277                         }
[2221]278                         num_return_files++;
[2377]279#ifdef DEBUG
280                         fprintf(stderr, "Just added %s to return_files.\n",
281                                 return_files[num_return_files-1]);
282#endif
[2175]283                    }
284               }
[2221]285               else if (options & FIND_UNDELETED) {
286                    retval = add_str(&return_files, num_return_files,
[23667]287                                     &return_files_size, matched_files[i]);
[2221]288                    if (retval) {
289                         error("add_str");
290                         return retval;
291                    }
292                    num_return_files++;
[2377]293#ifdef DEBUG
294                    fprintf(stderr, "Just added %s to return_files.\n",
295                            return_files[num_return_files-1]);
296#endif
[2221]297               }
[2175]298          }
[2221]299          free_list(matched_files, num_matched_files);
300          *num_found = num_return_files;
301          *found = return_files;
[1691]302     }
[2221]303     else {
304          *num_found = num_matched_files;
305          *found = matched_files;
306     }
307
[2175]308     return 0;
[1705]309}
310
311
312
313
[2221]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         
[1705]336
[2221]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
[2365]361 *   in *found is a valid pointer according to Malloc() and can be
[2221]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
[2482]374 *   dirp = Opendir(base)
[2221]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 */
[24908]414static int do_match(char *name, int *num_found, char ***found,
415                    Boolean match_undeleted, Boolean match_deleted)
[1705]416{
[2221]417     char base[MAXPATHLEN];
[5138]418     struct dirent *dp;
[1705]419     DIR *dirp;
420     char first[MAXNAMLEN], rest[MAXPATHLEN];
[2175]421     int retval;
[2221]422     int strsize;
[3618]423     struct stat statbuf;
[3061]424#ifdef PATTERN_DEBUG
425     int j;
426#endif
[23667]427     int found_size = 0;
[1705]428     
[1732]429#ifdef DEBUG
[2221]430     printf("do_match: looking for %s\n", name);
[1732]431#endif
[2221]432
433     /* start: */
434     
435     if (*name == '/') {
436          *base = '/';
437          *(base + 1) = '\0';
438          name++;
439     }
440     else
441          *base = '\0';
442
[2365]443     *found = (char **) Malloc(0);
[2221]444     *num_found = 0;
445     
[2482]446     dirp = Opendir(base);
[2175]447     if (! dirp) {
448          set_error(errno);
449          error(base);
[3061]450#ifdef PATTERN_DEBUG
451          fprintf(stderr, "do_match: return 2.\n");
452#endif
[2175]453          return error_code;
454     }
[2221]455     (void) strcpy(first, firstpart(name, rest));
456     if ((! *first) && (match_undeleted)) {
[23667]457          retval = add_str(found, *num_found, &found_size, base);
[2221]458          if (retval) {
459               error("add_str");
460               (void) popall();
[3061]461#ifdef PATTERN_DEBUG
462               fprintf(stderr, "do_match: return 3.\n");
463#endif
[2221]464               return retval;
465          }
466          (*num_found)++;
[3061]467#ifdef PATTERN_DEBUG
468          fprintf(stderr, "do_match: return 4.\n");
469#endif
[2221]470          return 0;
471     }
472     
473     while (1) {
474          dp = readdir(dirp);
475          if (! dp) goto updir;
[1691]476
[2175]477          retval = reg_cmp(first, dp->d_name);
[3061]478#ifdef PATTERN_DEBUG
479        fprintf(stderr, "do_match: comparing %s to %s returns %d.\n",
480                first, dp->d_name, retval);
481#endif
[2175]482          if (retval < 0) {
483               error("reg_cmp");
[2221]484               goto updir;
[2175]485          }
486
[2221]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]);
[3061]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
[2221]496               if (retval < 0) {
497                    error("reg_cmp");
498                    goto updir;
[1691]499               }
[2175]500
[2221]501               if (retval == REGEXP_MATCH)
502                    goto downdir;
503               else
504                    continue;
505          }
506          else
507               continue;
508
509     downdir:
[3061]510#ifdef PATTERN_DEBUG
511          fprintf(stderr, "do_match: downdir\n");
512#endif
[2221]513          retval = push(&dirp, sizeof(DIR *));
514          if (retval) {
515               error("push");
516               (void) popall();
[3061]517#ifdef PATTERN_DEBUG
518               fprintf(stderr, "do_match: return 5.\n");
519#endif
[2221]520               return retval;
521          }
[3061]522#ifdef PATTERN_DEBUG
523          fprintf(stderr, "do_match: pushing %s, %s, %s\n", first, rest, base);
524#endif
[2221]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) {
[23667]533                         retval = add_str(found, *num_found, &found_size,
534                                          base);
[2221]535                         if (retval) {
536                              error("add_str");
537                              (void) popall();
[3061]538#ifdef PATTERN_DEBUG
539                              fprintf(stderr, "do_match: return 6.\n");
540#endif
[2221]541                              return retval;
542                         }
543                         (*num_found)++;
[2175]544                    }
[2221]545               }
546               else if (match_undeleted) {
[23667]547                    retval = add_str(found, *num_found, &found_size, base);
[2221]548                    if (retval) {
[2175]549                         error("add_str");
[2221]550                         (void) popall();
[3061]551#ifdef PATTERN_DEBUG
552                         fprintf(stderr, "do_match: return 7.\n");
553#endif
[2175]554                         return retval;
555                    }
[2221]556                    (*num_found)++;
[1691]557               }
[2221]558               string_pop(base);
559               string_pop(rest);
560               string_pop(first);
[3061]561#ifdef PATTERN_DEBUG
562               fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
563                       rest, base);
564#endif
[2221]565               (void) pop(&dirp, sizeof(DIR *));
566               continue;
[1705]567          }
[3618]568
[6420]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);
[2221]605          if (! dirp) {
[6420]606               if (retval || ((statbuf.st_mode & S_IFMT) == S_IFDIR)) {
607                    set_error(errno);
608                    error(base);
609               }
[2221]610               string_pop(base);
611               string_pop(rest);
612               string_pop(first);
[3061]613#ifdef PATTERN_DEBUG
614               fprintf(stderr, "do_match: popped %s, %s, %s\n", first,
615                       rest, base);
616#endif
[2221]617               (void) pop(&dirp, sizeof(DIR *));
618               continue;
619          }
620          else
621               continue;
[2175]622
[2221]623     updir:
[3061]624#ifdef PATTERN_DEBUG
625          fprintf(stderr, "do_match: updir\n");
626#endif
[2221]627          closedir(dirp);
628          string_pop(base);
[3061]629#ifdef PATTERN_DEBUG
630          fprintf(stderr, "do_match: popped %s\n", base);
631#endif
[2221]632          if (retval) {
633               if (retval != STACK_EMPTY) {
634                    error("pop");
635                    (void) popall();
[3061]636#ifdef PATTERN_DEBUG
637                    fprintf(stderr, "do_match: return 8.\n");
638#endif
[2221]639                    return retval;
[1691]640               }
[3061]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
[2221]649               return 0;
[1691]650          }
[2221]651          string_pop(rest);
652          string_pop(first);
[3061]653#ifdef PATTERN_DEBUG
654          fprintf(stderr, "do_match: popped %s, %s\n", rest, first);
655#endif
[2221]656          retval = pop(&dirp, sizeof(DIR *));
657          if (retval) {
658               error("pop");
659               (void) popall();
[3061]660#ifdef PATTERN_DEBUG
661               fprintf(stderr, "do_match: return 10.\n");
662#endif
[2221]663               return retval;
664          }
665          continue;
[1691]666     }
667}
668
[1705]669
670
671
672
673
[2221]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
[4404]679 *   filename; points to a valid int memory storage location; found
680 *   points to a valid char ** memory storage location.
[2221]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)
[3618]695 *   check if we just opened a deleted symlink and return if we did
[2482]696 *   dirp = Opendir(base)
[2221]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 */
[24908]755static int do_recurs(char *name, int *num_found, char ***found, int options)
[1691]756{
[2221]757     char base[MAXPATHLEN];
[5138]758     struct dirent *dp;
[1705]759     DIR *dirp;
[2175]760     int retval;
[2221]761     int strsize;
762     struct stat statbuf;
763     int use_stat;
[23667]764     int found_size = 0;
[4404]765
[1732]766#ifdef DEBUG
[2377]767     fprintf(stderr, "do_recurs: opening %s\n", name);
[1732]768#endif
[2221]769
770     /* start: */
771     
[2365]772     *found = (char **) Malloc(0);
[2221]773     *num_found = 0;
774     strcpy(base, name);
[3618]775
[4404]776     if (lstat(base, &statbuf)) {
[3618]777          set_error(errno);
778          error(base);
779          return error_code;
780     }
781         
[23666]782#ifdef S_IFLNK
[4404]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          }
[2221]818     }
[23666]819#endif
[3618]820
821     if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
822          return 0;
[2221]823     
[2482]824     dirp = Opendir(base);
[2175]825     if (! dirp) {
[2377]826#ifdef DEBUG
[3618]827          fprintf(stderr, "Couldn't open %s.\n", base);
[2377]828#endif
[3618]829          set_error(errno);
830          error(base);
831          return error_code;
[1705]832     }
833
[2221]834     if (options & (RECURS_FIND_DELETED | RECURS_DELETED))
835          options |= FIND_DELETED;
836     if (options & RECURS_FIND_UNDELETED)
837          options |= FIND_UNDELETED;
[1691]838     
[2221]839     while (1) {
840          dp = readdir(dirp);
841          if (! dp) goto updir;
[1691]842
[2221]843          if (is_deleted(dp->d_name)) {
844               if (options & FIND_DELETED) {
[23667]845                    retval = add_str(found, *num_found, &found_size,
[2221]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          }
[1705]861
[2221]862          if (options & FIND_UNDELETED) {
863               if (is_dotfile(dp->d_name)) {
864                    if (options & FIND_DOTFILES) {
[23667]865                         retval = add_str(found, *num_found, &found_size,
[2221]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 {
[23667]876                    retval = add_str(found, *num_found, &found_size,
[2221]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          }
[2175]886
[2221]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;
[2175]892          }
[1705]893         
[2221]894          continue;
895         
896               
897     downdir:
898          retval = push(&dirp, sizeof(DIR *));
899          if (retval) {
900               error("push");
901               (void) popall();
902               return retval;
[1691]903          }
[2221]904          string_push(base);
905          (void) strcpy(base, append(base, dp->d_name));
906
907          /*
[2482]908           * Originally, I did an Opendir() right at the start and
909           * then only checked things if the Opendir resulted in an
[2221]910           * error.  However, this is inefficient, because the
[2482]911           * Opendir() procedure works by first calling open() on the
[2221]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
[2482]916           * Opendir only if the stat results are satisfactory.
[2221]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) {
[2175]924               set_error(errno);
[2221]925               error(base);
926               string_pop(base);
927               (void) pop(&dirp, sizeof(DIR *));
[1691]928               continue;
[1705]929          }
[2221]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. */
[2482]938          dirp = Opendir(base);
[2221]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)) {
[2175]950                    closedir(dirp);
[2221]951                    set_warning(PAT_IS_MOUNT);
952                    error(base);
953                    string_pop(base);
954                    (void) pop(&dirp, sizeof(DIR *));
955                    continue;
[2175]956               }
[2582]957#ifdef DEBUG
958               else {
959                    fprintf(stderr,
960                            "do_recurs: %s isn't a mountpoint, following.\n",
961                            base);
962               }
963#endif
[1691]964          }
[1732]965#ifdef DEBUG
[2582]966          printf("do_recurs: opening %s\n", base);
[1732]967#endif
[2221]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;
[2175]980          }
[2221]981          retval = pop(&dirp, sizeof(DIR *));
982          if (retval) {
983               error("pop");
984               (void) popall();
[2175]985               return retval;
986          }
[2221]987          continue;
[1705]988     }
989}
990
991
[24908]992void free_list(char **list, int num)
[1705]993{
[2221]994     int i;
[1705]995
[2221]996     for (i = 0; i < num; i++)
997          free(list[i]);
[2175]998
[2221]999     free((char *) list);
[1705]1000}
1001
1002
1003
1004
[1756]1005
1006
1007/*
[2175]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
[1756]1011 */
[24908]1012int no_wildcards(char *name)
[1756]1013{
1014     do {
1015          switch (*name) {
1016          case '\\':
1017               name++;
1018               break;
[2175]1019          case '?':
[1756]1020               return(0);
1021          case '*':
1022               return(0);
[2175]1023          case '[':
1024               return(0);
[1756]1025          }
1026     } while (*++name);
1027     return(1);
1028}
Note: See TracBrowser for help on using the repository browser.