[comp.sources.unix] v22i027: MIT Athena delete/undelete programs, release 2, Part03/03

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, &current);
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.