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$.
RevLine 
[1654]1/*
[12350]2 * $Id: delete.c,v 1.33 1999-01-22 23:08:52 ghudson Exp $
[1654]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.
[4505]9 * For copying and distribution information, see the file "mit-copying.h."
[1654]10 */
11
12#if (!defined(lint) && !defined(SABER))
[12350]13     static char rcsid_delete_c[] = "$Id: delete.c,v 1.33 1999-01-22 23:08:52 ghudson Exp $";
[1654]14#endif
15
16#include <sys/types.h>
[12144]17#ifdef HAVE_AFS
[5470]18#include <sys/time.h>
19#endif
[1654]20#include <stdio.h>
[5049]21#include <dirent.h>
[3049]22#include <string.h>
[6647]23#include <unistd.h>
24
[1654]25#include <sys/param.h>
[2184]26#include <errno.h>
27#include "errors.h"
28#include "delete_errs.h"
[1692]29#include "util.h"
30#include "delete.h"
[4505]31#include "mit-copying.h"
[1654]32
[1662]33
[1692]34
[1654]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?
[1672]47 *    yes -- is it a dotfile?
48 *           yes -- abort
49 *           no -- continue
50 *        -- is the filesonly option set?
[1654]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;
[3485]72int emulate_rm, linked_to_rm, linked_to_rmdir;
[12144]73#ifdef HAVE_AFS
[5470]74struct timeval tvp[2];
75#endif
[1654]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
[2184]87     initialize_del_error_table();
[5470]88
[12144]89#ifdef HAVE_AFS
[5470]90     gettimeofday(&tvp[0], (struct timezone *)0);
[6420]91     tvp[1] = tvp[0];
[5470]92#endif
93
[1654]94     force = interactive = recursive = noop = verbose = filesonly =
[3485]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     
[2184]102     while ((arg = getopt(argc, argv, "efirnvFD")) != -1) {
[1654]103          switch (arg) {
104          case 'r':
105               recursive++;
106               if (directoriesonly) {
[2184]107                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
108                            whoami);
109                    usage();
110                    exit(1);
[1654]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;
[2184]125          case 'e':
126               emulate_rm++;
127               break;
[1654]128          case 'F':
129               filesonly++;
130               if (directoriesonly) {
131                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
[2184]132                            whoami);
133                    usage();
134                    exit(1);
[1654]135               }
136               break;
137          case 'D':
138               directoriesonly++;
139               if (recursive) {
[2184]140                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
141                            whoami);
142                    usage();
143                    exit(1);
[1654]144               }
145               if (filesonly) {
[2184]146                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
147                            whoami);
148                    usage();
149                    exit(1);
[1654]150               }
151               break;
[1696]152          default:
153               usage();
[2184]154               exit(1);
[1654]155          }
156     }
[2184]157     report_errors = ! (force || emulate_rm);
158     
[1654]159     if (optind == argc) {
[2184]160          if (! force) {
161               fprintf(stderr, "%s: no files specified.\n", whoami);
162               usage();
163          }
164          exit(force ? 0 : 1);
[1654]165     }
166     while (optind < argc) {
[2184]167          if (delete(argv[optind], 0))
168               error(argv[optind]);
[1654]169          optind++;
170     }
[2184]171     exit(((! force) && error_occurred) ? 1 : 0);
[1654]172}
173
174
175
176usage()
177{
178     printf("Usage: %s [ options ] filename ...\n", whoami);
179     printf("Options are:\n");
[3485]180     if (! linked_to_rmdir)
181          printf("     -r     recursive\n");
[1654]182     printf("     -i     interactive\n");
183     printf("     -f     force\n");
184     printf("     -n     noop\n");
185     printf("     -v     verbose\n");
[3485]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     }
[1670]191     printf("     --     end options and start filenames\n");
[3485]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     }
[1654]196}
197
198
199
200
[1657]201delete(filename, recursed)
[1654]202char *filename;
[1657]203int recursed;
[1654]204{
205     struct stat stat_buf;
[2184]206     int retval;
207     
[1654]208     /* can the file be lstat'd? */
209     if (lstat(filename, &stat_buf) == -1) {
[2184]210          set_error(errno);
211          if (emulate_rm && (! force))
[1654]212               fprintf(stderr, "%s: %s nonexistent\n", whoami, filename);
[2184]213          error(filename);
214          return error_code;
[1654]215     }
[2184]216
[1654]217     /* is the file a directory? */
[1711]218     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
[1672]219          /* is the file a dot file? */
[2184]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;
[1672]226          }
[2184]227
[1654]228          /* is the filesonly option set? */
229          if (filesonly) {
230               /* is the recursive option specified? */
231               if (recursive) {
[2184]232                    if (retval = recursive_delete(filename, stat_buf,
233                                                  recursed)) {
234                         error(filename);
235                         return retval;
236                    }
[1654]237               }
238               else {
[2184]239                    if (emulate_rm && (! force))
[1654]240                         fprintf(stderr, "%s: %s directory\n", whoami,
241                                 filename);
[2184]242                    set_error(DELETE_CANT_DEL_DIR);
243                    error(filename);
244                    return error_code;
[1654]245               }
246          }
247          else {
248               /* is the directory empty? */
[2184]249               if ((retval = empty_directory(filename)) < 0) {
250                    error(filename);
251                    if (! emulate_rm)
252                         return error_code;
253               }
254
255               if (retval > 0) {
[1654]256                    /* remove it */
[2184]257                    if (retval = do_move(filename, stat_buf, 0)) {
258                         error(filename);
259                         return error_code;
260                    }
[1654]261               }
262               else {
263                    /* is the directoriesonly option set? */
264                    if (directoriesonly) {
[2184]265                         if (emulate_rm && (! force))
[1654]266                              fprintf(stderr, "%s: %s: Directory not empty\n",
[2184]267                                      whoami, filename);
268                         set_error(DELETE_DIR_NOT_EMPTY);
269                         error(filename);
270                         return error_code;
[1654]271                    }
272                    else {
273                         /* is the recursive option specified? */
274                         if (recursive) {
[2184]275                              if (retval = recursive_delete(filename, stat_buf,
276                                                            recursed)) {
277                                   error(filename);
278                                   return error_code;
279                              }
[1654]280                         }
281                         else {
[2184]282                              if (emulate_rm && (! force))
[1683]283                                   fprintf(stderr, "%s: %s not empty\n",
[1654]284                                           whoami, filename);
[2184]285                              set_error(DELETE_DIR_NOT_EMPTY);
286                              error(filename);
287                              return error_code;
[1654]288                         }
289                    }
290               }
291          }
292     }
293     else {
294          /* is the directoriesonly option set? */
295          if (directoriesonly) {
[2184]296               if (emulate_rm && (! force))
[1654]297                    fprintf(stderr, "%s: %s: Not a directory\n", whoami,
298                            filename);
[2184]299               set_error(DELETE_CANT_DEL_FILE);
300               error(filename);
301               return error_code;
[1654]302          }
[2184]303          else {
304               if (retval = do_move(filename, stat_buf, 0)) {
305                    error(filename);
306                    return error_code;
307               }
308          }
[1654]309     }
[2221]310     return 0;
[1654]311}
[2184]312         
[1654]313
314                 
315                         
316               
317empty_directory(filename)
318char *filename;
319{
320     DIR *dirp;
[5137]321     struct dirent *dp;
[1654]322
[2482]323     dirp = Opendir(filename);
[1654]324     if (! dirp) {
[2184]325          set_error(errno);
326          error(filename);
327          return -1;
[1654]328     }
329     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
330          if (is_dotfile(dp->d_name))
331               continue;
[1731]332          if (is_deleted(dp->d_name))
333               continue;
[1654]334          else {
335               closedir(dirp);
[2184]336               return 0;
[1654]337          }
338     }
339     closedir(dirp);
[2184]340     return 1;
[1654]341}
342
343
344
345
[1662]346recursive_delete(filename, stat_buf, recursed)
[1654]347char *filename;
[1662]348struct stat stat_buf;
[1657]349int recursed;
[1654]350{
351     DIR *dirp;
[5137]352     struct dirent *dp;
[1654]353     int status = 0;
354     char newfile[MAXPATHLEN];
[2482]355     int retval = 0;
[1654]356     
[1657]357     if (interactive && recursed) {
[1654]358          printf("%s: remove directory %s? ", whoami, filename);
[2184]359          if (! yes()) {
360               set_status(DELETE_NOT_DELETED);
361               return error_code;
362          }
[1654]363     }
[2482]364     dirp = Opendir(filename);
[1654]365     if (! dirp) {
[2184]366          if (emulate_rm && (! force))
[1654]367               fprintf(stderr, "%s: %s not changed\n", whoami, filename);
[2184]368          set_error(errno);
369          error(filename);
370          return error_code;
[1654]371     }
372     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
373          if (is_dotfile(dp->d_name))
374               continue;
[1672]375          if (is_deleted(dp->d_name))
376               continue;
[1654]377          else {
[2184]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               }
[1654]389          }
390     }
391     closedir(dirp);
[2184]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;
[1654]404}
405
406                                         
407
408
409
410
[2184]411do_move(filename, stat_buf, subs_not_deleted)
[1654]412char *filename;
413struct stat stat_buf;
[2184]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.         */
[1654]422{
423     char *last;
424     char buf[MAXPATHLEN];
425     char name[MAXNAMLEN];
426     struct stat deleted_buf;
427
[2184]428     (void) strncpy(buf, filename, MAXPATHLEN);
[1654]429     last = lastpart(buf);
430     if (strlen(last) > MAXNAMLEN) {
[2184]431          if (emulate_rm && (! force))
[1654]432               fprintf(stderr, "%s: %s: filename too long\n", whoami,
433                       filename);
[2184]434          set_error(ENAMETOOLONG);
435          error(filename);
436          return error_code;
[1654]437     }
[2184]438     (void) strcpy(name, last);
[1654]439     if (strlen(buf) + 3 > MAXPATHLEN) {
[2184]440          if (emulate_rm && (! force))
[1654]441               fprintf(stderr, "%s: %s: pathname too long\n", whoami,
442                       filename);
[2184]443          set_error(ENAMETOOLONG);
444          error(filename);
445          return error_code;
[1654]446     }
447     *last = '\0';
[2184]448     (void) strcat(buf, ".#");
449     (void) strcat(buf, name);
[1654]450     if (interactive) {
451          printf("%s: remove %s? ", whoami, filename);
[2184]452          if (! yes()) {
453               set_status(DELETE_NOT_DELETED);
454               return error_code;
455          }
[1654]456     }
[4413]457     else if ((! force)
458#ifdef S_IFLNK
459              && ((stat_buf.st_mode & S_IFMT) != S_IFLNK)
460#endif
[1711]461              && access(filename, W_OK)) {
[2184]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          }
[1654]472     }
[2184]473     if (emulate_rm && subs_not_deleted) {
474          if (! force)
475               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
476          return 1;
477     }
[1662]478     if (noop) {
479          fprintf(stderr, "%s: %s would be removed\n", whoami, filename);
[2184]480          return 0;
[1662]481     }
[2184]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     }
[1654]488     if (rename(filename, buf)) {
[2184]489          if (emulate_rm && (! force))
[1654]490               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
[2184]491          set_error(errno);
492          error(filename);
493          return error_code;
[1654]494     }
[1673]495     else {
496          if (verbose)
497               fprintf(stderr, "%s: %s removed\n", whoami, filename);
[12144]498#ifdef HAVE_AFS
[5470]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
[2184]515          return 0;
[1673]516     }
[1654]517}
518
519
520
521unlink_completely(filename)
522char *filename;
523{
524     char buf[MAXPATHLEN];
525     struct stat stat_buf;
526     DIR *dirp;
[5137]527     struct dirent *dp;
[1654]528     int status = 0;
[2184]529     int retval;
[1654]530     
[2184]531     if (lstat(filename, &stat_buf)) {
532          set_error(errno);
533          error(filename);
534          return error_code;
535     }
[1654]536
[1711]537     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
[2482]538          dirp = Opendir(filename);
[2184]539          if (! dirp) {
540               set_error(errno);
541               error(filename);
542               return error_code;
543          }
544         
[1654]545          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
[2184]546               if (is_dotfile(dp->d_name))
[1654]547                    continue;
[2184]548               (void) strcpy(buf, append(filename, dp->d_name));
549               if (! *buf) {
550                    status = error_code;
551                    error(filename);
552                    continue;
[1654]553               }
[2184]554               retval = unlink_completely(buf);
555               if (retval) {
556                    status = retval;
557                    error(filename);
558               }
[1654]559          }
560          closedir(dirp);
[2184]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          }
[1654]571     }
[2184]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     }
[2221]582     return 0;
[1654]583}
Note: See TracBrowser for help on using the repository browser.