[comp.sources.unix] v22i026: MIT Athena delete/undelete programs, release 2, Part02/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 26
Archive-name: undel2/part02

#! /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 2 (of 3)."
# Contents:  col.c expunge.c lsdel.c undelete.c util.c
# Wrapped by rsalz@papaya.bbn.com on Mon May  7 16:54:00 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'col.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'col.c'\"
else
echo shar: Extracting \"'col.c'\" \(5054 characters\)
sed "s/^X//" >'col.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/col.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_col_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/col.c,v 1.6 89/11/22 21:24:21 jik Exp $";
X#endif
X
X/*
X * Note that this function has a lot of options I'm not really using
X * because I took it out of other code that needed a lot more
X * versatility.
X */
X
X#include <stdio.h>
X#include <strings.h>
X#include "errors.h"
X#include "delete_errs.h"
X#include "col.h"
X#include "mit-copyright.h"
X
X
Xstatic int calc_string_width(), calc_widths(), num_width();
Xstatic void trim_strings();
X
Xint column_array(strings, num_to_print, screen_width, column_width,
X		 number_of_columns, margin, spread_flag, 
X		 number_flag, var_col_flag, outfile)
Xchar **strings;
XFILE *outfile;
X{
X     char buf[BUFSIZ];
X     int updown, leftright, height;
X     int string_width;
X     int numwidth;
X
X     numwidth = num_width(num_to_print);
X     if (! var_col_flag) {
X	  string_width = calc_string_width(column_width, margin, number_flag,
X					   num_to_print);
X	  if (string_width <= 0) {
X	       set_error(COL_COLUMNS_TOO_THIN);
X	       error("calc_string_width");
X	       return error_code;
X	  }
X	  trim_strings(strings, num_to_print, string_width);
X     } else if (calc_widths(strings, &screen_width, &column_width,
X			    &number_of_columns, num_to_print, &margin,
X			    spread_flag, number_flag)) {
X	  error("calc_widths");
X	  return error_code;
X     }
X     height = num_to_print / number_of_columns;
X     if (num_to_print % number_of_columns)
X	  height++;
X     
X     if (number_flag) for (updown = 0; updown < height; updown++) {
X	  for (leftright = updown; leftright < num_to_print; ) {
X	       (void) sprintf(buf, "%*d. %s", numwidth, leftright+1,
X			      strings[leftright]);
X	       if ((leftright += height) >= num_to_print)
X		    fprintf(outfile, "%s", buf );
X	       else
X		    fprintf(outfile, "%*s", -column_width, buf);
X	  }
X	  fprintf(outfile, "\n");
X     } else for (updown = 0; updown < height; updown++) {
X	  for (leftright = updown; leftright < num_to_print; ) {
X	       (void) sprintf(buf, "%s", strings[leftright]);
X	       if ((leftright += height) >= num_to_print)
X		    fprintf(outfile, "%s", buf );
X	       else
X		    fprintf(outfile, "%*s", -column_width, buf);
X	  }
X	  fprintf(outfile, "\n");
X     }
X     return 0;
X}
X
Xstatic int calc_string_width(column_width, margin, number_flag, max_number)
X{
X     int string_width;
X     
X     string_width = column_width - margin;
X     if (number_flag)
X	  string_width = string_width - num_width(max_number) - strlen(". ");
X     return string_width;
X}
X
X
Xstatic void trim_strings(strings, number, width)
Xchar **strings;
X{
X     int loop;
X     
X     for (loop = 0; loop < number; loop++)
X	  if (strlen(strings[loop]) > width)
X	       strings[loop][width] = '\0';
X}
X
X
Xstatic int calc_widths(strings, screen_width, column_width, number_of_columns,
X		       num_to_print, margin, spread_flag, number_flag)
Xint *screen_width, *column_width, *number_of_columns, *margin;
Xchar **strings;
X{
X     int loop;
X     int maxlen, templen;
X     int spread;
X     
X     maxlen = templen = 0;
X     for (loop = 0; loop < num_to_print; loop++)
X	  if (maxlen < (templen = strlen(strings[loop])))
X	       maxlen = templen;
X
X     *column_width = maxlen;
X     
X     if (number_flag)
X	  *column_width = *column_width + num_width(num_to_print) +
X	       strlen(". ");
X
X     if (! spread_flag) {
X	  *column_width += *margin;
X	  if (! *number_of_columns) {
X	       *number_of_columns = *screen_width / *column_width;
X	       if (! *number_of_columns) {
X		    (*number_of_columns)++;
X		    *column_width -= *margin;
X		    *margin = 0;
X		    *screen_width = *column_width;
X	       }
X	  }
X	  else
X	       *screen_width = *number_of_columns * *column_width;
X     } else {
X	  if (! *number_of_columns) {
X	       *number_of_columns = *screen_width / (*column_width + *margin);
X	       if (! *number_of_columns) {
X		    (*number_of_columns)++;
X		    *screen_width = *column_width;
X		    *margin = 0;
X	       }
X	       spread = (*screen_width - *number_of_columns * *column_width)
X		    / *number_of_columns;
X	       *column_width += spread;
X	  }
X	  else {
X	       if (*number_of_columns * (*column_width + *margin) >
X		   *screen_width) {
X		    *column_width += *margin;
X		    *screen_width = *column_width;
X	       } else {
X		    spread = (*screen_width - (*number_of_columns *
X					       *column_width)) /
X						    *number_of_columns;
X		    *column_width += spread;
X	       }
X	  }
X     }
X     return 0;
X}
X
X
X	       
X
Xstatic int num_width(number)
Xint number;
X{
X     char buf[BUFSIZ];
X
X     (void) sprintf(buf, "%d", number);
X     return strlen(buf);
X}
END_OF_FILE
if test 5054 -ne `wc -c <'col.c'`; then
    echo shar: \"'col.c'\" unpacked with wrong size!
