[comp.sources.misc] v17i024: delete - MIT Athena delete/undelete programs, Part02/04

jik@pit-manager.MIT.EDU (Jonathan I. Kamens) (02/26/91)

Submitted-by: Jonathan I. Kamens <jik@pit-manager.MIT.EDU>
Posting-number: Volume 17, Issue 24
Archive-name: delete/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 4)."
# Contents:  Makefile expunge.c lsdel.c undelete.c util.c
# Wrapped by jik@pit-manager on Fri Feb 22 08:14:23 1991
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(5485 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
X#     Copyright 1988 Massachusetts Institute of Technology.
X#
X#     For copying and distribution information, see the file
X#     "mit-copyright.h".
X#
X#     $Source: /afs/athena.mit.edu/user/j/jik/delete/src/RCS/Makefile,v $
X#     $Author: jik $
X#     $Header: /afs/athena.mit.edu/user/j/jik/delete/src/RCS/Makefile,v 1.28 91/02/22 07:35:51 jik Exp $
X#
X
XDESTDIR=
XTARGETS= 	delete undelete expunge purge lsdel
XINSTALLDIR= 	/usr/bin
XMANDIR=		/usr/man
XMANSECT=	1
XCC= 		cc
XDEPEND=		/usr/bin/X11/makedepend
XCOMPILE_ET= 	compile_et
XLINT= 		lint
XDEFINES=	
X
X
X# These variables apply only if you want this program to recognize
X# Andrew File System mount points.  If you don't want to support AFS,
X# then they should all be commented out or set to nothing.
X# 
X# AFSINC is the include directory for AFS header files.  
X# AFSLIB is the library directory that contains the AFS libraries.
X# 
X# AFSINC=		
X# AFSLIB=		
X# AFSINCS=	-I$(AFSINC)
X# AFSLDFLAGS=	-L$(AFSLIB) -L$(AFSLIB)/afs
X# AFSLIBS=	-lsys -lrx -llwp $(AFSLIB)/afs/util.a
X# AFSDEFINES=	-DAFS_MOUNTPOINTS
X
X
X# If you install the com_err library and include files in directories
X# that your compiler and linker know how to find, you don't have to
X# set these variables.  Otherwise, you do.
X# 
X# ETINCS is a -I flag pointing to the directory in which the et header
X# files are stored. 
X# ETLDFLAGS is a -L flag pointing to the directory where the et
X# library is stored.
X# 
X# ETINCS=		
X# ETLDFLAGS=	
X
X
X# You probably won't have to edit anything below this line.
X
XETLIBS=		-lcom_err
XINCLUDES=	$(ETINCS) $(AFSINCS)
XLDFLAGS=	$(ETLDFLAGS) $(AFSLDFLAGS) 
XLIBS= 		$(ETLIBS) $(AFSLIBS)
XCDEBUGFLAGS=	-O
XCFLAGS= 	$(INCLUDES) $(DEFINES) $(AFSDEFINES) $(CDEBUGFLAGS)
XLINTFLAGS=	-u $(INCLUDES) $(DEFINES) $(AFSDEFINES) $(CDEBUGFLAGS)
XLINTLIBS=	
X
XSRCS= 		delete.c undelete.c directories.c pattern.c util.c\
X		expunge.c lsdel.c col.c shell_regexp.c\
X		errors.c stack.c
XINCS= 		col.h delete.h directories.h expunge.h lsdel.h\
X		mit-copyright.h pattern.h undelete.h util.h\
X		shell_regexp.h errors.h stack.h
XETS=		delete_errs.h delete_errs.c
XETSRCS=		delete_errs.et
X
XMANS= 		man1/delete.1 man1/expunge.1 man1/lsdel.1 man1/purge.1\
X		man1/undelete.1
X
XARCHIVE=	README Makefile PATCHLEVEL $(SRCS) $(INCS) $(MANS)\
X		$(ETSRCS)
XARCHIVEDIRS= 	man1
X
XDELETEOBJS= 	delete.o util.o delete_errs.o errors.o
XUNDELETEOBJS= 	undelete.o directories.o util.o pattern.o\
X		shell_regexp.o delete_errs.o errors.o stack.o
XEXPUNGEOBJS= 	expunge.o directories.o pattern.o util.o col.o\
X		shell_regexp.o delete_errs.o errors.o stack.o
XLSDELOBJS= 	lsdel.o util.o directories.o pattern.o col.o\
X		shell_regexp.o delete_errs.o errors.o stack.o
X
XDELETESRC= 	delete.c util.c delete_errs.c errors.c
XUNDELETESRC= 	undelete.c directories.c util.c pattern.c\
X		shell_regexp.c delete_errs.c errors.c stack.c
XEXPUNGESRC= 	expunge.c directories.c pattern.c util.c col.c\
X		shell_regexp.c delete_errs.c errors.c stack.c
XLSDELSRC= 	lsdel.c util.c directories.c pattern.c col.c\
X		shell_regexp.c delete_errs.c errors.c stack.c
X
X.SUFFIXES: .c .h .et
X
X.et.h: $*.et
X	${COMPILE_ET} $*.et
X.et.c: $*.et
X	${COMPILE_ET} $*.et
X
Xall: $(TARGETS)
X
Xlint_all: lint_delete lint_undelete lint_expunge lint_lsdel
X
Xinstall:: bin_install man_install
X
X# Errors are ignored on bin_install and man_install because make on
X# some platforms, in combination with the shell, does really stupid
X# things and detects an error where there is none.
X
Xman_install:
X	-for i in $(TARGETS) ; do\
X	  install -c man1/$$i.1\
X		$(DESTDIR)$(MANDIR)/man$(MANSECT)/$$i.$(MANSECT);\
X	done
X
Xbin_install: $(TARGETS)
X	-for i in $(TARGETS) ; do\
X          if [ -f $(DESTDIR)$(INSTALLDIR)/$$i ]; then\
X            mv $(DESTDIR)$(INSTALLDIR)/$$i $(DESTDIR)$(INSTALLDIR)/.#$$i ; \
X          fi; \
X	  install -c -s $$i $(DESTDIR)$(INSTALLDIR) ; \
X        done
X
Xdelete: $(DELETEOBJS)
X	$(CC) $(LDFLAGS) $(CFLAGS) -o delete $(DELETEOBJS) $(LIBS)
X
Xsaber_delete:
X	#setopt program_name delete
X	#load $(LDFLAGS) $(CFLAGS) $(DELETESRC) $(LIBS)
X
Xlint_delete: $(DELETESRC)
X	$(LINT) $(LINTFLAGS) $(DELETESRC) $(LINTLIBS)
X
Xundelete: $(UNDELETEOBJS)
X	$(CC) $(LDFLAGS) $(CFLAGS) -o undelete $(UNDELETEOBJS) $(LIBS)
X
Xsaber_undelete:
X	#setopt program_name undelete
X	#load $(LDFLAGS) $(CFLAGS) $(UNDELETESRC) $(LIBS)
X
Xlint_undelete: $(UNDELETESRC)
X	$(LINT) $(LINTFLAGS) $(UNDELETESRC) $(LINTLIBS)
X
Xexpunge: $(EXPUNGEOBJS)
X	$(CC) $(LDFLAGS) $(CFLAGS) -o expunge $(EXPUNGEOBJS) $(LIBS)
X
Xsaber_expunge:
X	#setopt program_name expunge
X	#load $(LDFLAGS) $(CFLAGS) $(EXPUNGESRC) $(LIBS)
X
Xlint_expunge: $(EXPUNGESRC)
X	$(LINT) $(LINTFLAGS) $(EXPUNGESRC) $(LINTLIBS)
X
Xpurge: expunge
X	ln -s expunge purge
X
Xlsdel: $(LSDELOBJS)
X	$(CC) $(LDFLAGS) $(CFLAGS) -o lsdel $(LSDELOBJS) $(LIBS)
X
Xlint_lsdel: $(LSDELSRC)
X	$(LINT) $(LINTFLAGS) $(LSDELSRC) $(LINTLIBS)
X
Xsaber_lsdel:
X	#setopt program_name lsdel
X	#load $(LDFLAGS) $(CFLAGS) $(LSDELSRC) $(LIBS)
X
Xtar: $(ARCHIVE)
X	tar cvf - $(ARCHIVE) | compress > delete.tar.Z
X
Xshar: $(ARCHIVE)
X	makekit -oMANIFEST $(ARCHIVEDIRS) $(ARCHIVE)
X
Xpatch: $(ARCHIVE)
X	makepatch $(ARCHIVE)
X	mv patch delete.patch`cat PATCHLEVEL`
X	shar delete.patch`cat PATCHLEVEL` > delete.patch`cat PATCHLEVEL`.shar
X
Xclean::
X	-rm -f *~ *.bak *.o delete undelete lsdel expunge purge\
X		delete_errs.h delete_errs.c
X
Xdepend: $(SRCS) $(INCS) $(ETS)
X	$(DEPEND) -v $(CFLAGS) -s'# DO NOT DELETE' $(SRCS)
X
X$(DELETEOBJS): delete_errs.h
X$(EXPUNGEOBJS): delete_errs.h
X$(UNDELETEOBJS): delete_errs.h
X$(LSDELOBJS): delete_errs.h
X
X# DO NOT DELETE THIS LINE -- makedepend depends on it
END_OF_FILE
if test 5485 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
# end of 'Makefile'
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'\" \(11287 characters\)
sed "s/^X//" >'expunge.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/astaff/project/delete/src/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/astaff/project/delete/src/RCS/expunge.c,v 1.17 91/02/20 17:42: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#ifdef SYSV
X#include <string.h>
X#define index strchr
X#define rindex strrchr
X#else
X#include <strings.h>
X#endif /* SYSV */
X#include <com_err.h>
X#include <errno.h>
X#include "col.h"
X#include "util.h"
X#include "directories.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 bytes_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		      size_to_k(bytes_removed));
X	  else
X	       printf("Total expunged: %dk\n", size_to_k(bytes_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		  size_to_k(file_ent->specs.st_size));
X	  if (! yes()) {
X	       set_status(EXPUNGE_NOT_EXPUNGED);
X	       return error_code;
X	  }
X     }
X
X     if (noop) {
X	  bytes_removed += file_ent->specs.st_size;
X	  printf("%s: %s (%dk) would be expunged (%dk total)\n", whoami, user,
X		 size_to_k(file_ent->specs.st_size),
X		 size_to_k(bytes_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	  bytes_removed += file_ent->specs.st_size;
X	  if (verbose)
X	       printf("%s: %s (%dk) expunged (%dk total)\n", whoami, user,
X		      size_to_k(file_ent->specs.st_size),
X		      size_to_k(bytes_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 11287 -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'\" \(7464 characters\)
sed "s/^X//" >'lsdel.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/astaff/project/delete/src/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/astaff/project/delete/src/RCS/lsdel.c,v 1.14 91/02/20 17:36:11 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#ifdef SYSV
X#include <string.h>
X#define index strchr
X#define rindex strrchr
X#else
X#include <strings.h>
X#endif /* SYSV */
X#ifdef _AUX_SOURCE
Xextern char *strcmp();
X#endif
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
Xextern char *realloc();
Xextern time_t current_time;
Xextern int errno;
X
Xint byte_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"), size_to_k(byte_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, 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	       continue;
X	  }
X	  byte_total += leaf->specs.st_size;
X     }
X     free((char *) files);
X     return(num-skipped);
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 ((*number != 0) && (! 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 7464 -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'\" \(12715 characters\)
sed "s/^X//" >'undelete.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/astaff/project/delete/src/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/astaff/project/delete/src/RCS/undelete.c,v 1.22 91/02/20 17:27:38 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#ifdef SYSV
X#include <string.h>
X#define index strchr
X#define rindex strrchr
X#else
X#include <strings.h>
X#endif /* SYSV */
X#include <com_err.h>
X#include <errno.h>
X#include "delete_errs.h"
X#include "pattern.h"
X#include "util.h"
X#include "directories.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	       status = retval;
X	  }
X     }
X
X     if (! status) { /* recurse only if if top-level undelete */
X		     /* succeeded or was not requested        */
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
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 12715 -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'\" \(8062 characters\)
sed "s/^X//" >'util.c' <<'END_OF_FILE'
X/*
X * $Source: /afs/athena.mit.edu/astaff/project/delete/src/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/astaff/project/delete/src/RCS/util.c,v 1.21 91/02/20 17:28:11 jik Exp $";
X#endif
X
X#include <stdio.h>
X#include <sys/param.h>
X#include <sys/types.h>
X#ifdef SYSV /* SYSV doesn't define uid_t */
Xtypedef unsigned short uid_t;
X#endif
X#include <sys/dir.h>
X#ifdef SYSV
X#include <string.h>
X#define index strchr
X#define rindex strrchr
X#else
X#include <strings.h>
X#endif /* SYSV */
X#include <pwd.h>
X#include <errno.h>
X#ifdef AFS_MOUNTPOINTS
X#include <sys/ioctl.h>
X#include <afs/param.h>
X#include <afs/vice.h>
X#include <afs/venus.h>
X#endif
X#include "delete_errs.h"
X#include "util.h"
X#include "directories.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_ctime) / 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#ifdef S_IFLNK
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#endif
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 for 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 8062 -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 4\).
cp /dev/null ark2isdone
MISSING=""
for I in 1 2 3 4 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 4 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...
-- 
Kent Landfield                   INTERNET: kent@sparky.IMD.Sterling.COM
Sterling Software, IMD           UUCP:     uunet!sparky!kent
Phone:    (402) 291-8300         FAX:      (402) 291-4362
Please send comp.sources.misc-related mail to kent@uunet.uu.net.