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