home *** CD-ROM | disk | FTP | other *** search
/ The UNIX CD Bookshelf / OREILLY_TUCB_UNIX_CD.iso / upt / examples / SOURCES / DELETE / PART03.Z / PART03 / delete.c next >
Encoding:
C/C++ Source or Header  |  1998-07-24  |  13.4 KB  |  563 lines

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