fi
# end of 'col.c'
fi
if test -f 'expunge.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'expunge.c'\"
else
echo shar: Extracting \"'expunge.c'\" \(11211 characters\)
sed "s/^X//" >'expunge.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/expunge.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_expunge_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/expunge.c,v 1.13 89/12/28 14:45:15 jik Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/dir.h>
X#include <sys/param.h>
X#include <strings.h>
X#include <sys/stat.h>
X#include <com_err.h>
X#include <errno.h>
X#include "col.h"
X#include "directories.h"
X#include "util.h"
X#include "pattern.h"
X#include "expunge.h"
X#include "shell_regexp.h"
X#include "mit-copyright.h"
X#include "delete_errs.h"
X#include "errors.h"
X
Xextern char *realloc();
Xextern time_t current_time;
Xextern int errno;
X
Xchar *whoami;
X
Xtime_t timev; 		/* minimum mod time before undeletion */
X
Xint  interactive,	/* query before each expunge */
X     recursive,		/* expunge undeleted directories recursively */
X     noop,		/* print what would be done instead of doing it */
X     verbose,		/* print a line as each file is deleted */
X     force,		/* do not ask for any confirmation */
X     listfiles,		/* list files at toplevel */
X     yield,		/* print yield of expunge at end */
X     f_links,		/* follow symbolic links */
X     f_mounts;		/* follow mount points */
X
Xint blocks_removed = 0;
X
X
X
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X     extern char *optarg;
X     extern int optind;
X     int arg;
X
X     initialize_del_error_table();
X     
X     whoami = lastpart(argv[0]);
X     if (*whoami == 'p') { /* we're doing a purge */
X	  if (argc > 1) {
X	       set_error(PURGE_TOO_MANY_ARGS);
X	       error("");
X	       exit(1);
X	  }
X	  if (purge())
X	       error("purge");
X	  exit(error_occurred ? 1 : 0);
X     }
X     timev = 0;
X     yield = interactive = recursive = noop = verbose = listfiles = force = 0;
X     while ((arg = getopt(argc, argv, "t:irfnvlysm")) != EOF) {
X	  switch (arg) {
X	  case 't':
X	       timev = atoi(optarg);
X	       break;
X	  case 'i':
X	       interactive++;
X	       break;
X	  case 'r':
X	       recursive++;
X	       break;
X	  case 'f':
X	       force++;
X	       break;
X	  case 'n':
X	       noop++;
X	       break;
X	  case 'v':
X	       verbose++;
X	       break;
X	  case 'l':
X	       listfiles++;
X	       break;
X	  case 'y':
X	       yield++;
X	       break;
X	  case 's':
X	       f_links++;
X	       break;
X	  case 'm':
X	       f_mounts++;
X	       break;
X	  default:
X	       usage();
X	       exit(1);
X	  }
X     }
X     report_errors = ! force;
X     
X     if (optind == argc) {
X	  char *dir;
X	  dir = "."; /* current working directory */
X	  if (expunge(&dir, 1))
X	       error("expunging .");
X     }
X     else if (expunge(&argv[optind], argc - optind))
X	  error("expunge");
X
X     exit((error_occurred && (! force)) ? 1 : 0);
X}
X
X
X
X
X
Xpurge()
X{
X     char *home;
X     int retval;
X     
X     home = Malloc((unsigned) MAXPATHLEN);
X     if (! home) {
X	  set_error(errno);
X	  error("purge");
X	  return error_code;
X     }
X     timev = interactive = noop = verbose = force = 0;
X     yield = listfiles = recursive = 1;
X     if (retval = get_home(home)) {
X	  error("purge");
X	  return retval;
X     }
X
X     printf("Please be patient.... this may take a while.\n\n");
X
X     if (retval = expunge(&home, 1)) {
X	  error("expunge");
X	  return retval;
X     }
X     return 0;
X}
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, "     -t n   n-day-or-older expunge\n");
X     fprintf(stderr, "     -n     noop\n");
X     fprintf(stderr, "     -v     verbose\n");
X     fprintf(stderr, "     -l     list files before expunging\n");
X     fprintf(stderr, "     -s     follow symbolic links to directories\n");
X     fprintf(stderr, "     -m     follow mount points\n");
X     fprintf(stderr, "     -y     print yield of expunge\n");
X     fprintf(stderr, "     --     end options and start filenames\n");
X}
X
X
X
X
X
Xint expunge(files, num)
Xchar **files;
Xint num;
X{
X     char **found_files;
X     int num_found;
X     int status = 0;
X     int total = 0;
X     filerec *current;
X     int retval;
X     
X     if (initialize_tree())
X	  exit(1);
X
X     for ( ; num ; num--) {
X	  retval = get_the_files(files[num - 1], &num_found, &found_files);
X	  if (retval) {
X	       error(files[num - 1]);
X	       return retval;
X	  }
X	       
X	  if (num_found) {
X	       num_found = process_files(found_files, num_found);
X	       if (num_found < 0) {
X		    error("process_files");
X		    return error_code;
X	       }
X	  }
X	  
X	  total += num_found;
X	  if (! num_found) if (! force) {
X	       /*
X		* There are three different situations here.  Eiter we
X		* are dealing with an existing directory with no
X	        * deleted files in it, or we are deleting with a
X	        * non-existing deleted file with wildcards, or we are
X	        * dealing with a non-existing deleted file without
X	        * wildcards.  In the former case we print nothing, and
X	        * in the latter cases we print either "no match" or
X	        * "not found" respectively
X		*/
X	       if (no_wildcards(files[num - 1])) {
X		    if (! directory_exists(files[num - 1])) {
X			 set_error(ENOENT);
X			 error(files[num - 1]);
X		    }
X	       }
X	       else {
X		    set_error(ENOMATCH);
X		    error(files[num - 1]);
X	       }
X	  }
X     }
X     if (total && listfiles) {
X	  if (retval = list_files()) {
X	       error("list_files");
X	       return retval;
X	  }
X	  if (! force) if (! top_level()) {
X	       set_status(EXPUNGE_NOT_EXPUNGED);
X	       return error_code;
X	  }
X     }
X     current = get_root_tree();
X     if (current) {
X	  if (retval = expunge_specified(current)) {
X	       error("expunge_specified");
X	       status = retval;
X	  }
X     }
X     current = get_cwd_tree();
X     if (current) {
X	  if (retval = expunge_specified(current)) {
X	       error("expunge_specified");
X	       status = retval;
X	  }
X     }
X     if (yield) {
X	  if (noop)
X	       printf("Total that would be expunged: %dk\n",
X		      blk_to_k(blocks_removed));
X	  else
X	       printf("Total expunged: %dk\n", blk_to_k(blocks_removed));
X     }
X     return status;
X}
X
X
X
Xexpunge_specified(leaf)
Xfilerec *leaf;
X{
X     int status = 0;
X     int do_it = 1;
X     int retval;
X     
X     if ((leaf->specified) && ((leaf->specs.st_mode & S_IFMT) == S_IFDIR)) {
X	  char buf[MAXPATHLEN];
X
X	  if (retval = get_leaf_path(leaf, buf)) {
X	       error("get_leaf_path");
X	       return retval;
X	  }
X	  (void) convert_to_user_name(buf, buf);
X
X	  if (interactive) {
X	       printf("%s: Expunge directory %s? ", whoami, buf);
X	       status = (! (do_it = yes()));
X	  }
X     }
X     if (do_it) {
X	  if (leaf->dirs) {
X	       if (retval = expunge_specified(leaf->dirs)) {
X		    error("expunge_specified");
X		    status = retval;
X	       }
X	  }
X	  if (leaf->files) {
X	       if (retval = expunge_specified(leaf->files)) {
X		    error("expunge_specified");
X		    status = retval;
X	       }
X	  }
X     }
X     if (leaf->specified && (! status)) {
X	  if (retval = really_do_expunge(leaf)) {
X	       error("really_do_expunge");
X	       status = retval;
X	  }
X     }
X     if (leaf->next) {
X	  if (retval = expunge_specified(leaf->next)) {
X	       error("expunge_specified");
X	       status = retval;
X	  }
X     }
X
X     free_leaf(leaf);
X     return status;
X}
X
X
Xprocess_files(files, num)
Xchar **files;
Xint num;
X{
X     int i, skipped = 0;
X     filerec *leaf;
X     
X     for (i = 0; i < num; i++) {
X	  if (add_path_to_tree(files[i], &leaf)) {
X	       error("add_path_to_tree");
X	       return -1;
X	  }
X	  free(files[i]);
X	  if (! timed_out(leaf, current_time, timev)) {
X	       free_leaf(leaf);
X	       skipped++;
X	  }
X     }
X     free((char *) files);
X     return(num-skipped);
X}
X
X
X
X
X
X
X
X
X
Xreally_do_expunge(file_ent)
Xfilerec *file_ent;
X{
X     char real[MAXPATHLEN], user[MAXPATHLEN];
X     int status;
X     int retval;
X     
X     if (retval = get_leaf_path(file_ent, real)) {
X	  error("get_leaf_path");
X	  return retval;
X     }
X     (void) convert_to_user_name(real, user);
X
X     if (interactive) {
X	  printf ("%s: Expunge %s (%dk)? ", whoami, user,
X		  blk_to_k(file_ent->specs.st_blocks));
X	  if (! yes()) {
X	       set_status(EXPUNGE_NOT_EXPUNGED);
X	       return error_code;
X	  }
X     }
X
X     if (noop) {
X	  blocks_removed += file_ent->specs.st_blocks;
X	  printf("%s: %s (%dk) would be expunged (%dk total)\n", whoami, user,
X		 blk_to_k(file_ent->specs.st_blocks),
X		 blk_to_k(blocks_removed));
X	  return 0;
X     }
X
X     if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
X	  status = rmdir(real);
X     else
X	  status = unlink(real);
X     if (! status) {
X	  blocks_removed += file_ent->specs.st_blocks;
X	  if (verbose)
X	       printf("%s: %s (%dk) expunged (%dk total)\n", whoami, user,
X		      blk_to_k(file_ent->specs.st_blocks),
X		      blk_to_k(blocks_removed));
X	  return 0;
X     }
X     else {
X	  set_error(errno);
X	  error(real);
X	  return error_code;
X     }
X}
X
X
X
X
X
X
X
X
X
Xtop_level()
X{
X     if (interactive) {
Xprintf("The above files, which have been marked for deletion, are about to be\n");
Xprintf("expunged forever!  You will be asked for confirmation before each file is\n");
Xprintf("deleted.  Do you wish to continue [return = no]? ");
X     }
X     else {
Xprintf("The above files, which have been marked for deletion, are about to be\n");
Xprintf("expunged forever!  Make sure you don't need any of them before continuing.\n");
Xprintf("Do you wish to continue [return = no]? ");
X     }
X     return (yes());
X}
X
X
X
X
X
Xlist_files()
X{
X     filerec *current;
X     char **strings;
X     int num;
X     int retval;
X     
X     strings = (char **) Malloc(sizeof(char *));
X     num = 0;
X     if (! strings) {
X	  set_error(errno);
X	  error("Malloc");
X	  return error_code;
X     }
X
X     printf("The following deleted files are going to be expunged: \n\n");
X
X     current = get_root_tree();
X     if (retval = accumulate_names(current, &strings, &num)) {
X	  error("accumulate_names");
X	  return retval;
X     }
X     current = get_cwd_tree();
X     if (retval = accumulate_names(current, &strings, &num)) {
X	  error("accumulate_names");
X	  return retval;
X     }
X     if (retval = column_array(strings, num, DEF_SCR_WIDTH, 0, 0, 2, 1, 0,
X			       1, stdout)) {
X	  error("column_array");
X	  return retval;
X     }
X     
X     printf("\n");
X     return(0);
X}
X     
X
X
X
X
Xint get_the_files(name, num_found, found)
Xchar *name;
Xint *num_found;
Xchar ***found;
X{
X     int retval;
X     int options;
X     
X     options = FIND_DELETED | FIND_CONTENTS | RECURS_DELETED;
X     if (recursive)
X	  options |= RECURS_FIND_DELETED;
X     if (f_mounts)
X	  options |= FOLLW_MOUNTPOINTS;
X     if (f_links)
X	  options |= FOLLW_LINKS;
X     
X     retval = find_matches(name, num_found, found, options);
X     if (retval) {
X	  error("find_matches");
X	  return retval;
X     }
X
X     return 0;
X}
END_OF_FILE
if test 11211 -ne `wc -c <'expunge.c'`; then
    echo shar: \"'expunge.c'\" unpacked with wrong size!
