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

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