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

Revision 24908, 14.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: delete.c,v 1.33 1999-01-22 23:08:52 ghudson Exp $
3 *
4 * This program is a replacement for rm.  Instead of actually deleting
5 * files, it marks them for deletion by prefixing them with a ".#"
6 * prefix.
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 <sys/types.h>
13#ifdef HAVE_AFS
14#include <sys/time.h>
15#endif
16#include <stdio.h>
17#include <dirent.h>
18#include <string.h>
19#include <unistd.h>
20
21#include <sys/param.h>
22#include <errno.h>
23#include "errors.h"
24#include "delete_errs.h"
25#include "pattern.h"
26#include "delete.h"
27#include "mit-copying.h"
28#include "util.h"
29
30/*
31 * ALGORITHM:
32 *
33 * 1. Parse command-line arguments and set flags.
34 * 2. Call the function delete() for each filename command-line argument.
35 *
36 * delete():
37 *
38 * 1. Can the file be lstat'd?
39 *    no -- abort
40 *    yes -- continue
41 * 2. Is the file a directory?
42 *    yes -- is it a dotfile?
43 *           yes -- abort
44 *           no -- continue
45 *        -- is the filesonly option set?
46 *           yes -- is the recursive option specified?
47 *                  yes -- continue
48 *                  no -- abort
49 *           no -- is the directory empty?
50 *                  yes -- remove it
51 *                  no -- is the directoriesonly option set?
52 *                        yes -- abort
53 *                        no -- continue
54 *                     -- is the recursive option specified?
55 *                        yes -- continue
56 *                        no -- abort
57 *    no -- is the directoriesonly option set?
58 *          yes -- abort
59 *          no -- continue
60 * 3. If the file is a file, remove it.
61 * 4. If the file is a directory, open it and pass each of its members
62 *    (excluding . files) to delete().
63 */
64
65static void usage(void);
66static int delete(char *filename, int recursed);
67static int recursive_delete(char *filename, struct stat stat_buf, int recursed);
68static int empty_directory(char *filename);
69static int do_move(char *filename, struct stat stat_buf, int subs_not_deleted);
70static int unlink_completely(char *filename);
71
72int force, interactive, recursive, noop, verbose, filesonly, directoriesonly;
73int emulate_rm, linked_to_rm, linked_to_rmdir;
74#ifdef HAVE_AFS
75struct timeval tvp[2];
76#endif
77
78int main(int argc, char *argv[])
79{
80     extern char *optarg;
81     extern int optind;
82     int arg;
83     
84     whoami = lastpart(argv[0]);
85
86#if defined(__APPLE__) && defined(__MACH__)
87     add_error_table(&et_del_error_table);
88#else
89     initialize_del_error_table();
90#endif
91
92#ifdef HAVE_AFS
93     gettimeofday(&tvp[0], (struct timezone *)0);
94     tvp[1] = tvp[0];
95#endif
96
97     force = interactive = recursive = noop = verbose = filesonly =
98          directoriesonly = emulate_rm = linked_to_rm = linked_to_rmdir = 0;
99
100     if (!strcmp(whoami, "rm"))
101          emulate_rm++, filesonly++, linked_to_rm++;
102     if (!strcmp(whoami, "rmdir") || !strcmp(whoami, "rd"))
103          emulate_rm++, directoriesonly++, linked_to_rmdir++;
104     
105     while ((arg = getopt(argc, argv, "efirnvFD")) != -1) {
106          switch (arg) {
107          case 'r':
108               recursive++;
109               if (directoriesonly) {
110                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
111                            whoami);
112                    usage();
113                    exit(1);
114               }
115               break;
116          case 'f':
117               force++;
118               break;
119          case 'i':
120               interactive++;
121               break;
122          case 'n':
123               noop++;
124               break;
125          case 'v':
126               verbose++;
127               break;
128          case 'e':
129               emulate_rm++;
130               break;
131          case 'F':
132               filesonly++;
133               if (directoriesonly) {
134                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
135                            whoami);
136                    usage();
137                    exit(1);
138               }
139               break;
140          case 'D':
141               directoriesonly++;
142               if (recursive) {
143                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
144                            whoami);
145                    usage();
146                    exit(1);
147               }
148               if (filesonly) {
149                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
150                            whoami);
151                    usage();
152                    exit(1);
153               }
154               break;
155          default:
156               usage();
157               exit(1);
158          }
159     }
160     report_errors = ! emulate_rm;
161     
162     if (optind == argc) {
163          if (! force) {
164               fprintf(stderr, "%s: no files specified.\n", whoami);
165               usage();
166          }
167          exit(force ? 0 : 1);
168     }
169     while (optind < argc) {
170          if (delete(argv[optind], 0))
171               error(argv[optind]);
172          optind++;
173     }
174     exit(error_occurred ? 1 : 0);
175}
176
177
178
179static void usage()
180{
181     printf("Usage: %s [ options ] filename ...\n", whoami);
182     printf("Options are:\n");
183     if (! linked_to_rmdir)
184          printf("     -r     recursive\n");
185     printf("     -i     interactive\n");
186     printf("     -f     force\n");
187     printf("     -n     noop\n");
188     printf("     -v     verbose\n");
189     if (! (linked_to_rmdir || linked_to_rm)) {
190          printf("     -e     emulate rm/rmdir\n");
191          printf("     -F     files only\n");
192          printf("     -D     directories only\n");
193     }
194     printf("     --     end options and start filenames\n");
195     if (! (linked_to_rmdir || linked_to_rm)) {
196          printf("-r and -D are mutually exclusive\n");
197          printf("-F and -D are mutually exclusive\n");
198     }
199}
200
201
202
203
204static int delete(char *filename, int recursed)
205{
206     struct stat stat_buf;
207     int retval;
208     
209     /* can the file be lstat'd? */
210     if (lstat(filename, &stat_buf) == -1) {
211          if (! force) {
212            set_error(errno);
213            if (emulate_rm)
214              fprintf(stderr, "%s: %s nonexistent\n", whoami, filename);
215            error(filename);
216            return error_code;
217          }
218          return 0;
219     }
220
221     /* is the file a directory? */
222     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
223          /* is the file a dot file? */
224          if (is_dotfile(lastpart(filename))) {
225               set_error(DELETE_IS_DOTFILE);
226               if (emulate_rm && (! force))
227                    fprintf(stderr, "%s: cannot remove `.' or `..'\n", whoami);
228               error(filename);
229               return error_code;
230          }
231
232          /* is the filesonly option set? */
233          if (filesonly) {
234               /* is the recursive option specified? */
235               if (recursive) {
236                 if ((retval = recursive_delete(filename, stat_buf,
237                                                recursed))) {
238                   error(filename);
239                   return retval;
240                 }
241               }
242               else {
243                    if (emulate_rm && (! force))
244                         fprintf(stderr, "%s: %s directory\n", whoami,
245                                 filename);
246                    set_error(DELETE_CANT_DEL_DIR);
247                    error(filename);
248                    return error_code;
249               }
250          }
251          else {
252               /* is the directory empty? */
253               if ((retval = empty_directory(filename)) < 0) {
254                    error(filename);
255                    if (! emulate_rm)
256                         return error_code;
257               }
258
259               if (retval > 0) {
260                 /* remove it */
261                 if ((retval = do_move(filename, stat_buf, 0))) {
262                   error(filename);
263                   return error_code;
264                 }
265               }
266               else {
267                    /* is the directoriesonly option set? */
268                    if (directoriesonly) {
269                         if (emulate_rm && (! force))
270                              fprintf(stderr, "%s: %s: Directory not empty\n",
271                                      whoami, filename);
272                         set_error(DELETE_DIR_NOT_EMPTY);
273                         error(filename);
274                         return error_code;
275                    }
276                    else {
277                         /* is the recursive option specified? */
278                         if (recursive) {
279                           if ((retval = recursive_delete(filename, stat_buf,
280                                                          recursed))) {
281                             error(filename);
282                             return error_code;
283                           }
284                         }
285                         else {
286                              if (emulate_rm && (! force))
287                                   fprintf(stderr, "%s: %s not empty\n",
288                                           whoami, filename);
289                              set_error(DELETE_DIR_NOT_EMPTY);
290                              error(filename);
291                              return error_code;
292                         }
293                    }
294               }
295          }
296     }
297     else {
298          /* is the directoriesonly option set? */
299          if (directoriesonly) {
300               if (emulate_rm && (! force))
301                    fprintf(stderr, "%s: %s: Not a directory\n", whoami,
302                            filename);
303               set_error(DELETE_CANT_DEL_FILE);
304               error(filename);
305               return error_code;
306          }
307          else {
308            if ((retval = do_move(filename, stat_buf, 0))) {
309              error(filename);
310              return error_code;
311            }
312          }
313     }
314     return 0;
315}
316         
317
318                 
319                         
320               
321static int empty_directory(char *filename)
322{
323     DIR *dirp;
324     struct dirent *dp;
325
326     dirp = Opendir(filename);
327     if (! dirp) {
328          set_error(errno);
329          error(filename);
330          return -1;
331     }
332     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
333          if (is_dotfile(dp->d_name))
334               continue;
335          if (is_deleted(dp->d_name))
336               continue;
337          else {
338               closedir(dirp);
339               return 0;
340          }
341     }
342     closedir(dirp);
343     return 1;
344}
345
346
347
348
349static int recursive_delete(char *filename, struct stat stat_buf, int recursed)
350{
351     DIR *dirp;
352     struct dirent *dp;
353     int status = 0;
354     char newfile[MAXPATHLEN];
355     int retval = 0;
356     char **dir_contents = 0;
357     int num_dir_contents = 0, dir_contents_size = 0;
358
359     if (interactive && recursed) {
360          printf("%s: remove directory %s? ", whoami, filename);
361          if (! yes()) {
362               set_status(DELETE_NOT_DELETED);
363               return error_code;
364          }
365     }
366     dirp = Opendir(filename);
367     if (! dirp) {
368          if (emulate_rm && (! force))
369               fprintf(stderr, "%s: %s not changed\n", whoami, filename);
370          set_error(errno);
371          error(filename);
372          return error_code;
373     }
374     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
375          if (is_dotfile(dp->d_name))
376               continue;
377          if (is_deleted(dp->d_name))
378               continue;
379          (void) strcpy(newfile, append(filename, dp->d_name));
380          if (! *newfile) {
381            error(filename);
382            status = error_code;
383            break;
384          }
385          if ((retval = add_str(&dir_contents, num_dir_contents,
386                                &dir_contents_size, newfile))) {
387            error(filename);
388            status = retval;
389            break;
390          }
391          num_dir_contents++;
392     }
393
394     if (!status && num_dir_contents) {
395       int i;
396       for (i = 0; i < num_dir_contents; i++) {
397         retval = delete(dir_contents[i], 1);
398         if (retval) {
399           error(dir_contents[i]);
400           status = retval;
401         }
402       }
403     }
404
405     free_list(dir_contents, num_dir_contents);
406     closedir(dirp);
407
408     if (status && (! emulate_rm)) {
409          set_warning(DELETE_DIR_NOT_EMPTY);
410          error(filename);
411     }
412     else
413          retval = do_move(filename, stat_buf, status);
414     
415     if (retval)
416          status = retval;
417
418     return status;
419}
420
421                                         
422
423
424
425
426/*
427 * If the file in question is a directory, and there is something
428 * underneath it that hasn't been removed, then subs_not_deleted will
429 * be set to true.  The program asks if the user wants to delete the
430 * directory, and if the user says yes, checks the value of
431 * subs_not_deleted.  If it's true, an error results.  This is used
432 * only when emulating rm.
433 */
434static int do_move(char *filename, struct stat stat_buf, int subs_not_deleted)
435{
436     char *last;
437     char buf[MAXPATHLEN];
438     char name[MAXNAMLEN];
439     struct stat deleted_buf;
440
441     (void) strncpy(buf, filename, MAXPATHLEN);
442     last = lastpart(buf);
443     if (strlen(last) > MAXNAMLEN) {
444          if (emulate_rm && (! force))
445               fprintf(stderr, "%s: %s: filename too long\n", whoami,
446                       filename);
447          set_error(ENAMETOOLONG);
448          error(filename);
449          return error_code;
450     }
451     (void) strcpy(name, last);
452     if (strlen(buf) + 3 > MAXPATHLEN) {
453          if (emulate_rm && (! force))
454               fprintf(stderr, "%s: %s: pathname too long\n", whoami,
455                       filename);
456          set_error(ENAMETOOLONG);
457          error(filename);
458          return error_code;
459     }
460     *last = '\0';
461     (void) strcat(buf, ".#");
462     (void) strcat(buf, name);
463     if (interactive) {
464          printf("%s: remove %s? ", whoami, filename);
465          if (! yes()) {
466               set_status(DELETE_NOT_DELETED);
467               return error_code;
468          }
469     }
470     else if ((! force)
471#ifdef S_IFLNK
472              && ((stat_buf.st_mode & S_IFMT) != S_IFLNK)
473#endif
474              && access(filename, W_OK)) {
475          if (emulate_rm)
476               printf("%s: override protection %o for %s? ", whoami,
477                      stat_buf.st_mode & 0777, filename);
478          else
479               printf("%s: File %s not writeable.  Delete anyway? ", whoami,
480                      filename);
481          if (! yes()) {
482               set_status(DELETE_NOT_DELETED);
483               return error_code;
484          }
485     }
486     if (emulate_rm && subs_not_deleted) {
487          if (! force)
488               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
489          return 1;
490     }
491     if (noop) {
492          fprintf(stderr, "%s: %s would be removed\n", whoami, filename);
493          return 0;
494     }
495     if ((! lstat(buf, &deleted_buf)) && unlink_completely(buf)) {
496          if (emulate_rm && (! force))
497               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
498          error(filename);
499          return error_code;
500     }
501     if (rename(filename, buf)) {
502          if (emulate_rm && (! force))
503               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
504          set_error(errno);
505          error(filename);
506          return error_code;
507     }
508     else {
509          if (verbose)
510               fprintf(stderr, "%s: %s removed\n", whoami, filename);
511#ifdef HAVE_AFS
512          /*
513           * Normally, expunge uses the ctime to determine how long
514           * ago a file was deleted (since the ctime is normally
515           * updated when a file is renamed).  However, in AFS,
516           * renaming a file does not change the ctime, mtime OR
517           * atime, so we have to use utimes to force the change.
518           * This unfortunately causes the loss of the real mtime, but
519           * there's nothing we can do about that, if we want expunge
520           * to be able to do the right thing.
521           *
522           * Don't bother checking for errors, because we can't do
523           * anything about them anyway, and in any case, this isn't a
524           * *really* important operation.
525           */
526          utimes(buf, tvp);
527#endif
528          return 0;
529     }
530}
531
532
533
534static int unlink_completely(char *filename)
535{
536     char buf[MAXPATHLEN];
537     struct stat stat_buf;
538     DIR *dirp;
539     struct dirent *dp;
540     int status = 0;
541     int retval;
542     
543     if (lstat(filename, &stat_buf)) {
544          set_error(errno);
545          error(filename);
546          return error_code;
547     }
548
549     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
550          dirp = Opendir(filename);
551          if (! dirp) {
552               set_error(errno);
553               error(filename);
554               return error_code;
555          }
556         
557          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
558               if (is_dotfile(dp->d_name))
559                    continue;
560               (void) strcpy(buf, append(filename, dp->d_name));
561               if (! *buf) {
562                    status = error_code;
563                    error(filename);
564                    continue;
565               }
566               retval = unlink_completely(buf);
567               if (retval) {
568                    status = retval;
569                    error(filename);
570               }
571          }
572          closedir(dirp);
573
574          if (status)
575               return status;
576
577          retval = rmdir(filename);
578          if (retval) {
579               set_error(errno);
580               error(filename);
581               return errno;
582          }
583     }
584     else {
585          retval = unlink(filename);
586          if (retval) {
587               set_error(errno);
588               error(filename);
589               return error_code;
590          }
591          else
592               return 0;
593     }
594     return 0;
595}
Note: See TracBrowser for help on using the repository browser.