fi
# end of 'expunge.c'
fi
if test -f 'lsdel.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'lsdel.c'\"
else
echo shar: Extracting \"'lsdel.c'\" \(7280 characters\)
sed "s/^X//" >'lsdel.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/lsdel.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_lsdel_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/lsdel.c,v 1.9 89/11/22 21:31: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 <sys/stat.h>
X#include <strings.h>
X#include <errno.h>
X#include <com_err.h>
X#include "col.h"
X#include "util.h"
X#include "directories.h"
X#include "pattern.h"
X#include "lsdel.h"
X#include "shell_regexp.h"
X#include "mit-copyright.h"
X#include "delete_errs.h"
X#include "errors.h"
X
Xchar *realloc();
Xextern time_t current_time;
Xextern int errno;
X
Xint block_total = 0;
Xint dirsonly, recursive, yield, f_links, f_mounts;
Xtime_t timev;
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     dirsonly = recursive = timev = yield = f_links = f_mounts = 0;
X     while ((arg = getopt(argc, argv, "drt:ysm")) != -1) {
X	  switch (arg) {
X	  case 'd':
X	       dirsonly++;
X	       break;
X	  case 'r':
X	       recursive++;
X	       break;
X	  case 't':
X	       timev = atoi(optarg);
X	       break;
X	  case 'y':
X	       yield++;
X	       break;
X	  case 's':
X	       f_links++;
X	       break;
X	  case 'm':
X	       f_mounts++;
X	       break;
X	  default:
X	       usage();
X	       exit(1);
X	  }
X     }
X     if (optind == argc) {
X	  char *cwd;
X
X	  cwd = ".";
X
X	  if (ls(&cwd, 1))
X	       error("ls of .");
X     }
X     else if (ls(&argv[optind], argc - optind))
X	  error ("ls");
X
X     exit (error_occurred ? 1 : 0);
X}
X
X
X
X
X
X
Xusage()
X{
X     fprintf(stderr, "Usage: %s [ options ] [ filename [ ...]]\n", whoami);
X     fprintf(stderr, "Options are:\n");
X     fprintf(stderr, "     -d     list directory names, not contents\n");
X     fprintf(stderr, "     -r     recursive\n");
X     fprintf(stderr, "     -t n   list n-day-or-older files only\n");
X     fprintf(stderr, "     -y     report total space taken up by files\n");
X     fprintf(stderr, "     -s     follow symbolic links to directories\n");
X     fprintf(stderr, "     -m     follow mount points\n");
X}
X
X
X
X
Xls(args, num)
Xchar **args;
Xint num;
X{
X     char **found_files;
X     int num_found = 0, total = 0;
X     int status = 0;
X     int retval;
X
X     initialize_del_error_table();
X     
X     if (retval = initialize_tree()) {
X	  error("initialize_tree");
X	  return retval;
X     }
X     
X     for ( ; num; num--) {
X	  if (retval = get_the_files(args[num - 1], &num_found,
X				     &found_files)) {
X	       error(args[num - 1]);
X	       status = retval;
X	       continue;
X	  }
X
X	  if (num_found) {
X	       num_found = process_files(found_files, num_found);
X	       if (num_found < 0) {
X		    error("process_files");
X		    status = error_code;
X		    continue;
X	       }
X	       total += num_found;
X	  }
X	  else {
X	       /* What we do at this point depends on exactly what the
X	        * filename is.  There are several possible conditions:
X		* 1. The filename has no wildcards in it, which means that
X		*    if we couldn't find it, that means it doesn't
X		*    exist.  Print a not found error.
X		* 2. Filename is an existing directory, with no deleted
X		*    files in it.  Print nothing.
X		* 3. Filename doesn't exist, and there are wildcards in
X		*    it.  Print "no match".
X		* None of these are considered error conditions, so we
X		* don't set the error flag.
X		*/
X	       if (no_wildcards(args[num - 1])) {
X		    if (! directory_exists(args[num - 1])) {
X			 set_error(ENOENT);
X			 error(args[num - 1]);
X			 status = error_code;
X			 continue;
X		    }
X	       }
X	       else {
X		    set_error(ENOMATCH);
X		    error(args[num - 1]);
X		    status = error_code;
X		    continue;
X	       }
X	  }
X     }
X     if (total) {
X	  if (list_files()) {
X	       error("list_files");
X	       return error_code;
X	  }
X     }
X     if (yield)
X	  printf("\nTotal space taken up by file%s: %dk\n",
X		 (total == 1 ? "" : "s"), blk_to_k(block_total));
X
X     return status;
X}
X
X
X
X
Xint get_the_files(name, number_found, found)
Xchar *name;
Xint *number_found;
Xchar ***found;
X{
X     int retval;
X     int options;
X     
X     options = FIND_DELETED | FIND_CONTENTS;
X     if (recursive)
X	  options |= RECURS_FIND_DELETED;
X     if (! dirsonly)
X	  options |= RECURS_DELETED;
X     if (f_mounts)
X	  options |= FOLLW_MOUNTPOINTS;
X     if (f_links)
X	  options |= FOLLW_LINKS;
X     
X     retval = find_matches(name, number_found, found, options);
X     if (retval) {
X	  error("find_matches");
X	  return retval;
X     }
X
X     return 0;
X}
X
X
X
X
X
X
Xprocess_files(files, num)
Xchar **files;
Xint num;
X{
X     int i;
X     filerec *leaf;
X     
X     for (i = 0; i < num; i++) {
X	  if (add_path_to_tree(files[i], &leaf)) {
X	       error("add_path_to_tree");
X	       return -1;
X	  }
X	  free(files[i]);
X	  if (! timed_out(leaf, current_time, timev)) {
X	       free_leaf(leaf);
X	       num--;
X	       continue;
X	  }
X	  block_total += leaf->specs.st_blocks;
X     }
X     free((char *) files);
X     return(num);
X}
X
X
X
X
X
Xlist_files()
X{
X     filerec *current;
X     char **strings;
X     int num;
X     int retval;
X     
X     strings = (char **) Malloc((unsigned) sizeof(char *));
X     num = 0;
X     if (! strings) {
X	  set_error(errno);
X	  error("Malloc");
X	  return error_code;
X     }
X     current = get_root_tree();
X     if (retval = accumulate_names(current, &strings, &num)) {
X	  error("accumulate_names");
X	  return retval;
X     }
X     current = get_cwd_tree();
X     if (retval = accumulate_names(current, &strings, &num)) {
X	  error("accumulate_names");
X	  return retval;
X     }
X
X     if (retval = sort_files(strings, num)) {
X	  error("sort_files");
X	  return retval;
X     }
X     
X     if (retval = unique(&strings, &num)) {
X	  error("unique");
X	  return retval;
X     }
X     
X     if (retval = column_array(strings, num, DEF_SCR_WIDTH, 0, 0, 2, 1, 0,
X			       1, stdout)) {
X	  error("column_array");
X	  return retval;
X     }
X     
X     for ( ; num; num--)
X	  free(strings[num - 1]);
X     free((char *) strings);
X     return 0;
X}
X
X
Xint sort_files(data, num_data)
Xchar **data;
Xint num_data;
X{
X     qsort((char *) data, num_data, sizeof(char *), strcmp);
X
X     return 0;
X}
X
X
Xint unique(the_files, number)
Xchar ***the_files;
Xint *number;
X{
X     int i, last;
X     int offset;
X     char **files;
X
X     files = *the_files;
X     for (last = 0, i = 1; i < *number; i++) {
X	  if (! strcmp(files[last], files[i])) {
X	       free (files[i]);
X	       free (files[i]);
X	       files[i] = (char *) NULL;
X	  }
X	  else
X	       last = i;
X     }
X     
X     for (offset = 0, i = 0; i + offset < *number; i++) {
X	  if (! files[i])
X	       offset++;
X	  if (i + offset < *number)
X	       files[i] = files[i + offset];
X     }
X     *number -= offset;
X     files = (char **) realloc((char *) files,
X			       (unsigned) (sizeof(char *) * *number));
X     if (! files) {
X	  set_error(errno);
X	  error("realloc");
X	  return errno;
X     }
X
X     *the_files = files;
X     return 0;
X}
END_OF_FILE
if test 7280 -ne `wc -c <'lsdel.c'`; then
    echo shar: \"'lsdel.c'\" unpacked with wrong size!
