rsalz@uunet.uu.net (Rich Salz) (05/08/90)
Submitted-by: "Jonathan I. Kamens" <jik@pit-manager.mit.edu> Posting-number: Volume 22, Issue 27 Archive-name: undel2/part03 #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh <file", e.g.. If this archive is complete, you # will see the following message at the end: # "End of archive 3 (of 3)." # Contents: delete.c directories.c pattern.c # Wrapped by rsalz@papaya.bbn.com on Mon May 7 16:54:02 1990 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'delete.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'delete.c'\" else echo shar: Extracting \"'delete.c'\" \(13132 characters\) sed "s/^X//" >'delete.c' <<'END_OF_FILE' X/* X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/delete.c,v $ X * $Author: jik $ X * X * This program is a replacement for rm. Instead of actually deleting X * files, it marks them for deletion by prefixing them with a ".#" X * prefix. X * X * Copyright (c) 1989 by the Massachusetts Institute of Technology. X * For copying and distribution information, see the file "mit-copyright.h." X */ X X#if (!defined(lint) && !defined(SABER)) X static char rcsid_delete_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/delete.c,v 1.21 89/12/15 04:39:22 jik Exp $"; X#endif X X#include <sys/types.h> X#include <stdio.h> X#include <sys/stat.h> X#include <sys/dir.h> X#include <strings.h> X#include <sys/param.h> X#include <sys/file.h> X#include <errno.h> X#include "errors.h" X#include "delete_errs.h" X#include "util.h" X#include "delete.h" X#include "mit-copyright.h" X X X X/* X * ALGORITHM: X * X * 1. Parse command-line arguments and set flags. X * 2. Call the function delete() for each filename command-line argument. X * X * delete(): X * X * 1. Can the file be lstat'd? X * no -- abort X * yes -- continue X * 2. Is the file a directory? X * yes -- is it a dotfile? X * yes -- abort X * no -- continue X * -- is the filesonly option set? X * yes -- is the recursive option specified? X * yes -- continue X * no -- abort X * no -- is the directory empty? X * yes -- remove it X * no -- is the directoriesonly option set? X * yes -- abort X * no -- continue X * -- is the recursive option specified? X * yes -- continue X * no -- abort X * no -- is the directoriesonly option set? X * yes -- abort X * no -- continue X * 3. If the file is a file, remove it. X * 4. If the file is a directory, open it and pass each of its members X * (excluding . files) to delete(). X */ X X Xint force, interactive, recursive, noop, verbose, filesonly, directoriesonly; Xint emulate_rm; Xextern int errno; X Xmain(argc, argv) Xint argc; Xchar *argv[]; X{ X extern char *optarg; X extern int optind; X int arg; X X whoami = lastpart(argv[0]); X X initialize_del_error_table(); X X force = interactive = recursive = noop = verbose = filesonly = X directoriesonly = emulate_rm = 0; X while ((arg = getopt(argc, argv, "efirnvFD")) != -1) { X switch (arg) { X case 'r': X recursive++; X if (directoriesonly) { X fprintf(stderr, "%s: -r and -D are mutually exclusive.\n", X whoami); X usage(); X exit(1); X } X break; X case 'f': X force++; X break; X case 'i': X interactive++; X break; X case 'n': X noop++; X break; X case 'v': X verbose++; X break; X case 'e': X emulate_rm++; X break; X case 'F': X filesonly++; X if (directoriesonly) { X fprintf(stderr, "%s: -F and -D are mutually exclusive.\n", X whoami); X usage(); X exit(1); X } X break; X case 'D': X directoriesonly++; X if (recursive) { X fprintf(stderr, "%s: -r and -D are mutually exclusive.\n", X whoami); X usage(); X exit(1); X } X if (filesonly) { X fprintf(stderr, "%s: -F and -D are mutually exclusive.\n", X whoami); X usage(); X exit(1); X } X break; X default: X usage(); X exit(1); X } X } X report_errors = ! (force || emulate_rm); X X if (optind == argc) { X if (! force) { X fprintf(stderr, "%s: no files specified.\n", whoami); X usage(); X } X exit(force ? 0 : 1); X } X while (optind < argc) { X if (delete(argv[optind], 0)) X error(argv[optind]); X optind++; X } X exit(((! force) && error_occurred) ? 1 : 0); X} X X X Xusage() X{ X printf("Usage: %s [ options ] filename ...\n", whoami); X printf("Options are:\n"); X printf(" -r recursive\n"); X printf(" -i interactive\n"); X printf(" -f force\n"); X printf(" -n noop\n"); X printf(" -v verbose\n"); X printf(" -F files only\n"); X printf(" -D directories only\n"); X printf(" -- end options and start filenames\n"); X printf("-r and -D are mutually exclusive\n"); X printf("-F and -D are mutually exclusive\n"); X} X X X X Xdelete(filename, recursed) Xchar *filename; Xint recursed; X{ X struct stat stat_buf; X int retval; X X /* can the file be lstat'd? */ X if (lstat(filename, &stat_buf) == -1) { X set_error(errno); X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s nonexistent\n", whoami, filename); X error(filename); X return error_code; X } X X /* is the file a directory? */ X if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) { X /* is the file a dot file? */ X if (is_dotfile(lastpart(filename))) { X set_error(DELETE_IS_DOTFILE); X if (emulate_rm && (! force)) X fprintf(stderr, "%s: cannot remove `.' or `..'\n", whoami); X error(filename); X return error_code; X } X X /* is the filesonly option set? */ X if (filesonly) { X /* is the recursive option specified? */ X if (recursive) { X if (retval = recursive_delete(filename, stat_buf, X recursed)) { X error(filename); X return retval; X } X } X else { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s directory\n", whoami, X filename); X set_error(DELETE_CANT_DEL_DIR); X error(filename); X return error_code; X } X } X else { X /* is the directory empty? */ X if ((retval = empty_directory(filename)) < 0) { X error(filename); X if (! emulate_rm) X return error_code; X } X X if (retval > 0) { X /* remove it */ X if (retval = do_move(filename, stat_buf, 0)) { X error(filename); X return error_code; X } X } X else { X /* is the directoriesonly option set? */ X if (directoriesonly) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s: Directory not empty\n", X whoami, filename); X set_error(DELETE_DIR_NOT_EMPTY); X error(filename); X return error_code; X } X else { X /* is the recursive option specified? */ X if (recursive) { X if (retval = recursive_delete(filename, stat_buf, X recursed)) { X error(filename); X return error_code; X } X } X else { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s not empty\n", X whoami, filename); X set_error(DELETE_DIR_NOT_EMPTY); X error(filename); X return error_code; X } X } X } X } X } X else { X /* is the directoriesonly option set? */ X if (directoriesonly) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s: Not a directory\n", whoami, X filename); X set_error(DELETE_CANT_DEL_FILE); X error(filename); X return error_code; X } X else { X if (retval = do_move(filename, stat_buf, 0)) { X error(filename); X return error_code; X } X } X } X return 0; X} X X X X X Xempty_directory(filename) Xchar *filename; X{ X DIR *dirp; X struct direct *dp; X X dirp = Opendir(filename); X if (! dirp) { X set_error(errno); X error(filename); X return -1; X } X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { X if (is_dotfile(dp->d_name)) X continue; X if (is_deleted(dp->d_name)) X continue; X else { X closedir(dirp); X return 0; X } X } X closedir(dirp); X return 1; X} X X X X Xrecursive_delete(filename, stat_buf, recursed) Xchar *filename; Xstruct stat stat_buf; Xint recursed; X{ X DIR *dirp; X struct direct *dp; X int status = 0; X char newfile[MAXPATHLEN]; X int retval = 0; X X if (interactive && recursed) { X printf("%s: remove directory %s? ", whoami, filename); X if (! yes()) { X set_status(DELETE_NOT_DELETED); X return error_code; X } X } X dirp = Opendir(filename); X if (! dirp) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s not changed\n", whoami, filename); X set_error(errno); X error(filename); X return error_code; X } X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { X if (is_dotfile(dp->d_name)) X continue; X if (is_deleted(dp->d_name)) X continue; X else { X (void) strcpy(newfile, append(filename, dp->d_name)); X if (! *newfile) { X error(filename); X status = error_code; X } X X retval = delete(newfile, 1); X if (retval) { X error(newfile); X status = retval; X } X } X } X closedir(dirp); X X if (status && (! emulate_rm)) { X set_warning(DELETE_DIR_NOT_EMPTY); X error(filename); X } X else X retval = do_move(filename, stat_buf, status); X X if (retval) X status = retval; X X return status; X} X X X X X X Xdo_move(filename, stat_buf, subs_not_deleted) Xchar *filename; Xstruct stat stat_buf; Xint subs_not_deleted; /* If the file in question is a directory, and */ X /* there is something underneath it that hasn't */ X /* been removed, this will be set to true. */ X /* The program asks if the user wants to delete */ X /* the directory, and if the user says yes, */ X /* checks the value of subs_not_deleted. If */ X /* it's true, an error results. */ X /* This is used only when emulating rm. */ X{ X char *last; X char buf[MAXPATHLEN]; X char name[MAXNAMLEN]; X struct stat deleted_buf; X X (void) strncpy(buf, filename, MAXPATHLEN); X last = lastpart(buf); X if (strlen(last) > MAXNAMLEN) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s: filename too long\n", whoami, X filename); X set_error(ENAMETOOLONG); X error(filename); X return error_code; X } X (void) strcpy(name, last); X if (strlen(buf) + 3 > MAXPATHLEN) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s: pathname too long\n", whoami, X filename); X set_error(ENAMETOOLONG); X error(filename); X return error_code; X } X *last = '\0'; X (void) strcat(buf, ".#"); X (void) strcat(buf, name); X if (interactive) { X printf("%s: remove %s? ", whoami, filename); X if (! yes()) { X set_status(DELETE_NOT_DELETED); X return error_code; X } X } X else if ((! force) && ((stat_buf.st_mode & S_IFMT) != S_IFLNK) X && access(filename, W_OK)) { X if (emulate_rm) X printf("%s: override protection %o for %s? ", whoami, X stat_buf.st_mode & 0777, filename); X else X printf("%s: File %s not writeable. Delete anyway? ", whoami, X filename); X if (! yes()) { X set_status(DELETE_NOT_DELETED); X return error_code; X } X } X if (emulate_rm && subs_not_deleted) { X if (! force) X fprintf(stderr, "%s: %s not removed\n", whoami, filename); X return 1; X } X if (noop) { X fprintf(stderr, "%s: %s would be removed\n", whoami, filename); X return 0; X } X if ((! lstat(buf, &deleted_buf)) && unlink_completely(buf)) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s not removed\n", whoami, filename); X error(filename); X return error_code; X } X if (rename(filename, buf)) { X if (emulate_rm && (! force)) X fprintf(stderr, "%s: %s not removed\n", whoami, filename); X set_error(errno); X error(filename); X return error_code; X } X else { X if (verbose) X fprintf(stderr, "%s: %s removed\n", whoami, filename); X return 0; X } X} X X X Xunlink_completely(filename) Xchar *filename; X{ X char buf[MAXPATHLEN]; X struct stat stat_buf; X DIR *dirp; X struct direct *dp; X int status = 0; X int retval; X X if (lstat(filename, &stat_buf)) { X set_error(errno); X error(filename); X return error_code; X } X X if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) { X dirp = Opendir(filename); X if (! dirp) { X set_error(errno); X error(filename); X return error_code; X } X X for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) { X if (is_dotfile(dp->d_name)) X continue; X (void) strcpy(buf, append(filename, dp->d_name)); X if (! *buf) { X status = error_code; X error(filename); X continue; X } X retval = unlink_completely(buf); X if (retval) { X status = retval; X error(filename); X } X } X closedir(dirp); X X if (status) X return status; X X retval = rmdir(filename); X if (retval) { X set_error(errno); X error(filename); X return errno; X } X } X else { X retval = unlink(filename); X if (retval) { X set_error(errno); X error(filename); X return error_code; X } X else X return 0; X } X return 0; X} END_OF_FILE if test 13132 -ne `wc -c <'delete.c'`; then echo shar: \"'delete.c'\" unpacked with wrong size! fi # end of 'delete.c' fi if test -f 'directories.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'directories.c'\" else echo shar: Extracting \"'directories.c'\" \(14682 characters\) sed "s/^X//" >'directories.c' <<'END_OF_FILE' X/* X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/directories.c,v $ X * $Author: jik $ X * X * This program is part of a package including delete, undelete, X * lsdel, expunge and purge. The software suite is meant as a X * replacement for rm which allows for file recovery. X * X * Copyright (c) 1989 by the Massachusetts Institute of Technology. X * For copying and distribution information, see the file "mit-copyright.h." X */ X X#if !defined(lint) && !defined(SABER) X static char rcsid_directories_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/directories.c,v 1.15 89/11/22 21:32:24 jik Exp $"; X#endif X X#include <stdio.h> X#include <sys/types.h> X#include <sys/stat.h> X#include <sys/param.h> X#include <sys/dir.h> X#include <strings.h> X#include <errno.h> X#include <com_err.h> X#include "delete_errs.h" X#include "directories.h" X#include "util.h" X#include "mit-copyright.h" X#include "errors.h" X Xextern char *realloc(); Xextern long time(); Xextern int errno; X Xstatic filerec root_tree; Xstatic filerec cwd_tree; X Xvoid free_leaf(); X X /* These are not static because external routines need to be able to */ X /* access them. */ Xtime_t current_time; X X Xstatic filerec default_cwd = { X "", X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X False, X False, X {0} X}; X Xstatic filerec default_root = { X "/", X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X False, X False, X {0} X}; X Xstatic filerec default_directory = { X "", X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X False, X False, X {0} X}; X Xstatic filerec default_file = { X "", X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X (filerec *) NULL, X False, X False, X {0} X}; X X Xfilerec *get_root_tree() X{ X return(&root_tree); X} X X X Xfilerec *get_cwd_tree() X{ X return(&cwd_tree); X} X X Xint initialize_tree() X{ X int retval; X X root_tree = default_root; X cwd_tree = default_cwd; X X current_time = time((time_t *)0); X if (retval = get_specs(".", &cwd_tree.specs, FOLLOW_LINKS)) { X error("get_specs on ."); X return retval; X } X if (retval = get_specs("/", &root_tree.specs, FOLLOW_LINKS)) { X error("get_specs on /"); X return retval; X } X return 0; X} X X Xint add_path_to_tree(path, leaf) Xchar *path; Xfilerec **leaf; X{ X filerec *parent; X char next_name[MAXNAMLEN]; X char lpath[MAXPATHLEN], built_path[MAXPATHLEN], *ptr; X struct stat specs; X int retval; X X if (retval = get_specs(path, &specs, DONT_FOLLOW_LINKS)) { X char error_buf[MAXPATHLEN+14]; X X (void) sprintf(error_buf, "get_specs on %s", path); X error(error_buf); X return retval; X } X X (void) strcpy(lpath, path); /* we don't want to damage the user's */ X /* string */ X ptr = lpath; X if (*ptr == '/') { X parent = &root_tree; X ptr++; X (void) strcpy(built_path, "/"); X } X else if (! strncmp(ptr, "./", 2)) { X parent = &cwd_tree; X ptr += 2; X *built_path = '\0'; X } X else { X parent = &cwd_tree; X *built_path = '\0'; X } X X (void) strcpy(next_name, firstpart(ptr, ptr)); X while (*ptr) { X (void) strcat(built_path, next_name); X if (retval = add_directory_to_parent(parent, next_name, False, X &parent)) { X error("add_directory_to_parent"); X return retval; X } X (void) strcpy(next_name, firstpart(ptr, ptr)); X if (retval = get_specs(built_path, &parent->specs, FOLLOW_LINKS)) { X char error_buf[MAXPATHLEN+14]; X X (void) sprintf(error_buf, "get_specs on %s", built_path); X error(error_buf); X return retval; X } X (void) strcat(built_path, "/"); X } X if ((specs.st_mode & S_IFMT) == S_IFDIR) { X retval = add_directory_to_parent(parent, next_name, True, leaf); X if (retval) { X error("add_directory_to_parent"); X return retval; X } X } X else { X retval = add_file_to_parent(parent, next_name, True, leaf); X if (retval) { X error("add_file_to_parent"); X return retval; X } X } X X (*leaf)->specs = specs; X X return 0; X} X X X X Xint get_specs(path, specs, follow) Xchar *path; Xstruct stat *specs; Xint follow; /* follow symlinks or not? */ X{ X int status; X X if (strlen(path)) if ((path[strlen(path) - 1] == '/') && X (strlen(path) != 1)) X path[strlen(path) - 1] = '\0'; X if (follow == FOLLOW_LINKS) X status = stat(path, specs); X else X status = lstat(path, specs); X X if (status) { X set_error(errno); X error(path); X return error_code; X } X X return 0; X} X X X Xfilerec *next_leaf(leaf) Xfilerec *leaf; X{ X filerec *new; X X if ((leaf->specs.st_mode & S_IFMT) == S_IFDIR) { X new = first_in_directory(leaf); X if (new) X return(new); X new = next_directory(leaf); X return(new); X } X else { X new = next_in_directory(leaf); X return(new); X } X} X X Xfilerec *next_specified_leaf(leaf) Xfilerec *leaf; X{ X while (leaf = next_leaf(leaf)) X if (leaf->specified) X return(leaf); X return((filerec *) NULL); X} X X Xfilerec *next_directory(leaf) Xfilerec *leaf; X{ X filerec *ret; X if ((leaf->specs.st_mode & S_IFMT) != S_IFDIR) X leaf = leaf->parent; X if (leaf) X ret = leaf->next; X else X ret = (filerec *) NULL; X if (ret) if (ret->freed) X ret = next_directory(ret); X return(ret); X} X X Xfilerec *next_specified_directory(leaf) Xfilerec *leaf; X{ X while (leaf = next_directory(leaf)) X if (leaf->specified) X return(leaf); X return ((filerec *) NULL); X} X X X Xfilerec *next_in_directory(leaf) Xfilerec *leaf; X{ X filerec *ret; X X if (leaf->next) X ret = leaf->next; X else if (((leaf->specs.st_mode & S_IFMT) != S_IFDIR) && leaf->parent) X ret = leaf->parent->dirs; X else X ret = (filerec *) NULL; X if (ret) if (ret->freed) X ret = next_in_directory(ret); X return (ret); X} X X X X Xfilerec *next_specified_in_directory(leaf) Xfilerec *leaf; X{ X while (leaf = next_in_directory(leaf)) X if (leaf->specified) X return(leaf); X return ((filerec *) NULL); X} X X X Xfilerec *first_in_directory(leaf) Xfilerec *leaf; X{ X filerec *ret; X X if ((leaf->specs.st_mode & S_IFMT) != S_IFDIR) X ret = (filerec *) NULL; X else if (leaf->files) X ret = leaf->files; X else if (leaf->dirs) X ret = leaf->dirs; X else X ret = (filerec *) NULL; X if (ret) if (ret->freed) X ret = next_in_directory(ret); X return(ret); X} X X Xfilerec *first_specified_in_directory(leaf) Xfilerec *leaf; X{ X leaf = first_in_directory(leaf); X if (! leaf) X return((filerec *) NULL); X X if (leaf->specified) X return(leaf); X else X leaf = next_specified_in_directory(leaf); X return (leaf); X} X X Xvoid print_paths_from(leaf) Xfilerec *leaf; X{ X char buf[MAXPATHLEN]; X X printf("%s\n", get_leaf_path(leaf, buf)); X if (leaf->dirs) X print_paths_from(leaf->dirs); X if (leaf->files) X print_paths_from(leaf->files); X if (leaf->next) X print_paths_from(leaf->next); X} X X Xvoid print_specified_paths_from(leaf) Xfilerec *leaf; X{ X char buf[MAXPATHLEN]; X X if (leaf->specified) X printf("%s\n", get_leaf_path(leaf, buf)); X if (leaf->dirs) X print_specified_paths_from(leaf->dirs); X if (leaf->files) X print_specified_paths_from(leaf->files); X if (leaf->next) X print_specified_paths_from(leaf->next); X} X X Xint add_file_to_parent(parent, name, specified, last) Xfilerec *parent, **last; Xchar *name; XBoolean specified; X{ X filerec *files; X X *last = files = (filerec *) NULL; X files = parent->files; X while (files) { X if (! strcmp(files->name, name)) X break; X *last = files; X files = files->next; X } X if (files) { X files->specified = (files->specified || specified); X *last = files; X return 0; X } X if (*last) { X (*last)->next = (filerec *) Malloc((unsigned) sizeof(filerec)); X if (! (*last)->next) { X set_error(errno); X error("Malloc"); X return error_code; X } X *(*last)->next = default_file; X (*last)->next->previous = *last; X (*last)->next->parent = parent; X (*last) = (*last)->next; X } X else { X parent->files = (filerec *) Malloc(sizeof(filerec)); X if (! parent->files) { X set_error(errno); X error("Malloc"); X return error_code; X } X *parent->files = default_file; X parent->files->parent = parent; X parent->files->previous = (filerec *) NULL; X *last = parent->files; X } X (void) strcpy((*last)->name, name); X (*last)->specified = specified; X return 0; X} X X X X X Xint add_directory_to_parent(parent, name, specified, last) Xfilerec *parent, **last; Xchar *name; XBoolean specified; X{ X filerec *directories; X X *last = (filerec *) NULL; X directories = parent->dirs; X while (directories) { X if (! strcmp(directories->name, name)) X break; X (*last) = directories; X directories = directories->next; X } X if (directories) { X directories->specified = (directories->specified || specified); X *last = directories; X return 0; X } X if (*last) { X (*last)->next = (filerec *) Malloc(sizeof(filerec)); X if (! (*last)->next) { X set_error(errno); X error("Malloc"); X return error_code; X } X *(*last)->next = default_directory; X (*last)->next->previous = *last; X (*last)->next->parent = parent; X (*last) = (*last)->next; X } X else { X parent->dirs = (filerec *) Malloc(sizeof(filerec)); X if (! parent->dirs) { X set_error(errno); X error("Malloc"); X return error_code; X } X *parent->dirs = default_directory; X parent->dirs->parent = parent; X parent->dirs->previous = (filerec *) NULL; X (*last) = parent->dirs; X } X (void) strcpy((*last)->name, name); X (*last)->specified = specified; X return 0; X} X X X X X Xvoid free_leaf(leaf) Xfilerec *leaf; X{ X leaf->freed = True; X if (! (leaf->dirs || leaf->files)) { X if (leaf->previous) X leaf->previous->next = leaf->next; X if (leaf->next) X leaf->next->previous = leaf->previous; X if (leaf->parent) { X if ((leaf->specs.st_mode & S_IFMT) == S_IFDIR) { X if (leaf->parent->dirs == leaf) { X leaf->parent->dirs = leaf->next; X if (leaf->parent->freed) X free_leaf(leaf->parent); X } X } X else { X if (leaf->parent->files == leaf) { X leaf->parent->files = leaf->next; X if (leaf->parent->freed) X free_leaf(leaf->parent); X } X } X free((char *) leaf); X } X } X} X X X Xint find_child(directory, name, child) Xfilerec *directory, **child; Xchar *name; X{ X filerec *ptr; X X *child = (filerec *) NULL; X if ((directory->specs.st_mode & S_IFMT) != S_IFDIR) X return DIR_NOT_DIRECTORY; X ptr = directory->dirs; X while (ptr) X if (strcmp(ptr->name, name)) X ptr = ptr->next; X else X break; X if (ptr) { X *child = ptr; X return DIR_MATCH; X } X ptr = directory->files; X while (ptr) X if (strcmp(ptr->name, name)) X ptr = ptr->next; X else X break; X if (ptr) { X *child = ptr; X return DIR_MATCH; X } X set_status(DIR_NO_MATCH); X return DIR_NO_MATCH; X} X X X X X Xint change_path(old_path, new_path) Xchar *old_path, *new_path; X{ X char next_old[MAXNAMLEN], next_new[MAXNAMLEN]; X char rest_old[MAXPATHLEN], rest_new[MAXPATHLEN]; X int retval; X filerec *current; X X if (*old_path == '/') { X current = &root_tree; X old_path++; X new_path++; X } X else if (! strncmp(old_path, "./", 2)) { X current = &cwd_tree; X old_path += 2; X new_path += 2; X } X else X current = &cwd_tree; X X (void) strcpy(next_old, firstpart(old_path, rest_old)); X (void) strcpy(next_new, firstpart(new_path, rest_new)); X while (*next_old && *next_new) { X retval = find_child(current, next_old, ¤t); X if (retval == DIR_MATCH) { X if (current) { X (void) strcpy(current->name, next_new); X current->specified = False; X } X else { X set_error(INTERNAL_ERROR); X error("change_path"); X return error_code; X } X } X else { X error("change_path"); X return retval; X } X X (void) strcpy(next_old, firstpart(rest_old, rest_old)); X (void) strcpy(next_new, firstpart(rest_new, rest_new)); X } X return 0; X} X X Xint get_leaf_path(leaf, leaf_buf) Xfilerec *leaf; Xchar leaf_buf[]; /* RETURN */ X{ X char *name_ptr; X X name_ptr = Malloc(1); X if (! name_ptr) { X set_error(errno); X error("Malloc"); X *leaf_buf = '\0'; X return error_code; X } X *name_ptr = '\0'; X do { X name_ptr = realloc((char *) name_ptr, (unsigned) X (strlen(leaf->name) + strlen(name_ptr) + 2)); X if (! name_ptr) { X set_error(errno); X *leaf_buf = '\0'; X error("realloc"); X return error_code; X } X (void) strcpy(leaf_buf, name_ptr); X *name_ptr = '\0'; X if (leaf->parent) if (leaf->parent->parent) X (void) strcat(name_ptr, "/"); X (void) strcat(name_ptr, leaf->name); X (void) strcat(name_ptr, leaf_buf); X leaf = leaf->parent; X } while (leaf); X (void) strcpy(leaf_buf, name_ptr); X return 0; X} X X X X X Xint accumulate_names(leaf, in_strings, num) Xfilerec *leaf; Xchar ***in_strings; Xint *num; X{ X char newname[MAXPATHLEN]; X char **strings; X int retval; X X strings = *in_strings; X if (leaf->specified) { X *num += 1; X strings = (char **) realloc((char *) strings, (unsigned) X (sizeof(char *) * (*num))); X if (! strings) { X set_error(errno); X error("realloc"); X return error_code; X } X if (retval = get_leaf_path(leaf, newname)) { X error("get_leaf_path"); X return retval; X } X (void) convert_to_user_name(newname, newname); X strings[*num - 1] = Malloc((unsigned) (strlen(newname) + 1)); X if (! strings[*num - 1]) { X set_error(errno); X error("Malloc"); X return error_code; X } X (void) strcpy(strings[*num - 1], newname); X } X if (leaf->files) if (retval = accumulate_names(leaf->files, &strings, X num)) { X error("accumulate_names"); X return retval; X } X if (leaf->dirs) if (retval = accumulate_names(leaf->dirs, &strings, X num)) { X error("accumulate_names"); X return retval; X } X if (leaf->next) if (retval = accumulate_names(leaf->next, &strings, X num)) { X error("accumulate_names"); X return retval; X } X X *in_strings = strings; X return 0; X} END_OF_FILE if test 14682 -ne `wc -c <'directories.c'`; then echo shar: \"'directories.c'\" unpacked with wrong size! fi # end of 'directories.c' fi if test -f 'pattern.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'pattern.c'\" else echo shar: Extracting \"'pattern.c'\" \(23196 characters\) sed "s/^X//" >'pattern.c' <<'END_OF_FILE' X/* X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/pattern.c,v $ X * $Author: jik $ X * X * This program is part of a package including delete, undelete, X * lsdel, expunge and purge. The software suite is meant as a X * replacement for rm which allows for file recovery. X * X * Copyright (c) 1989 by the Massachusetts Institute of Technology. X * For copying and distribution information, see the file "mit-copyright.h." X */ X X#if (!defined(lint) && !defined(SABER)) X static char rcsid_pattern_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/pattern.c,v 1.16 90/01/11 04:11:58 jik Exp $"; X#endif X X#include <stdio.h> X#include <sys/types.h> X#include <sys/dir.h> X#include <sys/param.h> X#include <strings.h> X#include <sys/stat.h> X#include <errno.h> X#include <com_err.h> X#include "directories.h" X#include "pattern.h" X#include "util.h" X#include "undelete.h" X#include "shell_regexp.h" X#include "mit-copyright.h" X#include "delete_errs.h" X#include "errors.h" X#include "stack.h" X Xextern char *realloc(); Xextern int errno; Xextern char *whoami; X Xvoid free_list(); X X X/* X * add_arrays() takes pointers to two arrays of char **'s and their X * lengths, merges the two into the first by realloc'ing the first and X * then free's the second's memory usage. X */ Xint add_arrays(array1, num1, array2, num2) Xchar ***array1, ***array2; Xint *num1, *num2; X{ X int counter; X X *array1 = (char **) realloc((char *) *array1, (unsigned) X (sizeof(char *) * (*num1 + *num2))); X if (! *array1) { X set_error(errno); X error("realloc"); X return error_code; X } X for (counter = *num1; counter < *num1 + *num2; counter++) X *(*array1 + counter) = *(*array2 + counter - *num1); X free ((char *) *array2); X *num1 += *num2; X return 0; X} X X X X X X X/* X * Add a string to a list of strings. X */ Xint add_str(strs, num, str) Xchar ***strs; Xint num; Xchar *str; X{ X char **ary; X X ary = *strs = (char **) realloc((char *) *strs, (unsigned) X (sizeof(char *) * (num + 1))); X if (! *strs) { X set_error(errno); X error("realloc"); X return error_code; X } X ary[num] = Malloc((unsigned) (strlen(str) + 1)); X if (! ary[num]) { X set_error(errno); X error("Malloc"); X return error_code; X } X (void) strcpy(ary[num], str); X return 0; X} X X X X X X/* X * Find_matches will behave unpredictably if you try to use it to find X * very strange combinations of file types, for example only searching X * for undeleted files in the top-level directory, while searching X * recursively for deleted files. Basically, there are some conflicts X * between options that I don't list here because I don't think I'll X * ever need to use those combinations. X */ X/* X * Function: find_matches(char *name, int *num_found, char ***found, X * int options) X * X * Requires: name points to a NULL-terminated string, representing a X * filename pattern with regular filename characters, path X * separators and shell wildcard characters []*?; num_found points X * to a valid int memory storage location; found points to a valid X * char ** memory storage location. X * X * Effects: Returns a list of all the files in the file hierarchy that X * match the options specified in options and that match name. X * Options are: X * X * FIND_UNDELETED search for and return undeleted files X * X * FIND_DELETED search for and return deleted files X * X * FIND_CONTENTS means that if matches are directories (or links to X * directories), the contents of the directory should be matched X * in addition to the directory itself X * X * RECURS_FIND_DELETED to search all undeleted subdirectories X * recursively of matched directories looking for deleted files X * X * RECURS_FIND_UNDELETED to search all undeleted subdirectories X * recursively of matched directories looking for undeleted files X * X * RECURS_DELETED to recursively return all contents of deleted X * directories in addition to the directories themselves X * X * FOLLW_LINKS to pursue symlinks to directories and continue down X * the referenced directories when searching recursively (if the X * initial string is an undeleted symlink it is always traversed; X * deleted symlinks are never traversed) X * X * FOLLW_MOUNTPOINTS to traverse mount points when searching X * recursively (if the initial string is a mountpoint it is always X * traversed) X * X * FIND_DOTFILES forces the system to recognize dot files instead of X * discarding them when looking for files X * X * If the first character of name is '/', the search is conducted X * absolutely from the root of the hierarchy; else, it is conducted X * relative to the current working directory. The number of X * matching files is returned in *num_found, and a list of file X * names is returned in *found. If there are no errors, the return X * value is 0; else the return value represents the error code of X * the error which occurred. No matter how many file names are X * returned, the memory location addressed in *found is a valid X * pointer according to Malloc() and can be released using free() X * safely. However, if an error value is returned, the caller X * should not attempt to use the values stored in *num_found or X * *found. X * X * Modifies: *num_found, *found. X */ Xint find_matches(name, num_found, found, options) Xchar *name; Xint *num_found; Xchar ***found; Xint options; X{ X char **matched_files, **return_files, **recurs_files; X int num_matched_files = 0, num_return_files = 0, X num_recurs_files = 0; X int retval; X int i; X#ifdef DEBUG X int j; X#endif X int match_options = 0; X X#ifdef DEBUG X fprintf(stderr, "Entering find_matches, name = %s, options = %d.\n", X name, options); X#endif X X match_options = options & (FIND_DELETED | FIND_UNDELETED); X if (options & (RECURS_FIND_DELETED | RECURS_FIND_UNDELETED | X FIND_CONTENTS)) X match_options |= FIND_UNDELETED; X X if (! match_options) { X set_error(PAT_NO_FILES_REQUESTED); X error("find_matches"); X return error_code; X } X X retval = do_match(name, &num_matched_files, &matched_files, X match_options & FIND_UNDELETED, X match_options & FIND_DELETED); X if (retval) { X error(name); X return retval; X } X if (num_matched_files == 0) { X *num_found = num_matched_files; X *found = matched_files; X#ifdef DEBUG X fprintf(stderr, "No matches found, returning.\n"); X#endif X return 0; X } X X#ifdef DEBUG X fprintf(stderr, "The following matches were found:\n"); X for (i = 0; i < num_matched_files; i++) X fprintf(stderr, " %s\n", matched_files[i]); X#endif X X if (options & RECURS) { X return_files = (char **) Malloc(0); X if (! return_files) { X set_error(errno); X error("Malloc"); X return error_code; X } X num_return_files = 0; X X for (i = 0; i < num_matched_files; i++) { X X retval = do_recurs(matched_files[i], &num_recurs_files, X &recurs_files, options); X if (retval) { X error(matched_files[i]); X return retval; X } X X if (num_recurs_files) { X retval = add_arrays(&return_files, &num_return_files, X &recurs_files, &num_recurs_files); X if (retval) { X error("add_arrays"); X return retval; X } X#ifdef DEBUG X fprintf(stderr, X "Just added the following to return_files:\n"); X for (j = num_return_files - num_recurs_files; X j < num_return_files; j++) X fprintf(stderr, " %s\n", return_files[j]); X#endif X } X X if (is_deleted(lastpart(matched_files[i]))) { X if (options & FIND_DELETED) { X retval = add_str(&return_files, num_return_files, X matched_files[i]); X if (retval) { X error("add_str"); X return retval; X } X num_return_files++; X#ifdef DEBUG X fprintf(stderr, "Just added %s to return_files.\n", X return_files[num_return_files-1]); X#endif X } X } X else if (options & FIND_UNDELETED) { X retval = add_str(&return_files, num_return_files, X matched_files[i]); X if (retval) { X error("add_str"); X return retval; X } X num_return_files++; X#ifdef DEBUG X fprintf(stderr, "Just added %s to return_files.\n", X return_files[num_return_files-1]); X#endif X } X } X free_list(matched_files, num_matched_files); X *num_found = num_return_files; X *found = return_files; X } X else { X *num_found = num_matched_files; X *found = matched_files; X } X X return 0; X} X X X X X X X X X#define string_push(str)\ X strsize = strlen(str);\ X retval = push(str, strsize);\ X if (! retval)\ X retval |= push(&strsize, sizeof(int));\ X if (retval) {\ X error("push");\ X (void) popall();\ X return retval;\ X } X#define string_pop(str)\ X retval = pop(&strsize, sizeof(int));\ X if (! retval)\ X retval = pop(str, strsize);\ X if (! retval)\ X str[strsize] = '\0' X X X X X X X/* X * Function: do_match(char *name, int *num_found, char ***found, X * Boolean match_undeleted, Boolean match_deleted) X * X * Requires: name points to a NULL-terminated string, representing a X * filename pattern with regular filename characters, path X * separators and shell wildcard characters []*?; num_found points X * to a valid int memory storage location; found points to a valid X * char ** memory storage location. X * X * Effects: Returns a list of all the files in the file hierarchy that X * match name. If match_undeleted is true, will return undeleted X * files that match; if match_deleted is true, will return X * deleted_files that match. If the first character of name is '/', X * the search is conducted absolutely from the root of the X * hierarchy; else, it is conducted relative to the current working X * directory. The number of matching files is returned in X * *num_found, and a list of file names is returned in *found. If X * there are no errors, the return value is 0; else the return value X * represents the error code of the error which occurred. No matter X * how many file names are returned, the memory location addressed X * in *found is a valid pointer according to Malloc() and can be X * released using free() safely. However, if an error value is X * returned, the caller should not attempt to use the values stored X * in *num_found or *found. X * X * Modifies: *num_found, *found. X * X * Algorithm: X * X * start: X * base = "" or "/", X * name = name or name + 1 X * initialze found and num_found X * dirp = Opendir(base) X * first = firstpart(name, rest) (assigns rest as side-effect) X * if (! *first) { X * add string to list if appropriate X * return X * X * loop: X * dp = readdir(dirp) X * if (! dp) goto updir X * compare dp->d_name to first -- match? X * yes - goto downdir X * no - are we looking for deleted and is dp->d_name deleted? X * yes - compare undeleted dp->d_name to first -- match? X * yes - goto downdir X * no - goto loop X * no - goto loop X * X * downdir: X * save dirp, rest, first and base on stack X * first = firstpart(rest, rest) X * base = dp->d_name appended to base X * is first an empty string? X * yes - put back dirp, rest, first, base X * goto loop X * try to open dir base - opens? X * yes - goto loop X * no - is the error ENOTDIR? X * yes - don't worry about it X * no - report the error X * restore dirp, rest, first, base from stack X * goto loop X * X * updir: X * close dirp X * restore base, rest, first from stack X * STACK_EMPTY? X * yes - return from procedure with results X * restore dirp from stack X * goto loop X */ Xint do_match(name, num_found, found, match_undeleted, match_deleted) Xchar *name; Xint *num_found; Xchar ***found; XBoolean match_undeleted, match_deleted; X{ X char base[MAXPATHLEN]; X struct direct *dp; X DIR *dirp; X char first[MAXNAMLEN], rest[MAXPATHLEN]; X int retval; X int strsize; X X#ifdef DEBUG X printf("do_match: looking for %s\n", name); X#endif X X /* start: */ X X if (*name == '/') { X *base = '/'; X *(base + 1) = '\0'; X name++; X } X else X *base = '\0'; X X *found = (char **) Malloc(0); X if (! *found) { X set_error(errno); X error("Malloc"); X return error_code; X } X *num_found = 0; X X dirp = Opendir(base); X if (! dirp) { X set_error(errno); X error(base); X return error_code; X } X (void) strcpy(first, firstpart(name, rest)); X if ((! *first) && (match_undeleted)) { X retval = add_str(found, *num_found, base); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X return 0; X } X X while (1) { X dp = readdir(dirp); X if (! dp) goto updir; X X retval = reg_cmp(first, dp->d_name); X if (retval < 0) { X error("reg_cmp"); X goto updir; X } X X if (retval == REGEXP_MATCH) goto downdir; X X if (is_deleted(dp->d_name) && match_deleted) { X retval = reg_cmp(first, &dp->d_name[2]); X if (retval < 0) { X error("reg_cmp"); X goto updir; X } X X if (retval == REGEXP_MATCH) X goto downdir; X else X continue; X } X else X continue; X X downdir: X retval = push(&dirp, sizeof(DIR *)); X if (retval) { X error("push"); X (void) popall(); X return retval; X } X string_push(first); X string_push(rest); X string_push(base); X (void) strcpy(base, append(base, dp->d_name)); X (void) strcpy(first, firstpart(rest, rest)); X if (! *first) { X if (is_deleted(lastpart(base))) { X if (match_deleted) { X retval = add_str(found, *num_found, base); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X } X } X else if (match_undeleted) { X retval = add_str(found, *num_found, base); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X } X string_pop(base); X string_pop(rest); X string_pop(first); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X X dirp = Opendir(base); X if (! dirp) { X if (errno != ENOTDIR) { X set_error(errno); X error(base); X } X string_pop(base); X string_pop(rest); X string_pop(first); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X else X continue; X X updir: X closedir(dirp); X string_pop(base); X if (retval) { X if (retval != STACK_EMPTY) { X error("pop"); X (void) popall(); X return retval; X } X return 0; X } X string_pop(rest); X string_pop(first); X retval = pop(&dirp, sizeof(DIR *)); X if (retval) { X error("pop"); X (void) popall(); X return retval; X } X continue; X } X} X X X X X X X/* X * Function: do_recurs(char *name, int *num_found, char ***found, X * int options) X * X * Requires: name points to a NULL-terminated string, representing a X * filename pattern with regular filename characters, path X * separators and shell wildcard characters []*?; num_found points X * to a valid int memory storage location; found points to a valid X * char ** memory storage location. X * X * Effects: Returns a list of all the files in the file hierarchy that X * are underneath the specified file, governed by the options set in X * options. Options are as described in the find_matches() description. X * RECURS_FIND_DELETED and RECURS_DELETED imply FIND_DELETED. X * RECURS_FIND_UNDELETED implies FIND_UNDELETED. X * X * Modifies: *num_found, *found. X * X * Algorithm: X * X * start: X * initialze found and num_found X * strcopy(base, name) X * dirp = Opendir(base) X * check if we just opened a deleted symlink and return if we did X * check RECURS options and set FIND options as appropriate X * X * loop: X * dp = readdir(dirp) X * if (! dp) goto updir X * is dp deleted? X * yes - is FIND_DELETED set? X * yes - add to list X * is RECURS_DELETED set? X * yes - goto downdir X * no - goto loop X * no - goto loop X * no - is FIND_UNDELETED set? X * yes - is the file a dotfile? X * yes - is FIND_DOTFILES set? X * yes - add to list X * goto loop X * no - add to list X * are RECURS_FIND_DELETED and FIND_DELETED set? X * yes - goto downdir X * is RECURS_FIND_UNDELETED set? X * yes - goto downdir X * no - goto loop X * no - goto loop X * X * downdir: X * save dirp, base on stack X * base = dp->d_name appended to base X * try to open base -- opens? X * yes - is FOLLW_LINKS set? X * yes - is it deleted? X * yes - is it a link? X * yes - close the directory X * restore base and dirp X * goto loop X * no - is it a link? X * yes - close the directory X * restore base and dirp X * goto loop X * is FOLLW_MOUNTPOINTS set? X * no - is it a mountpoint? X * yes - close the directory X * restore base and dirp X * goto loop X * no - is the error ENOTDIR? X * yes - don't worry about it X * no - report the error X * restore base and dirp X * goto loop X * X * updir: X * close dirp X * restore base from stack X * STACK_EMPTY? X * yes - return from procedure with results X * restore dirp from stack X * goto loop X */ Xint do_recurs(name, num_found, found, options) Xchar *name; Xint *num_found; Xchar ***found; Xint options; X{ X char base[MAXPATHLEN]; X struct direct *dp; X DIR *dirp; X int retval; X int strsize; X struct stat statbuf; X int use_stat; X X#ifdef DEBUG X fprintf(stderr, "do_recurs: opening %s\n", name); X#endif X X /* start: */ X X *found = (char **) Malloc(0); X if (! *found) { X set_error(errno); X error("Malloc"); X return error_code; X } X *num_found = 0; X strcpy(base, name); X X /* Never follow deleted symlinks */ X if (is_deleted(lastpart(base)) && is_link(base, (struct stat *) NULL)) { X return 0; X } X X dirp = Opendir(base); X if (! dirp) { X /* If the problem is that it isn't a directory, just return */ X /* with zero matches -- the file exists, but cannot be */ X /* recursively searched. Otherwise, actually signal an */ X /* error. */ X if (errno != ENOTDIR) { X#ifdef DEBUG X fprintf(stderr, "Couldn't open %s.\n", base); X#endif X set_error(errno); X error(base); X return error_code; X } X else X return 0; X } X X if (options & (RECURS_FIND_DELETED | RECURS_DELETED)) X options |= FIND_DELETED; X if (options & RECURS_FIND_UNDELETED) X options |= FIND_UNDELETED; X X while (1) { X dp = readdir(dirp); X if (! dp) goto updir; X X if (is_deleted(dp->d_name)) { X if (options & FIND_DELETED) { X retval = add_str(found, *num_found, X append(base, dp->d_name)); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X if (options & RECURS_DELETED) X goto downdir; X else X continue; X } X else X continue; X } X X if (options & FIND_UNDELETED) { X if (is_dotfile(dp->d_name)) { X if (options & FIND_DOTFILES) { X retval = add_str(found, *num_found, X append(base, dp->d_name)); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X } X continue; X } X else { X retval = add_str(found, *num_found, X append(base, dp->d_name)); X if (retval) { X error("add_str"); X (void) popall(); X return retval; X } X (*num_found)++; X } X } X X if (! is_dotfile(dp->d_name)) { X if (options & RECURS_FIND_DELETED) X goto downdir; X if (options & RECURS_FIND_UNDELETED) X goto downdir; X } X X continue; X X X downdir: X retval = push(&dirp, sizeof(DIR *)); X if (retval) { X error("push"); X (void) popall(); X return retval; X } X string_push(base); X (void) strcpy(base, append(base, dp->d_name)); X X /* X * Originally, I did an Opendir() right at the start and X * then only checked things if the Opendir resulted in an X * error. However, this is inefficient, because the X * Opendir() procedure works by first calling open() on the X * file, and *then* calling fstat on the file descriptor X * that is returned. since most of the time we will be X * trying to open things that are not directory, it is much X * more effecient to do the stat first here and to do the X * Opendir only if the stat results are satisfactory. X */ X use_stat = (options & FOLLW_LINKS) && (! is_deleted(lastpart(base))); X if (use_stat) X retval = stat(base, &statbuf); X else X retval = lstat(base, &statbuf); X if (retval == -1) { X set_error(errno); X error(base); X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X /* It's not a directory, so punt it and continue. */ X if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X X /* Actually try to open it. */ X dirp = Opendir(base); X if (! dirp) { X set_error(errno); X error(base); X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X X if (! (options & FOLLW_MOUNTPOINTS)) { X if (is_mountpoint(base, use_stat ? (struct stat *) NULL : X &statbuf)) { X closedir(dirp); X set_warning(PAT_IS_MOUNT); X error(base); X string_pop(base); X (void) pop(&dirp, sizeof(DIR *)); X continue; X } X#ifdef DEBUG X else { X fprintf(stderr, X "do_recurs: %s isn't a mountpoint, following.\n", X base); X } X#endif X } X#ifdef DEBUG X printf("do_recurs: opening %s\n", base); X#endif X continue; X X updir: X closedir(dirp); X string_pop(base); X if (retval) { X if (retval != STACK_EMPTY) { X error("pop"); X (void) popall(); X return retval; X } X return 0; X } X retval = pop(&dirp, sizeof(DIR *)); X if (retval) { X error("pop"); X (void) popall(); X return retval; X } X continue; X } X} X X Xvoid free_list(list, num) Xchar **list; Xint num; X{ X int i; X X for (i = 0; i < num; i++) X free(list[i]); X X free((char *) list); X} X X X X X X X/* X * returns true if the filename has no globbing wildcards in it. That X * means no non-quoted question marks, asterisks, or open square X * braces. Assumes a null-terminated string, and a valid globbing X */ Xint no_wildcards(name) Xchar *name; X{ X do { X switch (*name) { X case '\\': X name++; X break; X case '?': X return(0); X case '*': X return(0); X case '[': X return(0); X } X } while (*++name); X return(1); X} END_OF_FILE if test 23196 -ne `wc -c <'pattern.c'`; then echo shar: \"'pattern.c'\" unpacked with wrong size! fi # end of 'pattern.c' fi echo shar: End of archive 3 \(of 3\). cp /dev/null ark3isdone MISSING="" for I in 1 2 3 ; do if test ! -f ark${I}isdone ; then MISSING="${MISSING} ${I}" fi done if test "${MISSING}" = "" ; then echo You have unpacked all 3 archives. rm -f ark[1-9]isdone else echo You still need to unpack the following archives: echo " " ${MISSING} fi ## End of shell archive. exit 0 exit 0 # Just in case... -- Please send comp.sources.unix-related mail to rsalz@uunet.uu.net. Use a domain-based address or give alternate paths, or you may lose out.