home *** CD-ROM | disk | FTP | other *** search
/ InfoMagic Source Code 1993 July / THE_SOURCE_CODE_CD_ROM.iso / gnu / fileutils-3.6 / src / rm.c < prev    next >
Encoding:
C/C++ Source or Header  |  1993-05-14  |  12.9 KB  |  524 lines

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