home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume22 / undel2 / part03 / delete.c next >
Encoding:
C/C++ Source or Header  |  1990-06-07  |  12.8 KB  |  543 lines

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