fi
# end of 'lsdel.c'
fi
if test -f 'undelete.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'undelete.c'\"
else
echo shar: Extracting \"'undelete.c'\" \(12500 characters\)
sed "s/^X//" >'undelete.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/user/j/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: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/undelete.c,v 1.19 89/12/15 04:39:31 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 <com_err.h>
X#include <errno.h>
X#include "delete_errs.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 "errors.h"
X
Xextern char *realloc();
Xextern int errno;
X
Xint interactive, recursive, verbose, directoriesonly, noop, force;
X
X
Xmain(argc, argv)
Xint argc;
Xchar *argv[];
X{
X     extern char *optarg;
X     extern int optind;
X     int arg;
X     int retval;
X     
X     initialize_del_error_table();
X     
X     whoami = lastpart(argv[0]);
X     interactive = recursive = verbose = directoriesonly = noop = force = 0;
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	       break;
X	  default:
X	       usage();
X	       exit(1);
X	  }
X     }
X
X     report_errors = ! force;
X     
X     if (optind == argc) {
X	  if (interactive_mode())
X	       error("interactive_mode");
X     }
X     else while (optind < argc) {
X	  retval = undelete(argv[optind]);
X	  if (retval)
X	       error(argv[optind]);
X	  optind++;
X     }
X     exit(((! force) && error_occurred) ? 1 : 0);
X}
X
X
X
Xinteractive_mode()
X{
X     char buf[MAXPATHLEN];
X     char *ptr;
X     int status = 0;
X     int retval;
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	  retval = undelete(buf);
X	  if (retval) {
X	       error(buf);
X	       status = retval;
X	  }
X     } while (*buf);
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
Xundelete(name)
Xchar *name;
X{
X     char **found_files;
X     int num_found;
X     int status = 0;
X     filerec *current;
X     int retval;
X     
X     if (retval =  get_the_files(name, &num_found, &found_files)) {
X	  error(name);
X	  return retval;
X     }
X     
X     if (num_found) {
X	  if (retval = process_files(found_files, num_found)) {
X	       error(name);
X	       return retval;
X	  }
X	  if (*name == '/')
X	       current = get_root_tree();
X	  else
X	       current = get_cwd_tree();
X
X	  status = recurs_and_undelete(current);
X	  if (status) {
X	       error(name);
X	       return status;
X	  }
X     }
X     else {
X	  if (no_wildcards(name)) {
X	       set_error(ENOENT)
X	  }
X	  else
X	       set_error(ENOMATCH);
X	  error(name);
X	  return error_code;
X     }
X
X     return status;
X}
X
X
X
X
X
Xint recurs_and_undelete(leaf)
Xfilerec *leaf;
X{
X     int status = 0;
X     int retval;
X     
X     if (leaf->specified) {
X	  retval = do_undelete(leaf);
X	  if (retval) {
X	       error("do_undelete");
X	       return retval;
X	  }
X     }
X
X     if (leaf->dirs) {
X	  retval = recurs_and_undelete(leaf->dirs);
X	  if (retval) {
X	       error("recurs_and_undelete");
X	       status = retval;
X	  }
X     }
X
X     if (leaf->files) {
X	  retval = recurs_and_undelete(leaf->files);
X	  if (retval) {
X	       error("recurs_and_undelete");
X	       status = retval;
X	  }
X     }
X     
X     if (leaf->next) {
X	  retval = recurs_and_undelete(leaf->next);
X	  if (retval) {
X	       error("recurs_and_undelete");
X	       status = retval;
X	  }
X     }
X     
X     free_leaf(leaf);
X
X     return status;
X}
X
X
X
X
X
X
Xint process_files(files, num)
Xchar **files;
Xint num;
X{
X     int i;
X     listrec *filelist;
X     struct filrec *not_needed;
X     int retval;
X     
X     filelist = (listrec *) Malloc((unsigned) (sizeof(listrec) * num));
X     if (! filelist) {
X	  set_error(errno);
X	  error("process_files");
X	  return error_code;
X     }
X     
X     for (i = 0; i < num; i++) {
X	  filelist[i].real_name = Malloc((unsigned) (strlen(files[i]) + 1));
X	  if (! filelist[i].real_name) {
X	       set_error(errno);
X	       error("process_files");
X	       return error_code;
X	  }
X	  (void) strcpy(filelist[i].real_name, files[i]);
X	  filelist[i].user_name = Malloc((unsigned) (strlen(files[i]) + 1));
X	  if (! filelist[i].user_name) {
X	       set_error(errno);
X	       error("process_files");
X	       return error_code;
X	  }
X 	  (void) convert_to_user_name(files[i], filelist[i].user_name);
X	  free(files[i]);
X     }
X     free((char *) files);
X	  
X     if (retval = sort_files(filelist, num)) {
X	  error("sort_files");
X	  return retval;
X     }
X     if (retval = unique(&filelist, &num)) {
X	  error("unique");
X	  return retval;
X     }
X     if (retval = initialize_tree()) {
X	  error("initialize_tree");
X	  return retval;
X     }
X	  
X     for (i = 0; i < num; i++) {
X	  if (retval = add_path_to_tree(filelist[i].real_name, &not_needed)) {
X	       error("add_path_to_tree");
X	       return retval;
X	  }
X	  else {
X	       free(filelist[i].real_name);
X	       free(filelist[i].user_name);
X	  }
X     }
X     free((char *) filelist);
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     int retval;
X     
X     if (retval = get_leaf_path(file_ent, real_name)) {
X	  if (! force)
X	       fprintf(stderr, "%s: %s: %s\n", whoami, "get_leaf_path",
X		       error_message(retval));
X	  return retval;
X     }
X     
X     (void) 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	       set_status(UNDEL_NOT_UNDELETED);
X	       return error_code;
X	  }
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	       set_status(UNDEL_NOT_UNDELETED);
X	       return error_code;
X	  }
X	  if (! noop) if (retval = unlink_completely(user_name)) {
X	       error(user_name);
X	       return retval;
X	  }
X     }
X     if (noop) {
X	  printf("%s: %s would be undeleted\n", whoami, user_name);
X	  return 0;
X     }
X
X     if (retval = do_file_rename(real_name, user_name)) {
X	  error("do_file_rename");
X	  return retval;
X     }
X     else {
X	  if (verbose)
X	       printf("%s: %s undeleted\n", whoami, user_name);
X	  return 0;
X     }
X}
X
X
X
X
Xdo_file_rename(real_name, user_name)
Xchar *real_name, *user_name;
X{
X     char *ptr;
X     int retval;
X     char error_buf[MAXPATHLEN+MAXPATHLEN+14];
X     char old_name[MAXPATHLEN], new_name[MAXPATHLEN];
X     char buf[MAXPATHLEN];
X
X     (void) strcpy(old_name, real_name);
X     (void) strcpy(new_name, real_name);
X
X     while (ptr = strrindex(new_name, ".#")) {
X	  (void) convert_to_user_name(ptr, ptr);
X	  (void) strcpy(ptr, firstpart(ptr, buf));
X	  (void) strcpy(&old_name[ptr - new_name],
X			firstpart(&old_name[ptr - new_name], buf));
X	  if (rename(old_name, new_name)) {
X	       set_error(errno);
X	       (void) sprintf(error_buf, "renaming %s to %s",
X			      old_name, new_name);
X	       error(error_buf);
X	       return error_code;
X	  }
X	  if (ptr > new_name) {
X	       *--ptr = '\0';
X	       old_name[ptr - new_name] = '\0';
X	  }
X     }
X     if (retval = change_path(real_name, user_name)) {
X	  error("change_path");
X	  return retval;
X     }
X     
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     
Xint sort_files(data, num_data)
Xlistrec *data;
Xint num_data;
X{
X     qsort((char *) data, num_data, sizeof(listrec), filecmp);
X
X     return 0;
X}
X
X
X
X
X
Xint unique(the_files, number)
Xlistrec **the_files;
Xint *number;
X{
X     int i, last;
X     int offset;
X     listrec *files;
X
X     files = *the_files;
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((char *) files,
X				 (unsigned) (sizeof(listrec) * *number));
X     if (! files) {
X	  set_error(errno);
X	  error("realloc");
X	  return errno;
X     }
X
X     *the_files = files;
X     return 0;
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 retval;
X     int status = 0;
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 (retval = unlink_completely(buf)) {
X		    error(buf);
X		    status = retval;
X	       }
X	  }
X	  closedir(dirp);
X	  if (retval = rmdir(filename)) {
X	       set_error(errno);
X	       error(filename);
X	       return error_code;
X	  }
X     }
X     else if (retval = unlink(filename)) {
X	  set_error(errno);
X	  error(filename);
X	  return error_code;
X     }
X
X     return status;
X}
X
X
X
X
Xint get_the_files(name, num_found, found)
Xchar *name;
Xint *num_found;
Xchar ***found;
X{
X     int retval;
X     int options;
X     
X     options = FIND_DELETED;
X     if (recursive)
X	  options |= RECURS_DELETED;
X     if (! directoriesonly)
X	  options |= FIND_CONTENTS;
X
X     retval = find_matches(name, num_found, found, options);
X     if (retval) {
X	  error("find_matches");
X	  return retval;
X     }
X
X     return 0;
X}
END_OF_FILE
if test 12500 -ne `wc -c <'undelete.c'`; then
    echo shar: \"'undelete.c'\" unpacked with wrong size!
