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, ¬_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.