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

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