[comp.sources.unix] v18i077: MIT Athena delete/undelete programs, Part05/06

rsalz@uunet.uu.net (Rich Salz) (03/29/89)

Submitted-by: Jonathan I. Kamens <jik@PIT-MANAGER.MIT.EDU>
Posting-number: Volume 18, Issue 77
Archive-name: undel/part05

#! /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 5 (of 6)."
# Contents:  undelete.c
# Wrapped by jik@pit-manager on Mon Mar 27 12:16:54 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'undelete.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'undelete.c'\"
else
echo shar: Extracting \"'undelete.c'\" \(12143 characters\)
sed "s/^X//" >'undelete.c' <<'END_OF_FILE'
X/*
X * $Source: /mit/jik/src/delete/RCS/undelete.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_undelete_c[] = "$Header: undelete.c,v 1.15 89/03/27 12:08:08 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 "directories.h"
X#include "pattern.h"
X#include "util.h"
X#include "undelete.h"
X#include "mit-copyright.h"
X
X#define ERROR_MASK 1
X#define NO_DELETE_MASK 2
X
Xchar *malloc(), *realloc();
X
Xint interactive, recursive, verbose, directoriesonly, noop, force;
Xint del_recursive = 0; /* this tells the pattern matcher that we do */
X		       /* *not* want it to recurse deleted directories */
X		       /* when recursive is set to false. */
X
Xchar *whoami, *error_buf;
X
X
X
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X     extern char *optarg;
X     extern int optind;
X     int arg;
X     int status = 0;
X     
X     whoami = lastpart(argv[0]);
X     interactive = recursive = verbose = directoriesonly = noop = force = 0;
X     error_buf = malloc(MAXPATHLEN + strlen(whoami));
X     if (! error_buf) {
X	  perror(whoami);
X	  exit(1);
X     }
X     while ((arg = getopt(argc, argv, "firvnR")) != -1) {
X	  switch (arg) {
X	  case 'f':
X	       force++;
X	       break;
X	  case 'i':
X	       interactive++;
X	       break;
X	  case 'r':
X	       recursive++;
X	       if (directoriesonly) {
X		    fprintf(stderr, "%s: -r and -R and mutually exclusive.\n",
X			    whoami);
X		    usage();
X		    exit(1);
X	       }
X	       break;
X	  case 'v':
X	       verbose++;
X	       break;
X	  case 'n':
X	       noop++;
X	       break;
X	  case 'R':
X	       directoriesonly++;
X	       if (recursive) {
X		    fprintf(stderr, "%s: -r and -R are mutually exclusive.\n",
X			    whoami);
X		    usage();
X		    exit(1);
X	       }
X	  default:
X	       usage();
X	       exit(1);
X	  }
X     }
X     if (optind == argc)
X	  exit(interactive_mode());
X     else while (optind < argc) {
X	  status = status | undelete(argv[optind]);
X	  optind++;
X     }
X     exit(status & ERROR_MASK);
X}
X
X
X
Xinteractive_mode()
X{
X     char buf[MAXPATHLEN];
X     char *ptr;
X     int status = 0;
X
X     if (verbose) {
X	  printf("Enter the files to be undeleted, one file per line.\n");
X	  printf("Hit <RETURN> on a line by itself to exit.\n\n");
X     }
X     do {
X	  printf("%s: ", whoami);
X	  ptr = fgets(buf, MAXPATHLEN, stdin);
X	  if (! ptr) {
X	       printf("\n");
X	       return(status);
X	  }
X	  ptr = index(buf, '\n');  /* fgets breakage */
X	  if (ptr)
X	       *ptr = '\0';
X	  if (! *buf)
X	       return(status);
X	  status = status | undelete(buf);
X     } while (*ptr);
X     return(status);
X}
X
X
X
Xusage()
X{
X     fprintf(stderr, "Usage: %s [ options ] [filename ...]\n", whoami);
X     fprintf(stderr, "Options are:\n");
X     fprintf(stderr, "     -r     recursive\n");
X     fprintf(stderr, "     -i     interactive\n");
X     fprintf(stderr, "     -f     force\n");
X     fprintf(stderr, "     -v     verbose\n");
X     fprintf(stderr, "     -n     noop\n");
X     fprintf(stderr, "     -R     directories only (i.e. no recursion)\n");
X     fprintf(stderr, "     --     end options and start filenames\n");
X     fprintf(stderr, "-r and -D are mutually exclusive\n");
X}
X
X
Xundelete(file_exp)
Xchar *file_exp;
X{
X     char *file_re;
X     char **found_files;
X     int num_found;
X     char *startdir;
X     int status = 0;
X     filerec *current;
X     
X     if (*file_exp == '/') {
X	  startdir = "/";
X	  file_re = parse_pattern(file_exp + 1);
X     }
X     else {
X	  startdir = "";
X	  file_re = parse_pattern(file_exp);
X     }
X     if (! file_re)
X	  return(ERROR_MASK);
X     found_files = get_the_files(startdir, file_re, &num_found);
X     free(file_re);
X     if (num_found) {
X	  process_files(found_files, num_found);
X	  if (*file_exp == '/')
X	       current = get_root_tree();
X	  else
X	       current = get_cwd_tree();
X	  status = recurs_and_undelete(current);
X     }
X     else {
X	  if (! force)
X	       fprintf(stderr, "%s: %s not found\n", whoami, file_exp);
X	  status = ERROR_MASK;
X     }
X     return(status);
X}
X
X
X
X
X
Xrecurs_and_undelete(leaf)
Xfilerec *leaf;
X{
X     int status = 0;
X
X     if ((leaf->specified) && ((leaf->specs.st_mode & S_IFMT) == S_IFDIR))
X	  status = do_directory_undelete(leaf);
X     /* the "do_directory_undelete" really only asks the user if he */
X     /* wants to expunge the directory, it doesn't do any deleting. */
X     if (! status) {
X	  if (leaf->dirs)
X	       status |= recurs_and_undelete(leaf->dirs);
X	  if (leaf->files)
X	       status |= recurs_and_undelete(leaf->files);
X     }
X     if (leaf->specified)
X	  status |= do_undelete(leaf);
X     if (leaf->next)
X	  status |= recurs_and_undelete(leaf->next);
X     free_leaf(leaf);
X     return(status);
X}
X
X
X
X
X
X
Xdo_directory_undelete(file_ent)
Xfilerec *file_ent;
X{
X     char buf[MAXPATHLEN];
X
X     get_leaf_path(file_ent, buf);
X     convert_to_user_name(buf, buf);
X     
X     if (interactive) {
X	  printf("%s: Undelete directory %s? ", whoami, buf);
X	  if (! yes())
X	       return(NO_DELETE_MASK);
X     }
X     return(0);
X}
X
X
X
X
X
Xprocess_files(files, num)
Xchar **files;
Xint num;
X{
X     int i;
X     listrec *new_files;
X     listrec *filelist;
X
X     filelist = (listrec *) malloc(sizeof(listrec) * num);
X     if (! filelist) {
X	  perror(sprintf(error_buf, "%s: process_files\n", whoami));
X	  exit(1);
X     }
X     for (i = 0; i < num; i++) {
X	  filelist[i].real_name = malloc(strlen(files[i]) + 1);
X	  strcpy(filelist[i].real_name, files[i]);
X	  filelist[i].user_name = malloc(strlen(files[i]) + 1);
X	  convert_to_user_name(files[i], filelist[i].user_name);
X	  free(files[i]);
X     }
X     free(files);
X     
X     new_files = sort_files(filelist, num);
X     new_files = unique(new_files, &num);
X     if (initialize_tree()) {
X	  exit(1);
X     }
X     for (i = 0; i < num; i++) {
X	  if (!add_path_to_tree(new_files[i].real_name)) {
X	       fprintf(stderr, "%s: error adding path to filename tree\n",
X		       whoami);
X	       exit(1);
X	  }
X	  else {
X	       free(new_files[i].real_name);
X	       free(new_files[i].user_name);
X	  }
X     }
X     free(new_files);
X     return(0);
X}
X
X     
X
X
X
X
X
X     
Xdo_undelete(file_ent)
Xfilerec *file_ent;
X{
X     struct stat stat_buf;
X     char user_name[MAXPATHLEN], real_name[MAXPATHLEN];
X
X     get_leaf_path(file_ent, real_name);
X     convert_to_user_name(real_name, user_name);
X
X     if (interactive) {
X	  if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
X	       printf("%s: Undelete directory %s? ", whoami, user_name);
X	  else
X	       printf("%s: Undelete %s? ", whoami, user_name);
X	  if (! yes())
X	       return(NO_DELETE_MASK);
X     }
X     if (! lstat(user_name, &stat_buf)) if (! force) {
X	  printf("%s: An undeleted %s already exists.\n", whoami, user_name);
X	  printf("Do you wish to continue with the undelete and overwrite that version? ");
X	  if (! yes())
X	       return(NO_DELETE_MASK);
X	  unlink_completely(user_name);
X     }
X     if (noop) {
X	  printf("%s: %s would be undeleted\n", whoami, user_name);
X	  return(0);
X     }
X
X     if (! do_file_rename(real_name, user_name)) {
X	  if (verbose)
X	       printf("%s: %s undeleted\n", whoami, user_name);
X	  return(0);
X     }
X     else {
X	  if (! force)
X	       fprintf(stderr, "%s: %s not undeleted\n", whoami, user_name);
X	  return(ERROR_MASK);
X     }
X}
X
X
X
X
Xdo_file_rename(real_name, user_name)
Xchar *real_name, *user_name;
X{
X     char *ptr;
X     
X     char old_name[MAXPATHLEN], new_name[MAXPATHLEN];
X     char buf[MAXPATHLEN];
X     
X     strcpy(old_name, real_name);
X     strcpy(new_name, real_name);
X
X     while (ptr = strrindex(new_name, ".#")) {
X	  convert_to_user_name(ptr, ptr);
X	  strcpy(ptr, firstpart(ptr, buf));
X	  strcpy(&old_name[ptr - new_name],
X		 firstpart(&old_name[ptr - new_name], buf));
X	  if (rename(old_name, new_name)) {
X	       return(ERROR_MASK);
X	  }
X	  if (ptr > new_name) {
X	       *--ptr = '\0';
X	       old_name[ptr - new_name] = '\0';
X	  }
X     }
X     change_path(real_name, user_name);
X     return(0);
X}
X
X
X
X
X
X
Xfilecmp(file1, file2)
Xlistrec *file1, *file2;
X{
X     return(strcmp(file1->user_name, file2->user_name));
X}
X
X     
X     
Xlistrec *sort_files(data, num_data)
Xlistrec *data;
Xint num_data;
X{
X     qsort(data, num_data, sizeof(listrec), filecmp);
X     return(data);
X}
X
X
X
X
X
Xlistrec *unique(files, number)
Xlistrec *files;
Xint *number;
X{
X     int i, last;
X     int offset;
X     
X     for (last = 0, i = 1; i < *number; i++) {
X	  if (! strcmp(files[last].user_name, files[i].user_name)) {
X	       int better;
X
X	       better = choose_better(files[last].real_name,
X				      files[i].real_name);
X	       if (better == 1) { /* the first one is better */
X		    free (files[i].real_name);
X		    free (files[i].user_name);
X		    files[i].real_name = (char *) NULL;
X	       }
X	       else {
X		    free (files[last].real_name);
X		    free (files[last].user_name);
X		    files[last].real_name = (char *) NULL;
X		    last = i;
X	       }
X	  }
X	  else
X	       last = i;
X     }
X     
X     for (offset = 0, i = 0; i + offset < *number; i++) {
X	  if (! files[i].real_name)
X	       offset++;
X	  if (i + offset < *number)
X	       files[i] = files[i + offset];
X     }
X     *number -= offset;
X     files = (listrec *) realloc(files, sizeof(listrec) * *number);
X     if (! files) {
X	  perror(sprintf(error_buf, "%s: unique", whoami));
X	  exit(1);
X     }
X     return(files);
X}
X
X
X
X
Xchoose_better(str1, str2)
Xchar *str1, *str2;
X{
X     char *pos1, *pos2;
X     
X     pos1 = strindex(str1, ".#");
X     pos2 = strindex(str2, ".#");
X     while (pos1 && pos2) {
X	  if (pos1 - str1 < pos2 - str2)
X	       return(2);
X	  else if (pos2 - str2 < pos1 - str1)
X	       return(1);
X	  pos1 = strindex(pos1 + 1, ".#");
X	  pos2 = strindex(pos2 + 1, ".#");
X     }
X     if (! pos1)
X	  return(1);
X     else
X	  return(2);
X}
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     
X     if (lstat(filename, &stat_buf))
X	  return(1);
X
X     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
X	  dirp = opendir(filename);
X	  if (! dirp)
X	       return(1);
X	  for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
X	       if (is_dotfile(dp->d_name))
X		    continue;
X	       strcpy(buf, append(filename, dp->d_name));
X	       if (! buf) {
X		    status = 1;
X		    continue;
X	       }
X	       status = status | unlink_completely(buf);
X	  }
X	  closedir(dirp);
X     }
X     else
X	  return(unlink(filename) == -1);
X     return(0);
X}
X
X
X
X
Xchar **get_the_files(base, reg_exp, num_found)
Xchar *base, *reg_exp;
Xint *num_found;
X{
X     char **matches;
X     int num_matches;
X     char **found;
X     int num;
X     int i;
X     
X     found = (char **) malloc(0);
X     num = 0;
X     
X     matches = find_matches(base, reg_exp, &num_matches);
X     if (recursive) {
X	  char **recurs_found;
X	  int recurs_num;
X	  
X	  for (i = 0; i < num_matches; free(matches[i]), i++) {
X	       if (is_deleted(lastpart(matches[i]))) {
X		    found = add_str(found, num, matches[i]);
X		    num++;
X	       }
X	       recurs_found = find_deleted_recurses(matches[i], &recurs_num);
X	       add_arrays(&found, &num, &recurs_found, &recurs_num);
X	  }
X     }
X     else {
X	  struct stat stat_buf;
X	  char **contents_found;
X	  int num_contents;
X	  
X	  for (i = 0; i < num_matches; free(matches[i]), i++) {
X	       if (is_deleted(lastpart(matches[i]))) {
X		    found = add_str(found, num, matches[i]);
X		    num++;
X	       }
X	       else if (! directoriesonly) {
X		    if (lstat(matches[i], &stat_buf))
X			 continue;
X		    if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
X			 contents_found = find_deleted_contents(matches[i],
X								&num_contents);
X			 add_arrays(&found, &num, &contents_found,
X				    &num_contents);
X		    }
X	       }
X	  }
X	  
X     }
X     free(matches);
X     *num_found = num;
X     return(found);
X}
X
X			 
X		    
X	  
X	  
END_OF_FILE
if test 12143 -ne `wc -c <'undelete.c'`; then
    echo shar: \"'undelete.c'\" unpacked with wrong size!
fi
# end of 'undelete.c'
fi
echo shar: End of archive 5 \(of 6\).
cp /dev/null ark5isdone
MISSING=""
for I in 1 2 3 4 5 6 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 6 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

-- 
Please send comp.sources.unix-related mail to rsalz@uunet.uu.net.