home *** CD-ROM | disk | FTP | other *** search
/ MacGames Sampler / PHT MacGames Bundle.iso / MacSource Folder / Samples from the CD / Editors / emacs / Emacs-1.14b1-sources / sources / utility-src / fileutils / src / rm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1994-04-08  |  14.3 KB  |  569 lines  |  [TEXT/EMAC]

  1. /* `rm' file deletion utility for GNU.
  2.    Copyright (C) 1988, 1990, 1991 Free Software Foundation, Inc.
  3.  
  4.    This program is free software; you can redistribute it and/or modify
  5.    it under the terms of the GNU General Public License as published by
  6.    the Free Software Foundation; either version 2, or (at your option)
  7.    any later version.
  8.  
  9.    This program is distributed in the hope that it will be useful,
  10.    but WITHOUT ANY WARRANTY; without even the implied warranty of
  11.    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  12.    GNU General Public License for more details.
  13.  
  14.    You should have received a copy of the GNU General Public License
  15.    along with this program; if not, write to the Free Software
  16.    Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.  */
  17.  
  18. /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */
  19.  
  20. #ifdef HAVE_CONFIG_H
  21. #if defined (CONFIG_BROKETS)
  22. /* We use <config.h> instead of "config.h" so that a compilation
  23.    using -I. -I$srcdir will use ./config.h rather than $srcdir/config.h
  24.    (which it would do because it found this file in $srcdir).  */
  25. #include <config.h>
  26. #else
  27. #include "config.h"
  28. #endif
  29. #endif
  30.  
  31. #include "stdio.h"
  32. #include "getopt.h"
  33. #include "sys/types.h"
  34. #include "system.h"
  35. #include "version.h"
  36.  
  37. #ifdef _POSIX_SOURCE
  38. /* POSIX.1 doesn't have inodes, so fake them to avoid lots of ifdefs. */
  39. #define ino_t unsigned long
  40. #define D_INO(dp) 1
  41. #else
  42. #define D_INO(dp) ((dp)->d_ino)
  43. #endif
  44.  
  45. char *basename ();
  46. char *stpcpy ();
  47. char *xmalloc ();
  48. char *xrealloc ();
  49. int eaccess_stat ();
  50. int yesno ();
  51. void error ();
  52. void strip_trailing_slashes ();
  53.  
  54. static int clear_directory ();
  55. static int duplicate_entry ();
  56. static int remove_dir ();
  57. static int remove_file ();
  58. static int rm ();
  59. static void usage ();
  60.  
  61. /* Name this program was run with.  */
  62. char *program_name;
  63.  
  64. /* Path of file now being processed; extended as necessary. */
  65. static char *pathname;
  66.  
  67. /* Number of bytes currently allocated for `pathname';
  68.    made larger when necessary, but never smaller.  */
  69. static int pnsize;
  70.  
  71. /* If nonzero, display the name of each file removed. */
  72. static int verbose;
  73.  
  74. /* If nonzero, ignore nonexistant files. */
  75. static int ignore_missing_files;
  76.  
  77. /* If nonzero, recursively remove directories. */
  78. static int recursive;
  79.  
  80. /* If nonzero, query the user about whether to remove each file. */
  81. static int interactive;
  82.  
  83. /* If nonzero, remove directories with unlink instead of rmdir, and don't
  84.    require a directory to be empty before trying to unlink it.
  85.    Only works for the super-user. */
  86. static int unlink_dirs;
  87.  
  88. /* If nonzero, stdin is a tty. */
  89. static int stdin_tty;
  90.  
  91. /* If non-zero, display usage information and exit.  */
  92. static int show_help;
  93.  
  94. /* If non-zero, print the version on standard output and exit.  */
  95. static int show_version;
  96.  
  97. static struct option /* const */ long_opts[] =
  98. {
  99.   {"directory", no_argument, 0, 1},
  100.   {"force", no_argument, NULL, 'f'},
  101.   {"interactive", no_argument, NULL, 'i'},
  102.   {"recursive", no_argument, 0, 1},
  103.   {"verbose", no_argument, 0, 1},
  104.   {"help", no_argument, 0, 1},
  105.   {"version", no_argument, 0, 1},
  106.   {"", 0, NULL, 0}
  107. };
  108.  
  109. void
  110. main (argc, argv)
  111.      int argc;
  112.      char **argv;
  113. {
  114.   int err = 0;
  115.   int c;
  116.  
  117. /*
  118.   {"directory", no_argument, &unlink_dirs, 1},
  119.   {"force", no_argument, NULL, 'f'},
  120.   {"interactive", no_argument, NULL, 'i'},
  121.   {"recursive", no_argument, &recursive, 1},
  122.   {"verbose", no_argument, &verbose, 1},
  123.   {"help", no_argument, &show_help, 1},
  124.   {"version", no_argument, &show_version, 1},
  125.   {NULL, 0, NULL, 0}
  126. */
  127.     long_opts[0].flag = &unlink_dirs;
  128.     long_opts[3].flag = &recursive;
  129.     long_opts[4].flag = &verbose;
  130.     long_opts[5].flag = &show_help;
  131.     long_opts[6].flag = &show_version;
  132.     
  133.  
  134.  
  135.   verbose = ignore_missing_files = recursive = interactive
  136.     = unlink_dirs = 0;
  137.   pnsize = 256;
  138.   pathname = xmalloc (pnsize);
  139.   program_name = argv[0];
  140.  
  141.   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
  142.     {
  143.       switch (c)
  144.     {
  145.     case 0:        /* Long option. */
  146.       break;
  147.     case 'd':
  148.       unlink_dirs = 1;
  149.       break;
  150.     case 'f':
  151.       interactive = 0;
  152.       ignore_missing_files = 1;
  153.       break;
  154.     case 'i':
  155.       interactive = 1;
  156.       ignore_missing_files = 0;
  157.       break;
  158.     case 'r':
  159.     case 'R':
  160.       recursive = 1;
  161.       break;
  162.     case 'v':
  163.       verbose = 1;
  164.       break;
  165.     default:
  166.       usage (1);
  167.     }
  168.     }
  169.  
  170.   if (show_version)
  171.     {
  172.       printf ("%s\n", version_string);
  173.       exit (0);
  174.     }
  175.  
  176.   if (show_help)
  177.     usage (0);
  178.  
  179.   if (optind == argc)
  180.     {
  181.       if (ignore_missing_files)
  182.     exit (0);
  183.       else
  184.     usage (1);
  185.     }
  186.  
  187.   stdin_tty = isatty (0);
  188.  
  189.   for (; optind < argc; optind++)
  190.     {
  191.       int len;
  192.  
  193.       /* Stripping slashes is harmless for rmdir;
  194.      if the arg is not a directory, it will fail with ENOTDIR.  */
  195.       strip_trailing_slashes (argv[optind]);
  196.       len = strlen (argv[optind]);
  197.       if (len + 1 > pnsize)
  198.     {
  199.       free (pathname);
  200.       pnsize = 2 * (len + 1);
  201.       pathname = xmalloc (pnsize);
  202.     }
  203.       strcpy (pathname, argv[optind]);
  204.       err += rm ();
  205.     }
  206.  
  207.   exit (err > 0);
  208. }
  209.  
  210. /* Remove file or directory `pathname' after checking appropriate things.
  211.    Return 0 if `pathname' is removed, 1 if not. */
  212.  
  213. static int
  214. rm ()
  215. {
  216.   struct stat path_stats;
  217.   char *base = basename (pathname);
  218.  
  219.   if (base[0] == '.' && (base[1] == '\0'
  220.                  || (base[1] == '.' && base[2] == '\0')))
  221.     {
  222.       error (0, 0, "cannot remove `.' or `..'");
  223.       return 1;
  224.     }
  225.  
  226.   if (lstat (pathname, &path_stats))
  227.     {
  228.       if (errno == ENOENT && ignore_missing_files)
  229.     return 0;
  230.       error (0, errno, "%s", pathname);
  231.       return 1;
  232.     }
  233.  
  234.   if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
  235.     return remove_dir (&path_stats);
  236.   else
  237.     return remove_file (&path_stats);
  238. }
  239.  
  240. /* Query the user if appropriate, and if ok try to remove the
  241.    non-directory `pathname', which STATP contains info about.
  242.    Return 0 if `pathname' is removed, 1 if not. */
  243.  
  244. static int
  245. remove_file (statp)
  246.      struct stat *statp;
  247. {
  248.   if (!ignore_missing_files && (interactive || stdin_tty)
  249.       && eaccess_stat (statp, W_OK)
  250. #ifdef S_ISLNK
  251.       && !S_ISLNK (statp->st_mode)
  252. #endif
  253.       )
  254.     {
  255.       fprintf (stderr, "%s: remove %s`%s', overriding mode %04o? ",
  256.            program_name,
  257.            S_ISDIR (statp->st_mode) ? "directory " : "",
  258.            pathname,
  259.            (unsigned int) (statp->st_mode & 07777));
  260.       if (!yesno ())
  261.     return 1;
  262.     }
  263.   else if (interactive)
  264.     {
  265.       fprintf (stderr, "%s: remove %s`%s'? ", program_name,
  266.            S_ISDIR (statp->st_mode) ? "directory " : "",
  267.            pathname);
  268.       if (!yesno ())
  269.     return 1;
  270.     }
  271.  
  272.   if (verbose)
  273.     printf ("%s\n", pathname);
  274.  
  275.   if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
  276.     {
  277.       error (0, errno, "%s", pathname);
  278.       return 1;
  279.     }
  280.   return 0;
  281. }
  282.  
  283. /* If not in recursive mode, print an error message and return 1.
  284.    Otherwise, query the user if appropriate, then try to recursively
  285.    remove directory `pathname', which STATP contains info about.
  286.    Return 0 if `pathname' is removed, 1 if not. */
  287.  
  288. static int
  289. remove_dir (statp)
  290.      struct stat *statp;
  291. {
  292.   int err;
  293.  
  294.   if (!recursive)
  295.     {
  296.       error (0, 0, "%s: is a directory", pathname);
  297.       return 1;
  298.     }
  299.  
  300.   if (!ignore_missing_files && (interactive || stdin_tty)
  301.       && eaccess_stat (statp, W_OK))
  302.     {
  303.       fprintf (stderr,
  304.            "%s: descend directory `%s', overriding mode %04o? ",
  305.            program_name, pathname,
  306.            (unsigned int) (statp->st_mode & 07777));
  307.       if (!yesno ())
  308.     return 1;
  309.     }
  310.   else if (interactive)
  311.     {
  312.       fprintf (stderr, "%s: descend directory `%s'? ",
  313.            program_name, pathname);
  314.       if (!yesno ())
  315.     return 1;
  316.     }
  317.  
  318.   if (verbose)
  319.     printf ("%s\n", pathname);
  320.  
  321.   err = clear_directory (statp);
  322.  
  323.   if (interactive)
  324.     {
  325.       if (err)
  326.     fprintf (stderr, "%s: remove directory `%s' (might be nonempty)? ",
  327.          program_name, pathname);
  328.       else
  329.     fprintf (stderr, "%s: remove directory `%s'? ",
  330.          program_name, pathname);
  331.       if (!yesno ())
  332.     return 1;
  333.     }
  334.  
  335.   if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
  336.     {
  337.       error (0, errno, "%s", pathname);
  338.       return 1;
  339.     }
  340.   return 0;
  341. }
  342.  
  343. /* An element in a stack of pointers into `pathname'.
  344.    `pathp' points to where in `pathname' the terminating '\0' goes
  345.    for this level's directory name. */
  346. struct pathstack
  347. {
  348.   struct pathstack *next;
  349.   char *pathp;
  350.   ino_t inum;
  351. };
  352.  
  353. /* Linked list of pathnames of directories in progress in recursive rm.
  354.    The entries actually contain pointers into `pathname'.
  355.    `pathstack' is the current deepest level. */
  356. static struct pathstack *pathstack = NULL;
  357.  
  358. /* Read directory `pathname' and remove all of its entries,
  359.    avoiding use of chdir.
  360.    On entry, STATP points to the results of stat on `pathname'.
  361.    Return 0 for success, error count for failure.
  362.    Upon return, `pathname' will have the same contents as before,
  363.    but its address might be different; in that case, `pnsize' will
  364.    be larger, as well. */
  365.  
  366. static int
  367. clear_directory (statp)
  368.      struct stat *statp;
  369. {
  370.   DIR *dirp;
  371.   struct dirent *dp;
  372.   char *name_space;        /* Copy of directory's filenames. */
  373.   char *namep;            /* Current entry in `name_space'. */
  374.   unsigned name_size;        /* Bytes allocated for `name_space'. */
  375.   int name_length;        /* Length of filename in `namep' plus '\0'. */
  376.   int pathname_length;        /* Length of `pathname'. */
  377.   ino_t *inode_space;        /* Copy of directory's inodes. */
  378.   ino_t *inodep;        /* Current entry in `inode_space'. */
  379.   unsigned n_inodes_allocated;    /* There is space for this many inodes
  380.                       in `inode_space'. */
  381.   int err = 0;            /* Return status. */
  382.   struct pathstack pathframe;    /* New top of stack. */
  383.   struct pathstack *pp;        /* Temporary. */
  384.  
  385.   name_size = statp->st_size;
  386.   name_space = (char *) xmalloc (name_size);
  387.  
  388.   n_inodes_allocated = (statp->st_size + sizeof (ino_t) - 1) / sizeof (ino_t);
  389.   inode_space = (ino_t *) xmalloc (n_inodes_allocated * sizeof (ino_t));
  390.  
  391.   do
  392.     {
  393.       namep = name_space;
  394.       inodep = inode_space;
  395.  
  396.       errno = 0;
  397.       dirp = opendir (pathname);
  398.       if (dirp == NULL)
  399.     {
  400.       if (errno != ENOENT || !ignore_missing_files)
  401.         {
  402.           error (0, errno, "%s", pathname);
  403.           err = 1;
  404.         }
  405.       free (name_space);
  406.       free (inode_space);
  407.       return err;
  408.     }
  409.  
  410.       while ((dp = readdir (dirp)) != NULL)
  411.     {
  412.       /* Skip "." and ".." (some NFS filesystems' directories lack them). */
  413.       if (dp->d_name[0] != '.'
  414.           || (dp->d_name[1] != '\0'
  415.           && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
  416.         {
  417.           unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
  418.  
  419.           if (size_needed > name_size)
  420.         {
  421.           char *new_name_space;
  422.  
  423.           while (size_needed > name_size)
  424.             name_size += 1024;
  425.  
  426.           new_name_space = xrealloc (name_space, name_size);
  427.           namep += new_name_space - name_space;
  428.           name_space = new_name_space;
  429.         }
  430.           namep = stpcpy (namep, dp->d_name) + 1;
  431.  
  432.           if (inodep == inode_space + n_inodes_allocated)
  433.         {
  434.           ino_t *new_inode_space;
  435.  
  436.           n_inodes_allocated += 1024;
  437.           new_inode_space = (ino_t *) xrealloc (inode_space,
  438.                     n_inodes_allocated * sizeof (ino_t));
  439.           inodep += new_inode_space - inode_space;
  440.           inode_space = new_inode_space;
  441.         }
  442.           *inodep++ = D_INO (dp);
  443.         }
  444.     }
  445.       *namep = '\0';
  446.       if (CLOSEDIR (dirp))
  447.     {
  448.       error (0, errno, "%s", pathname);
  449.       err = 1;
  450.     }
  451.  
  452.       pathname_length = strlen (pathname);
  453.  
  454.       for (namep = name_space, inodep = inode_space; *namep != '\0';
  455.        namep += name_length, inodep++)
  456.     {
  457.       name_length = strlen (namep) + 1;
  458.  
  459.       /* Satisfy GNU requirement that filenames can be arbitrarily long. */
  460.       if (pathname_length + 1 + name_length > pnsize)
  461.         {
  462.           char *new_pathname;
  463.  
  464.           pnsize = (pathname_length + 1 + name_length) * 2;
  465.           new_pathname = xrealloc (pathname, pnsize);
  466.           /* Update the all the pointers in the stack to use the new area. */
  467.           for (pp = pathstack; pp != NULL; pp = pp->next)
  468.         pp->pathp += new_pathname - pathname;
  469.           pathname = new_pathname;
  470.         }
  471.  
  472.       /* Add a new frame to the top of the path stack. */
  473.       pathframe.pathp = pathname + pathname_length;
  474.       pathframe.inum = *inodep;
  475.       pathframe.next = pathstack;
  476.       pathstack = &pathframe;
  477.  
  478.       /* Append '/' and the filename to current pathname, take care of the
  479.          file (which could result in recursive calls), and take the filename
  480.          back off. */
  481.  
  482.       *pathstack->pathp = '/';
  483.       strcpy (pathstack->pathp + 1, namep);
  484.  
  485.       /* If the i-number has already appeared, there's an error. */
  486.       if (duplicate_entry (pathstack->next, pathstack->inum))
  487.         err++;
  488.       else if (rm ())
  489.         err++;
  490.  
  491.       *pathstack->pathp = '\0';
  492.       pathstack = pathstack->next;    /* Pop the stack. */
  493.     }
  494.     }
  495.   /* Keep trying while there are still files to remove. */
  496.   while (namep > name_space && err == 0);
  497.  
  498.   free (name_space);
  499.   free (inode_space);
  500.   return err;
  501. }
  502.  
  503. /* If STACK does not already have an entry with the same i-number as INUM,
  504.    return 0. Otherwise, ask the user whether to continue;
  505.    if yes, return 1, and if no, exit.
  506.    This assumes that no one tries to remove filesystem mount points;
  507.    doing so could cause duplication of i-numbers that would not indicate
  508.    a corrupted file system. */
  509.  
  510. static int
  511. duplicate_entry (stack, inum)
  512.      struct pathstack *stack;
  513.      ino_t inum;
  514. {
  515. #ifndef _POSIX_SOURCE
  516.   struct pathstack *p;
  517.  
  518.   for (p = stack; p != NULL; p = p->next)
  519.     {
  520.       if (p->inum == inum)
  521.     {
  522.       fprintf (stderr, "\
  523. %s: WARNING: Circular directory structure.\n\
  524. This almost certainly means that you have a corrupted file system.\n\
  525. NOTIFY YOUR SYSTEM MANAGER.\n\
  526. Cycle detected:\n\
  527. %s\n\
  528. is the same file as\n", program_name, pathname);
  529.       *p->pathp = '\0';    /* Truncate pathname. */
  530.       fprintf (stderr, "%s\n", pathname);
  531.       *p->pathp = '/';    /* Put it back. */
  532.       if (interactive)
  533.         {
  534.           fprintf (stderr, "%s: continue? ", program_name);
  535.           if (!yesno ())
  536.         exit (1);
  537.           return 1;
  538.         }
  539.       else
  540.         exit (1);
  541.     }
  542.     }
  543. #endif
  544.   return 0;
  545. }
  546.  
  547. static void
  548. usage (status)
  549.      int status;
  550. {
  551.   if (status != 0)
  552.     fprintf (stderr, "Try `%s --help' for more information.\n",
  553.          program_name);
  554.   else
  555.     {
  556.       printf ("Usage: %s [OPTION]... PATH...\n", program_name);
  557.       printf ("\
  558. \n\
  559.   -d, --directory       unlink directory, even if non-empty (super-user only)\n\
  560.   -f, --force           ignore nonexistent files, never prompt\n\
  561.   -i, --interactive     prompt before any removal\n\
  562.   -v, --verbose         explain what is being done\n\
  563.   -r, -R, --recursive   remove the contents of directories recursively\n\
  564.       --help            display this help and exit\n\
  565.       --version         output version information and exit\n");
  566.     }
  567.   exit (status);
  568. }
  569.