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

Revision 23271, 14.1 KB checked in by broder, 16 years ago (diff)
Add com_err support for delete and discuss on OS X.
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#if defined(__APPLE__) && defined(__MACH__)
88     add_error_table(&et_del_error_table);
89#else
90     initialize_del_error_table();
91#endif
92
93#ifdef HAVE_AFS
94     gettimeofday(&tvp[0], (struct timezone *)0);
95     tvp[1] = tvp[0];
96#endif
97
98     force = interactive = recursive = noop = verbose = filesonly =
99          directoriesonly = emulate_rm = linked_to_rm = linked_to_rmdir = 0;
100
101     if (!strcmp(whoami, "rm"))
102          emulate_rm++, filesonly++, linked_to_rm++;
103     if (!strcmp(whoami, "rmdir") || !strcmp(whoami, "rd"))
104          emulate_rm++, directoriesonly++, linked_to_rmdir++;
105     
106     while ((arg = getopt(argc, argv, "efirnvFD")) != -1) {
107          switch (arg) {
108          case 'r':
109               recursive++;
110               if (directoriesonly) {
111                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
112                            whoami);
113                    usage();
114                    exit(1);
115               }
116               break;
117          case 'f':
118               force++;
119               break;
120          case 'i':
121               interactive++;
122               break;
123          case 'n':
124               noop++;
125               break;
126          case 'v':
127               verbose++;
128               break;
129          case 'e':
130               emulate_rm++;
131               break;
132          case 'F':
133               filesonly++;
134               if (directoriesonly) {
135                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
136                            whoami);
137                    usage();
138                    exit(1);
139               }
140               break;
141          case 'D':
142               directoriesonly++;
143               if (recursive) {
144                    fprintf(stderr, "%s: -r and -D are mutually exclusive.\n",
145                            whoami);
146                    usage();
147                    exit(1);
148               }
149               if (filesonly) {
150                    fprintf(stderr, "%s: -F and -D are mutually exclusive.\n",
151                            whoami);
152                    usage();
153                    exit(1);
154               }
155               break;
156          default:
157               usage();
158               exit(1);
159          }
160     }
161     report_errors = ! (force || emulate_rm);
162     
163     if (optind == argc) {
164          if (! force) {
165               fprintf(stderr, "%s: no files specified.\n", whoami);
166               usage();
167          }
168          exit(force ? 0 : 1);
169     }
170     while (optind < argc) {
171          if (delete(argv[optind], 0))
172               error(argv[optind]);
173          optind++;
174     }
175     exit(((! force) && error_occurred) ? 1 : 0);
176}
177
178
179
180usage()
181{
182     printf("Usage: %s [ options ] filename ...\n", whoami);
183     printf("Options are:\n");
184     if (! linked_to_rmdir)
185          printf("     -r     recursive\n");
186     printf("     -i     interactive\n");
187     printf("     -f     force\n");
188     printf("     -n     noop\n");
189     printf("     -v     verbose\n");
190     if (! (linked_to_rmdir || linked_to_rm)) {
191          printf("     -e     emulate rm/rmdir\n");
192          printf("     -F     files only\n");
193          printf("     -D     directories only\n");
194     }
195     printf("     --     end options and start filenames\n");
196     if (! (linked_to_rmdir || linked_to_rm)) {
197          printf("-r and -D are mutually exclusive\n");
198          printf("-F and -D are mutually exclusive\n");
199     }
200}
201
202
203
204
205delete(filename, recursed)
206char *filename;
207int recursed;
208{
209     struct stat stat_buf;
210     int retval;
211     
212     /* can the file be lstat'd? */
213     if (lstat(filename, &stat_buf) == -1) {
214          set_error(errno);
215          if (emulate_rm && (! force))
216               fprintf(stderr, "%s: %s nonexistent\n", whoami, filename);
217          error(filename);
218          return error_code;
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               
321empty_directory(filename)
322char *filename;
323{
324     DIR *dirp;
325     struct dirent *dp;
326
327     dirp = Opendir(filename);
328     if (! dirp) {
329          set_error(errno);
330          error(filename);
331          return -1;
332     }
333     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
334          if (is_dotfile(dp->d_name))
335               continue;
336          if (is_deleted(dp->d_name))
337               continue;
338          else {
339               closedir(dirp);
340               return 0;
341          }
342     }
343     closedir(dirp);
344     return 1;
345}
346
347
348
349
350recursive_delete(filename, stat_buf, recursed)
351char *filename;
352struct stat stat_buf;
353int recursed;
354{
355     DIR *dirp;
356     struct dirent *dp;
357     int status = 0;
358     char newfile[MAXPATHLEN];
359     int retval = 0;
360     
361     if (interactive && recursed) {
362          printf("%s: remove directory %s? ", whoami, filename);
363          if (! yes()) {
364               set_status(DELETE_NOT_DELETED);
365               return error_code;
366          }
367     }
368     dirp = Opendir(filename);
369     if (! dirp) {
370          if (emulate_rm && (! force))
371               fprintf(stderr, "%s: %s not changed\n", whoami, filename);
372          set_error(errno);
373          error(filename);
374          return error_code;
375     }
376     for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
377          if (is_dotfile(dp->d_name))
378               continue;
379          if (is_deleted(dp->d_name))
380               continue;
381          else {
382               (void) strcpy(newfile, append(filename, dp->d_name));
383               if (! *newfile) {
384                    error(filename);
385                    status = error_code;
386               }
387
388               retval = delete(newfile, 1);
389               if (retval) {
390                    error(newfile);
391                    status = retval;
392               }
393          }
394     }
395     closedir(dirp);
396
397     if (status && (! emulate_rm)) {
398          set_warning(DELETE_DIR_NOT_EMPTY);
399          error(filename);
400     }
401     else
402          retval = do_move(filename, stat_buf, status);
403     
404     if (retval)
405          status = retval;
406
407     return status;
408}
409
410                                         
411
412
413
414
415do_move(filename, stat_buf, subs_not_deleted)
416char *filename;
417struct stat stat_buf;
418int subs_not_deleted; /* If the file in question is a directory, and  */
419                      /* there is something underneath it that hasn't */
420                      /* been removed, this will be set to true.      */
421                      /* The program asks if the user wants to delete */
422                      /* the directory, and if the user says yes,     */
423                      /* checks the value of subs_not_deleted.  If    */
424                      /* it's true, an error results.                 */
425                      /* This is used only when emulating rm.         */
426{
427     char *last;
428     char buf[MAXPATHLEN];
429     char name[MAXNAMLEN];
430     struct stat deleted_buf;
431
432     (void) strncpy(buf, filename, MAXPATHLEN);
433     last = lastpart(buf);
434     if (strlen(last) > MAXNAMLEN) {
435          if (emulate_rm && (! force))
436               fprintf(stderr, "%s: %s: filename too long\n", whoami,
437                       filename);
438          set_error(ENAMETOOLONG);
439          error(filename);
440          return error_code;
441     }
442     (void) strcpy(name, last);
443     if (strlen(buf) + 3 > MAXPATHLEN) {
444          if (emulate_rm && (! force))
445               fprintf(stderr, "%s: %s: pathname too long\n", whoami,
446                       filename);
447          set_error(ENAMETOOLONG);
448          error(filename);
449          return error_code;
450     }
451     *last = '\0';
452     (void) strcat(buf, ".#");
453     (void) strcat(buf, name);
454     if (interactive) {
455          printf("%s: remove %s? ", whoami, filename);
456          if (! yes()) {
457               set_status(DELETE_NOT_DELETED);
458               return error_code;
459          }
460     }
461     else if ((! force)
462#ifdef S_IFLNK
463              && ((stat_buf.st_mode & S_IFMT) != S_IFLNK)
464#endif
465              && access(filename, W_OK)) {
466          if (emulate_rm)
467               printf("%s: override protection %o for %s? ", whoami,
468                      stat_buf.st_mode & 0777, filename);
469          else
470               printf("%s: File %s not writeable.  Delete anyway? ", whoami,
471                      filename);
472          if (! yes()) {
473               set_status(DELETE_NOT_DELETED);
474               return error_code;
475          }
476     }
477     if (emulate_rm && subs_not_deleted) {
478          if (! force)
479               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
480          return 1;
481     }
482     if (noop) {
483          fprintf(stderr, "%s: %s would be removed\n", whoami, filename);
484          return 0;
485     }
486     if ((! lstat(buf, &deleted_buf)) && unlink_completely(buf)) {
487          if (emulate_rm && (! force))
488               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
489          error(filename);
490          return error_code;
491     }
492     if (rename(filename, buf)) {
493          if (emulate_rm && (! force))
494               fprintf(stderr, "%s: %s not removed\n", whoami, filename);
495          set_error(errno);
496          error(filename);
497          return error_code;
498     }
499     else {
500          if (verbose)
501               fprintf(stderr, "%s: %s removed\n", whoami, filename);
502#ifdef HAVE_AFS
503          /*
504           * Normally, expunge uses the ctime to determine how long
505           * ago a file was deleted (since the ctime is normally
506           * updated when a file is renamed).  However, in AFS,
507           * renaming a file does not change the ctime, mtime OR
508           * atime, so we have to use utimes to force the change.
509           * This unfortunately causes the loss of the real mtime, but
510           * there's nothing we can do about that, if we want expunge
511           * to be able to do the right thing.
512           *
513           * Don't bother checking for errors, because we can't do
514           * anything about them anyway, and in any case, this isn't a
515           * *really* important operation.
516           */
517          utimes(buf, tvp);
518#endif
519          return 0;
520     }
521}
522
523
524
525unlink_completely(filename)
526char *filename;
527{
528     char buf[MAXPATHLEN];
529     struct stat stat_buf;
530     DIR *dirp;
531     struct dirent *dp;
532     int status = 0;
533     int retval;
534     
535     if (lstat(filename, &stat_buf)) {
536          set_error(errno);
537          error(filename);
538          return error_code;
539     }
540
541     if ((stat_buf.st_mode & S_IFMT) == S_IFDIR) {
542          dirp = Opendir(filename);
543          if (! dirp) {
544               set_error(errno);
545               error(filename);
546               return error_code;
547          }
548         
549          for (dp = readdir(dirp); dp != NULL; dp = readdir(dirp)) {
550               if (is_dotfile(dp->d_name))
551                    continue;
552               (void) strcpy(buf, append(filename, dp->d_name));
553               if (! *buf) {
554                    status = error_code;
555                    error(filename);
556                    continue;
557               }
558               retval = unlink_completely(buf);
559               if (retval) {
560                    status = retval;
561                    error(filename);
562               }
563          }
564          closedir(dirp);
565
566          if (status)
567               return status;
568
569          retval = rmdir(filename);
570          if (retval) {
571               set_error(errno);
572               error(filename);
573               return errno;
574          }
575     }
576     else {
577          retval = unlink(filename);
578          if (retval) {
579               set_error(errno);
580               error(filename);
581               return error_code;
582          }
583          else
584               return 0;
585     }
586     return 0;
587}
Note: See TracBrowser for help on using the repository browser.