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

Revision 24908, 14.5 KB checked in by ghudson, 13 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.
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#include <sys/types.h>
[12144]13#ifdef HAVE_AFS
[5470]14#include <sys/time.h>
15#endif
[1654]16#include <stdio.h>
[5049]17#include <dirent.h>
[3049]18#include <string.h>
[6647]19#include <unistd.h>
20
[1654]21#include <sys/param.h>
[2184]22#include <errno.h>
23#include "errors.h"
24#include "delete_errs.h"
[24908]25#include "pattern.h"
[1692]26#include "delete.h"
[4505]27#include "mit-copying.h"
[24908]28#include "util.h"
[1654]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?
[1672]42 *    yes -- is it a dotfile?
43 *           yes -- abort
44 *           no -- continue
45 *        -- is the filesonly option set?
[1654]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
[24908]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);
[1654]71
72int force, interactive, recursive, noop, verbose, filesonly, directoriesonly;
[3485]73int emulate_rm, linked_to_rm, linked_to_rmdir;
[12144]74#ifdef HAVE_AFS
[5470]75struct timeval tvp[2];
76#endif
[1654]77
[24908]78int main(int argc, char *argv[])
[1654]79{
80     extern char *optarg;
81     extern int optind;
82     int arg;
83     
84     whoami = lastpart(argv[0]);
85
[23271]86#if defined(__APPLE__) && defined(__MACH__)
87     add_error_table(&et_del_error_table);
88#else
[2184]89     initialize_del_error_table();
[23271]90#endif
[5470]91
[12144]92#ifdef HAVE_AFS
[5470]93     gettimeofday(&tvp[0], (struct timezone *)0);
[6420]94     tvp[1] = tvp[0];
[5470]95#endif
96
[1654]97     force = interactive = recursive = noop = verbose = filesonly =
[3485]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     
[2184]105     while ((arg = getopt(argc, argv, "efirnvFD")) != -1) {
[1654]106          switch (arg) {
107          case 'r':
108               recursive++;
109               if (directoriesonly) {
[2184]110                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
111                            whoami);
112                    usage();
113                    exit(1);
[1654]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;
[2184]128          case 'e':
129               emulate_rm++;
130               break;
[1654]131          case 'F':
132               filesonly++;
133               if (directoriesonly) {
134                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
[2184]135                            whoami);
136                    usage();
137                    exit(1);
[1654]138               }
139               break;
140          case 'D':
141               directoriesonly++;
142               if (recursive) {
[2184]143                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
144                            whoami);
145                    usage();
146                    exit(1);
[1654]147               }
148               if (filesonly) {
[2184]149                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
150                            whoami);
151                    usage();
152                    exit(1);
[1654]153               }
154               break;
[1696]155          default:
156               usage();
[2184]157               exit(1);
[1654]158          }
159     }
[24908]160     report_errors = ! emulate_rm;
[2184]161     
[1654]162     if (optind == argc) {
[2184]163          if (! force) {
164               fprintf(stderr, "%s: no files specified.\n", whoami);
165               usage();
166          }
167          exit(force ? 0 : 1);
[1654]168     }
169     while (optind < argc) {
[2184]170          if (delete(argv[optind], 0))
171               error(argv[optind]);
[1654]172          optind++;
173     }
[24908]174     exit(error_occurred ? 1 : 0);
[1654]175}
176
177
178
[24908]179static void usage()
[1654]180{
181     printf("Usage: %s [ options ] filename ...\n", whoami);
182     printf("Options are:\n");
[3485]183     if (! linked_to_rmdir)
184          printf("     -r     recursive\n");
[1654]185     printf("     -i     interactive\n");
186     printf("     -f     force\n");
187     printf("     -n     noop\n");
188     printf("     -v     verbose\n");
[3485]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     }
[1670]194     printf("     --     end options and start filenames\n");
[3485]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     }
[1654]199}
200
201
202
203
[24908]204static int delete(char *filename, int recursed)
[1654]205{
206     struct stat stat_buf;
[2184]207     int retval;
208     
[1654]209     /* can the file be lstat'd? */
210     if (lstat(filename, &stat_buf) == -1) {
[24908]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;
[1654]219     }
[2184]220
[1654]221     /* is the file a directory? */
[1711]222     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
[1672]223          /* is the file a dot file? */
[2184]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;
[1672]230          }
[2184]231
[1654]232          /* is the filesonly option set? */
233          if (filesonly) {
234               /* is the recursive option specified? */
235               if (recursive) {
[24908]236                 if ((retval = recursive_delete(filename, stat_buf,
237                                                recursed))) {
238                   error(filename);
239                   return retval;
240                 }
[1654]241               }
242               else {
[2184]243                    if (emulate_rm && (! force))
[1654]244                         fprintf(stderr, "%s: %s directory\n", whoami,
245                                 filename);
[2184]246                    set_error(DELETE_CANT_DEL_DIR);
247                    error(filename);
248                    return error_code;
[1654]249               }
250          }
251          else {
252               /* is the directory empty? */
[2184]253               if ((retval = empty_directory(filename)) < 0) {
254                    error(filename);
255                    if (! emulate_rm)
256                         return error_code;
257               }
258
259               if (retval > 0) {
[24908]260                 /* remove it */
261                 if ((retval = do_move(filename, stat_buf, 0))) {
262                   error(filename);
263                   return error_code;
264                 }
[1654]265               }
266               else {
267                    /* is the directoriesonly option set? */
268                    if (directoriesonly) {
[2184]269                         if (emulate_rm && (! force))
[1654]270                              fprintf(stderr, "%s: %s: Directory not empty\n",
[2184]271                                      whoami, filename);
272                         set_error(DELETE_DIR_NOT_EMPTY);
273                         error(filename);
274                         return error_code;
[1654]275                    }
276                    else {
277                         /* is the recursive option specified? */
278                         if (recursive) {
[24908]279                           if ((retval = recursive_delete(filename, stat_buf,
280                                                          recursed))) {
281                             error(filename);
282                             return error_code;
283                           }
[1654]284                         }
285                         else {
[2184]286                              if (emulate_rm && (! force))
[1683]287                                   fprintf(stderr, "%s: %s not empty\n",
[1654]288                                           whoami, filename);
[2184]289                              set_error(DELETE_DIR_NOT_EMPTY);
290                              error(filename);
291                              return error_code;
[1654]292                         }
293                    }
294               }
295          }
296     }
297     else {
298          /* is the directoriesonly option set? */
299          if (directoriesonly) {
[2184]300               if (emulate_rm && (! force))
[1654]301                    fprintf(stderr, "%s: %s: Not a directory\n", whoami,
302                            filename);
[2184]303               set_error(DELETE_CANT_DEL_FILE);
304               error(filename);
305               return error_code;
[1654]306          }
[2184]307          else {
[24908]308            if ((retval = do_move(filename, stat_buf, 0))) {
309              error(filename);
310              return error_code;
311            }
[2184]312          }
[1654]313     }
[2221]314     return 0;
[1654]315}
[2184]316         
[1654]317
318                 
319                         
320               
[24908]321static int empty_directory(char *filename)
[1654]322{
323     DIR *dirp;
[5137]324     struct dirent *dp;
[1654]325
[2482]326     dirp = Opendir(filename);
[1654]327     if (! dirp) {
[2184]328          set_error(errno);
329          error(filename);
330          return -1;
[1654]331     }
332     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
333          if (is_dotfile(dp->d_name))
334               continue;
[1731]335          if (is_deleted(dp->d_name))
336               continue;
[1654]337          else {
338               closedir(dirp);
[2184]339               return 0;
[1654]340          }
341     }
342     closedir(dirp);
[2184]343     return 1;
[1654]344}
345
346
347
348
[24908]349static int recursive_delete(char *filename, struct stat stat_buf, int 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;
[23659]356     char **dir_contents = 0;
357     int num_dir_contents = 0, dir_contents_size = 0;
[24908]358
[1657]359     if (interactive && recursed) {
[1654]360          printf("%s: remove directory %s? ", whoami, filename);
[2184]361          if (! yes()) {
362               set_status(DELETE_NOT_DELETED);
363               return error_code;
364          }
[1654]365     }
[2482]366     dirp = Opendir(filename);
[1654]367     if (! dirp) {
[2184]368          if (emulate_rm && (! force))
[1654]369               fprintf(stderr, "%s: %s not changed\n", whoami, filename);
[2184]370          set_error(errno);
371          error(filename);
372          return error_code;
[1654]373     }
374     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
375          if (is_dotfile(dp->d_name))
376               continue;
[1672]377          if (is_deleted(dp->d_name))
378               continue;
[23659]379          (void) strcpy(newfile, append(filename, dp->d_name));
380          if (! *newfile) {
381            error(filename);
382            status = error_code;
383            break;
[1654]384          }
[23659]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++;
[1654]392     }
[23659]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);
[1654]406     closedir(dirp);
[2184]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;
[1654]419}
420
421                                         
422
423
424
425
[24908]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)
[1654]435{
436     char *last;
437     char buf[MAXPATHLEN];
438     char name[MAXNAMLEN];
439     struct stat deleted_buf;
440
[2184]441     (void) strncpy(buf, filename, MAXPATHLEN);
[1654]442     last = lastpart(buf);
443     if (strlen(last) > MAXNAMLEN) {
[2184]444          if (emulate_rm && (! force))
[1654]445               fprintf(stderr, "%s: %s: filename too long\n", whoami,
446                       filename);
[2184]447          set_error(ENAMETOOLONG);
448          error(filename);
449          return error_code;
[1654]450     }
[2184]451     (void) strcpy(name, last);
[1654]452     if (strlen(buf) + 3 > MAXPATHLEN) {
[2184]453          if (emulate_rm && (! force))
[1654]454               fprintf(stderr, "%s: %s: pathname too long\n", whoami,
455                       filename);
[2184]456          set_error(ENAMETOOLONG);
457          error(filename);
458          return error_code;
[1654]459     }
460     *last = '\0';
[2184]461     (void) strcat(buf, ".#");
462     (void) strcat(buf, name);
[1654]463     if (interactive) {
464          printf("%s: remove %s? ", whoami, filename);
[2184]465          if (! yes()) {
466               set_status(DELETE_NOT_DELETED);
467               return error_code;
468          }
[1654]469     }
[4413]470     else if ((! force)
471#ifdef S_IFLNK
472              && ((stat_buf.st_mode & S_IFMT) != S_IFLNK)
473#endif
[1711]474              && access(filename, W_OK)) {
[2184]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          }
[1654]485     }
[2184]486     if (emulate_rm && subs_not_deleted) {
487          if (! force)
488               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
489          return 1;
490     }
[1662]491     if (noop) {
492          fprintf(stderr, "%s: %s would be removed\n", whoami, filename);
[2184]493          return 0;
[1662]494     }
[2184]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     }
[1654]501     if (rename(filename, buf)) {
[2184]502          if (emulate_rm && (! force))
[1654]503               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
[2184]504          set_error(errno);
505          error(filename);
506          return error_code;
[1654]507     }
[1673]508     else {
509          if (verbose)
510               fprintf(stderr, "%s: %s removed\n", whoami, filename);
[12144]511#ifdef HAVE_AFS
[5470]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
[2184]528          return 0;
[1673]529     }
[1654]530}
531
532
533
[24908]534static int unlink_completely(char *filename)
[1654]535{
536     char buf[MAXPATHLEN];
537     struct stat stat_buf;
538     DIR *dirp;
[5137]539     struct dirent *dp;
[1654]540     int status = 0;
[2184]541     int retval;
[1654]542     
[2184]543     if (lstat(filename, &stat_buf)) {
544          set_error(errno);
545          error(filename);
546          return error_code;
547     }
[1654]548
[1711]549     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
[2482]550          dirp = Opendir(filename);
[2184]551          if (! dirp) {
552               set_error(errno);
553               error(filename);
554               return error_code;
555          }
556         
[1654]557          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
[2184]558               if (is_dotfile(dp->d_name))
[1654]559                    continue;
[2184]560               (void) strcpy(buf, append(filename, dp->d_name));
561               if (! *buf) {
562                    status = error_code;
563                    error(filename);
564                    continue;
[1654]565               }
[2184]566               retval = unlink_completely(buf);
567               if (retval) {
568                    status = retval;
569                    error(filename);
570               }
[1654]571          }
572          closedir(dirp);
[2184]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          }
[1654]583     }
[2184]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     }
[2221]594     return 0;
[1654]595}
Note: See TracBrowser for help on using the repository browser.