fi
# end of 'undelete.c'
fi
if test -f 'util.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'util.c'\"
else
echo shar: Extracting \"'util.c'\" \(7849 characters\)
sed "s/^X//" >'util.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/util.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_util_c[] = "$Header: /afs/athena.mit.edu/user/j/jik/src/delete/RCS/util.c,v 1.15 90/01/11 03:47:11 jik Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/param.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/dir.h>
X#include <strings.h>
X#include <pwd.h>
X#include <errno.h>
X#ifdef AFS_MOUNTPOINTS
X#include <sys/ioctl.h>
X#include <afs/vice.h>
X#include <afs/venus.h>
X#endif
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 *getenv();
Xextern uid_t getuid();
Xextern int errno;
X
Xchar *convert_to_user_name(real_name, user_name)
Xchar real_name[];
Xchar user_name[];  /* RETURN */
X{
X     char *ptr, *q;
X     
X     (void) strcpy(user_name, real_name);
X     while (ptr = strrindex(user_name, ".#")) {
X	  for (q = ptr; *(q + 2); q++)
X	       *q = *(q + 2);
X	  *q = '\0';
X     }
X     return (user_name);
X}
X
X     
X
X
X
Xchar *strindex(str, sub_str)
Xchar *str, *sub_str;
X{
X     char *ptr = str;
X     while (ptr = index(ptr, *sub_str)) {
X	  if (! strncmp(ptr, sub_str, strlen(sub_str)))
X	       return(ptr);
X	  ptr++;
X     }
X     return ((char *) NULL);
X}
X
X
X
Xchar *strrindex(str, sub_str)
Xchar *str, *sub_str;
X{
X     char *ptr;
X
X     if (strlen(str))
X	  ptr = &str[strlen(str) - 1];
X     else
X	  return((char *) NULL);
X     while ((*ptr != *sub_str) && (ptr != str)) ptr--;
X     while (ptr != str) {
X	  if (! strncmp(ptr, sub_str, strlen(sub_str)))
X	       return(ptr);
X	  ptr--;
X	  while ((*ptr != *sub_str) && (ptr != str)) ptr--;
X     }
X     if (! strncmp(ptr, sub_str, strlen(sub_str)))
X	  return(str);
X     else
X	  return ((char *) NULL);
X}
X     
X     
X/*
X * NOTE: Append uses a static array, so its return value must be
X * copied immediately.
X */
Xchar *append(filepath, filename)
Xchar *filepath, *filename;
X{
X     static char buf[MAXPATHLEN];
X
X     (void) strcpy(buf, filepath);
X     if ((! *filename) || (! *filepath)) {
X	  (void) strcpy(buf, filename);
X	  return(buf);
X     }
X     if (buf[strlen(buf) - 1] == '/')
X	  buf[strlen(buf) - 1] = '\0';
X     if (strlen(buf) + strlen(filename) + 2 > MAXPATHLEN) {
X	  set_error(ENAMETOOLONG);
X	  strncat(buf, "/", MAXPATHLEN - strlen(buf) - 1);
X	  strncat(buf, filename, MAXPATHLEN - strlen(buf) - 1);
X	  error(buf);
X 	  *buf = '\0';
X	  return buf;
X     }
X     (void) strcat(buf, "/");
X     (void) strcat(buf, filename);
X     return buf;
X}
X
X
X
X
Xyes() {
X     char buf[BUFSIZ];
X     char *val;
X     
X     val = fgets(buf, BUFSIZ, stdin);
X     if (! val) {
X	  printf("\n");
X	  exit(1);
X     }
X     if (! index(buf, '\n')) do
X	  (void) fgets(buf + 1, BUFSIZ - 1, stdin);
X     while (! index(buf + 1, '\n'));
X     return(*buf == 'y');
X}
X
X
X
X
Xchar *lastpart(filename)
Xchar *filename;
X{
X     char *part;
X
X     part = rindex(filename, '/');
X
X     if (! part)
X	  part = filename;
X     else if (part == filename)
X	  part++;
X     else if (part - filename + 1 == strlen(filename)) {
X	  part = rindex(--part, '/');
X	  if (! part)
X	       part = filename;
X	  else
X	       part++;
X     }
X     else
X	  part++;
X
X     return(part);
X}
X
X
X
X
Xchar *firstpart(filename, rest)
Xchar *filename;
Xchar *rest; /* RETURN */
X{
X     char *part;
X     static char buf[MAXPATHLEN];
X
X     (void) strcpy(buf, filename);
X     part = index(buf, '/');
X     if (! part) {
X	  *rest = '\0';
X	  return(buf);
X     }
X     (void) strcpy(rest, part + 1);
X     *part = '\0';
X     return(buf);
X}
X
X
X
X
X
Xget_home(buf)
Xchar *buf;
X{
X     char *user;
X     struct passwd *psw;
X     
X     (void) strcpy(buf, getenv("HOME"));
X     
X     if (*buf)
X	  return(0);
X
X     user = getenv("USER");
X     psw = getpwnam(user);
X
X     if (psw) {
X	  (void) strcpy(buf, psw->pw_dir);
X	  return(0);
X     }
X     
X     psw = getpwuid((int) getuid());
X
X     if (psw) {
X	  (void) strcpy(buf, psw->pw_dir);
X	  return(0);
X     }
X
X     set_error(NO_HOME_DIR);
X     error("get_home");
X     return error_code;
X}
X
X
X
X
Xtimed_out(file_ent, current_time, min_days)
Xfilerec *file_ent;
Xtime_t current_time, min_days;
X{
X     if ((current_time - file_ent->specs.st_mtime) / 86400 >= min_days)
X	  return(1);
X     else
X	  return(0);
X}
X
X
X
Xint directory_exists(dirname)
Xchar *dirname;
X{
X     struct stat stat_buf;
X
X     if (stat(dirname, &stat_buf))
X	  return(0);
X     else if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
X	  return(1);
X     else
X	  return(0);
X}
X
X
X
Xis_link(name, oldbuf)
Xchar *name;
Xstruct stat *oldbuf;
X{
X     struct stat statbuf;
X
X     if (oldbuf)
X	  statbuf = *oldbuf;
X     else if (lstat(name, &statbuf) < 0) {
X	  set_error(errno);
X	  error("is_link");
X	  return(0);
X     }
X
X     if ((statbuf.st_mode & S_IFMT) == S_IFLNK)
X	  return 1;
X     else
X	  return 0;
X}
X
X
X
X/*
X * This is one of the few procedures that is allowed to break the
X * rule of always returning an error value if an error occurs.  That's
X * because it's stupid to expect a boolean function to do that, and
X * because defaulting to not being a mountpoint if there is an error
X * is a reasonable thing to do.
X */
X/*
X * The second parameter is optional -- if it is non-NULL< it is
X * presumed to be a stat structure fo the file being passed in.
X */
Xint is_mountpoint(name, oldbuf)
Xchar *name;
Xstruct stat *oldbuf;
X{
X     struct stat statbuf;
X     dev_t device;
X     char buf[MAXPATHLEN];
X#ifdef AFS_MOUNTPOINTS
X     struct ViceIoctl blob;
X     char retbuf[MAXPATHLEN];
X     int retval;
X     char *shortname;
X#endif
X
X     /* First way to check for a mount point -- if the device number */
X     /* of name is different from the device number of name/..       */
X     if (oldbuf)
X	  statbuf = *oldbuf;
X     else if (lstat(name, &statbuf) < 0) {
X	  set_error(errno);
X	  error(name);
X	  return 0;
X     }
X
X     device = statbuf.st_dev;
X
X     if (strlen(name) + 4 /* length of "/.." + a NULL */ > MAXPATHLEN) {
X	  set_error(ENAMETOOLONG);
X	  error(name);
X	  return 0;
X     }
X
X     strcpy(buf, name);
X     strcat(buf, "/..");
X     if (lstat(buf, &statbuf) < 0) {
X	  set_error(errno);
X	  error(name);
X	  return 0;
X     }
X
X     if (statbuf.st_dev != device)
X	  return 1;
X
X#ifdef AFS_MOUNTPOINTS
X     /* Check for AFS mountpoint using the AFS pioctl call. */
X     if ((shortname = lastpart(name)) == name) {
X	  strcpy(buf, ".");
X	  blob.in = name;
X	  blob.in_size = strlen(name) + 1;
X	  blob.out = retbuf;
X	  blob.out_size = MAXPATHLEN;
X	  bzero(retbuf, MAXPATHLEN);
X     }
X     else {
X	  strncpy(buf, name, shortname - name - 1);
X	  buf[shortname - name - 1] = '\0';
X	  if (*buf == '\0')
X	       strcpy(buf, "/");
X	  blob.in = shortname;
X	  blob.in_size = strlen(shortname) + 1;
X	  blob.out = retbuf;
X	  blob.out_size = MAXPATHLEN;
X	  bzero(retbuf, MAXPATHLEN);
X     }
X
X     retval = pioctl(buf, VIOC_AFS_STAT_MT_PT, &blob, 0);
X
X     if (retval == 0) {
X#ifdef DEBUG
X	  printf("%s is an AFS mountpoint, is_mountpoint returning true.\n",
X		 name);
X#endif
X	  return 1;
X     }
X     else {
X	  if (errno != EINVAL) {
X	       set_error(errno);
X	       error(name);
X	  }
X     }
X#endif /* AFS_MOUNTPOINTS */
X
X     return 0;
X}
X
X#ifdef MALLOC_DEBUG
Xchar *Malloc(size)
Xunsigned size;
X{
X     extern char *malloc();
X
X     static int count = 0;
X     char buf[10];
X     
X     count++;
X
X     fprintf(stderr, "This is call number %d to Malloc, for %u bytes.\n",
X	     count, size);
X     fprintf(stdout, "Shall I return NULL for this malloc? ");
X     fgets(buf, 10, stdin);
X     if ((*buf == 'y') || (*buf == 'Y')) {
X	  errno = ENOMEM;
X	  return ((char *) NULL);
X     }
X     else
X	  return (malloc(size));
X}
X#endif
X
X	  
END_OF_FILE
if test 7849 -ne `wc -c <'util.c'`; then
    echo shar: \"'util.c'\" unpacked with wrong size!
fi
# end of 'util.c'
fi
echo shar: End of archive 2 \(of 3\).
cp /dev/null ark2isdone
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.