source: trunk/athena/bin/delete/expunge.c @ 24908

Revision 24908, 11.5 KB checked in by ghudson, 14 years ago (diff)
In delete: * Patches from Jonathan Kamens: - The "-f" flag to delete should suppress nonexistent file errors but not other errors. - When the "-v" flag is specified to expunge, the correct totals should be reported. Previously, the totals were incorrect. - Code cleanup.
Line 
1/*
2 * $Id: expunge.c,v 1.25 1999-01-22 23:08:59 ghudson Exp $
3 *
4 * This program is part of a package including delete, undelete,
5 * lsdel, expunge and purge.  The software suite is meant as a
6 * replacement for rm which allows for file recovery.
7 *
8 * Copyright (c) 1989 by the Massachusetts Institute of Technology.
9 * For copying and distribution information, see the file "mit-copying.h."
10 */
11
12#include <stdio.h>
13#include <sys/types.h>
14#include <sys/time.h>
15#include <dirent.h>
16#include <sys/param.h>
17#include <string.h>
18#include <com_err.h>
19#include <errno.h>
20#include <unistd.h>
21#include "col.h"
22#include "directories.h"
23#include "pattern.h"
24#include "expunge.h"
25#include "shell_regexp.h"
26#include "mit-copying.h"
27#include "delete_errs.h"
28#include "errors.h"
29#include "util.h"
30
31static void usage(void);
32static int purge(void), expunge(char **files, int num);
33static int process_files(char **files, int num), list_files(void);
34static int top_level(void), expunge_specified(filerec *leaf);
35static int really_do_expunge(filerec *file_ent);
36
37extern time_t current_time;
38
39extern char *whoami;
40
41time_t timev;           /* minimum mod time before undeletion */
42
43int  interactive,       /* query before each expunge */
44     recursive,         /* expunge undeleted directories recursively */
45     noop,              /* print what would be done instead of doing it */
46     verbose,           /* print a line as each file is deleted */
47     force,             /* do not ask for any confirmation */
48     listfiles,         /* list files at toplevel */
49     yield,             /* print yield of expunge at end */
50     f_links,           /* follow symbolic links */
51     f_mounts;          /* follow mount points */
52
53int space_removed = 0;
54
55
56
57
58int main(int argc, char *argv[])
59{
60     extern char *optarg;
61     extern int optind;
62     int arg;
63
64#if defined(__APPLE__) && defined(__MACH__)
65     add_error_table(&et_del_error_table);
66#else
67     initialize_del_error_table();
68#endif
69     
70     whoami = lastpart(argv[0]);
71     if (*whoami == 'p') { /* we're doing a purge */
72          if (argc > 1) {
73               set_error(PURGE_TOO_MANY_ARGS);
74               error("");
75               exit(1);
76          }
77          if (purge())
78               error("purge");
79          exit(error_occurred ? 1 : 0);
80     }
81     timev = 0;
82     yield = interactive = recursive = noop = verbose = listfiles = force = 0;
83     while ((arg = getopt(argc, argv, "t:irfnvlysm")) != EOF) {
84          switch (arg) {
85          case 't':
86               timev = atoi(optarg);
87               break;
88          case 'i':
89               interactive++;
90               break;
91          case 'r':
92               recursive++;
93               break;
94          case 'f':
95               force++;
96               break;
97          case 'n':
98               noop++;
99               break;
100          case 'v':
101               verbose++;
102               break;
103          case 'l':
104               listfiles++;
105               break;
106          case 'y':
107               yield++;
108               break;
109          case 's':
110               f_links++;
111               break;
112          case 'm':
113               f_mounts++;
114               break;
115          default:
116               usage();
117               exit(1);
118          }
119     }
120     report_errors = ! force;
121     
122     if (optind == argc) {
123          char *dir;
124          dir = "."; /* current working directory */
125          if (expunge(&dir, 1))
126               error("expunging .");
127     }
128     else if (expunge(&argv[optind], argc - optind))
129          error("expunge");
130
131     exit((error_occurred && (! force)) ? 1 : 0);
132}
133
134
135
136
137
138static int purge()
139{
140     char *home;
141     int retval;
142     
143     home = Malloc((unsigned) MAXPATHLEN);
144     if (! home) {
145          set_error(errno);
146          error("purge");
147          return error_code;
148     }
149     timev = interactive = noop = verbose = force = 0;
150     yield = listfiles = recursive = 1;
151     if ((retval = get_home(home))) {
152          error("purge");
153          return retval;
154     }
155
156     printf("Please be patient.... this may take a while.\n\n");
157
158     if ((retval = expunge(&home, 1))) {
159          error("expunge");
160          return retval;
161     }
162     return 0;
163}
164
165
166
167
168static void usage()
169{
170     fprintf(stderr, "Usage: %s [ options ] [ filename [ ... ]]\n", whoami);
171     fprintf(stderr, "Options are:\n");
172     fprintf(stderr, "     -r     recursive\n");
173     fprintf(stderr, "     -i     interactive\n");
174     fprintf(stderr, "     -f     force\n");
175     fprintf(stderr, "     -t n   n-day-or-older expunge\n");
176     fprintf(stderr, "     -n     noop\n");
177     fprintf(stderr, "     -v     verbose\n");
178     fprintf(stderr, "     -l     list files before expunging\n");
179     fprintf(stderr, "     -s     follow symbolic links to directories\n");
180     fprintf(stderr, "     -m     follow mount points\n");
181     fprintf(stderr, "     -y     print yield of expunge\n");
182     fprintf(stderr, "     --     end options and start filenames\n");
183}
184
185
186
187
188
189static int expunge(char **files, int num)
190{
191     char **found_files;
192     int num_found;
193     int status = 0;
194     int total = 0;
195     filerec *current;
196     int retval;
197     
198     if (initialize_tree())
199          exit(1);
200
201     for ( ; num ; num--) {
202          retval = get_the_files(files[num - 1], &num_found, &found_files);
203          if (retval) {
204               error(files[num - 1]);
205               return retval;
206          }
207               
208          if (num_found) {
209               num_found = process_files(found_files, num_found);
210               if (num_found < 0) {
211                    error("process_files");
212                    return error_code;
213               }
214          }
215         
216          total += num_found;
217          if (! num_found) if (! force) {
218               /*
219                * There are three different situations here.  Eiter we
220                * are dealing with an existing directory with no
221                * deleted files in it, or we are deleting with a
222                * non-existing deleted file with wildcards, or we are
223                * dealing with a non-existing deleted file without
224                * wildcards.  In the former case we print nothing, and
225                * in the latter cases we print either "no match" or
226                * "not found" respectively
227                */
228               if (no_wildcards(files[num - 1])) {
229                    if (! directory_exists(files[num - 1])) {
230                         set_error(ENOENT);
231                         error(files[num - 1]);
232                    }
233               }
234               else {
235                    set_error(DELETE_ENOMATCH);
236                    error(files[num - 1]);
237               }
238          }
239     }
240     if (total && listfiles) {
241       if ((retval = list_files())) {
242         error("list_files");
243         return retval;
244       }
245       if (! (force || top_level())) {
246         set_status(EXPUNGE_NOT_EXPUNGED);
247         return error_code;
248       }
249     }
250     current = get_root_tree();
251     if (current) {
252       if ((retval = expunge_specified(current))) {
253         error("expunge_specified");
254         status = retval;
255       }
256     }
257     current = get_cwd_tree();
258     if (current) {
259       if ((retval = expunge_specified(current))) {
260         error("expunge_specified");
261         status = retval;
262       }
263     }
264     if (yield) {
265       char *friendly = space_to_friendly(space_removed);
266       if (noop)
267         printf("Total that would be expunged: %s\n", friendly);
268       else
269         printf("Total expunged: %s\n", friendly);
270       free(friendly);
271     }
272     return status;
273}
274
275
276
277static int expunge_specified(filerec *leaf)
278{
279     int status = 0;
280     int do_it = 1;
281     int retval;
282     
283     if ((leaf->specified) && ((leaf->specs.st_mode & S_IFMT) == S_IFDIR)) {
284          /*
285           * This is static so that we don't create a copy of it for
286           * every recursive invocation of expunge_specified.
287           */
288          static char buf[MAXPATHLEN];
289
290          if ((retval = get_leaf_path(leaf, buf))) {
291               error("get_leaf_path");
292               return retval;
293          }
294          (void) convert_to_user_name(buf, buf);
295
296          if (interactive) {
297               printf("%s: Expunge directory %s? ", whoami, buf);
298               status = (! (do_it = yes()));
299          }
300     }
301     if (do_it) {
302          if (leaf->dirs) {
303            if ((retval = expunge_specified(leaf->dirs))) {
304              error("expunge_specified");
305              status = retval;
306            }
307          }
308          if (leaf->files) {
309            if ((retval = expunge_specified(leaf->files))) {
310              error("expunge_specified");
311              status = retval;
312            }
313          }
314     }
315     if (leaf->specified && (! status)) {
316       if ((retval = really_do_expunge(leaf))) {
317         error("really_do_expunge");
318         status = retval;
319       }
320     }
321     if (leaf->next) {
322       if ((retval = expunge_specified(leaf->next))) {
323         error("expunge_specified");
324         status = retval;
325       }
326     }
327
328     free_leaf(leaf);
329     return status;
330}
331
332
333static int process_files(char **files, int num)
334{
335     int i, skipped = 0;
336     filerec *leaf;
337     
338     for (i = 0; i < num; i++) {
339          if (add_path_to_tree(files[i], &leaf)) {
340               error("add_path_to_tree");
341               return -1;
342          }
343          free(files[i]);
344          if (! timed_out(leaf, current_time, timev)) {
345               free_leaf(leaf);
346               skipped++;
347          }
348     }
349     free((char *) files);
350     return(num-skipped);
351}
352
353
354
355
356
357
358
359
360
361static int really_do_expunge(filerec *file_ent)
362{
363     char real[MAXPATHLEN], user[MAXPATHLEN];
364     int status;
365     int retval;
366     
367     if ((retval = get_leaf_path(file_ent, real))) {
368          error("get_leaf_path");
369          return retval;
370     }
371     (void) convert_to_user_name(real, user);
372
373     if (interactive) {
374       char *friendly = specs_to_friendly(file_ent->specs);
375       printf ("%s: Expunge %s (%s)? ", whoami, user, friendly);
376       free(friendly);
377          if (! yes()) {
378               set_status(EXPUNGE_NOT_EXPUNGED);
379               return error_code;
380          }
381     }
382
383     if (noop) {
384          space_removed += specs_to_space(file_ent->specs);
385          char *friendly = space_to_friendly(space_removed);
386          char *friendly2 = specs_to_friendly(file_ent->specs);
387          printf("%s: %s (%s) would be expunged (%s total)\n", whoami, user,
388                 friendly2, friendly);
389          free(friendly);
390          free(friendly2);
391          return 0;
392     }
393
394     if ((file_ent->specs.st_mode & S_IFMT) == S_IFDIR)
395          status = rmdir(real);
396     else
397          status = unlink(real);
398     if (! status) {
399          space_removed += specs_to_space(file_ent->specs);
400          if (verbose) {
401            char *friendly = space_to_friendly(space_removed);
402            char *friendly2 = specs_to_friendly(file_ent->specs);
403            printf("%s: %s (%s) expunged (%s total)\n", whoami, user,
404                   friendly2, friendly);
405            free(friendly);
406            free(friendly2);
407          }
408          return 0;
409     }
410     else {
411          set_error(errno);
412          error(real);
413          return error_code;
414     }
415}
416
417
418
419
420
421
422
423
424
425static int top_level()
426{
427     if (interactive) {
428printf("The above files, which have been marked for deletion, are about to be\n");
429printf("expunged forever!  You will be asked for confirmation before each file is\n");
430printf("deleted.  Do you wish to continue [return = no]? ");
431     }
432     else {
433printf("The above files, which have been marked for deletion, are about to be\n");
434printf("expunged forever!  Make sure you don't need any of them before continuing.\n");
435printf("Do you wish to continue [return = no]? ");
436     }
437     return (yes());
438}
439
440
441
442
443
444static int list_files()
445{
446     filerec *current;
447     char **strings;
448     int num;
449     int retval;
450     
451     strings = (char **) Malloc(sizeof(char *));
452     num = 0;
453     if (! strings) {
454          set_error(errno);
455          error("Malloc");
456          return error_code;
457     }
458
459     printf("The following deleted files are going to be expunged: \n\n");
460
461     current = get_root_tree();
462     if ((retval = accumulate_names(current, &strings, &num))) {
463          error("accumulate_names");
464          return retval;
465     }
466     current = get_cwd_tree();
467     if ((retval = accumulate_names(current, &strings, &num))) {
468          error("accumulate_names");
469          return retval;
470     }
471     if ((retval = column_array(strings, num, DEF_SCR_WIDTH, 0, 0, 2, 1, 0,
472                                1, stdout))) {
473          error("column_array");
474          return retval;
475     }
476     
477     printf("\n");
478     return(0);
479}
480     
481
482
483
484
485int get_the_files(name, num_found, found)
486char *name;
487int *num_found;
488char ***found;
489{
490     int retval;
491     int options;
492     
493     options = FIND_DELETED | FIND_CONTENTS | RECURS_DELETED;
494     if (recursive)
495          options |= RECURS_FIND_DELETED;
496     if (f_mounts)
497          options |= FOLLW_MOUNTPOINTS;
498     if (f_links)
499          options |= FOLLW_LINKS;
500     
501     retval = find_matches(name, num_found, found, options);
502     if (retval) {
503          error("find_matches");
504          return retval;
505     }
506
507     return 0;
508}
Note: See TracBrowser for help on using the repository browser.