don't hard-code `rm'
[platform/upstream/coreutils.git] / src / rm.c
1 /* `rm' file deletion utility for GNU.
2    Copyright (C) 88, 90, 91, 1994-2000 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 Foundation,
16    Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */
17
18 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman.
19    Reworked to use chdir and hash tables by Jim Meyering.  */
20
21 /* Implementation overview:
22
23    In the `usual' case, RM saves no state for directories it is processing.
24    When a removal fails (either due to an error or to an interactive `no'
25    reply), the failure is noted (see description of `ht' in remove.c's
26    remove_cwd_entries function) so that when/if the containing directory
27    is reopened, RM doesn't try to remove the entry again.
28
29    RM may delete arbitrarily deep hierarchies -- even ones in which file
30    names (from root to leaf) are longer than the system-imposed maximum.
31    It does this by using chdir to change to each directory in turn before
32    removing the entries in that directory.
33
34    RM detects directory cycles by maintaining a table of the currently
35    active directories.  See the description of active_dir_map in remove.c.
36
37    RM is careful to avoid forming full file names whenever possible.
38    A full file name is formed only when it is about to be used -- e.g.
39    in a diagnostic or in an interactive-mode prompt.
40
41    RM minimizes the number of lstat system calls it makes.  On systems
42    that have valid d_type data in directory entries, RM makes only one
43    lstat call per command line argument -- regardless of the depth of
44    the hierarchy.  */
45
46 #include <config.h>
47 #include <stdio.h>
48 #include <getopt.h>
49 #include <sys/types.h>
50 #include <assert.h>
51
52 #include "system.h"
53 #include "error.h"
54 #include "remove.h"
55 #include "save-cwd.h"
56
57 /* The official name of this program (e.g., no `g' prefix).  */
58 #define PROGRAM_NAME "rm"
59
60 #define AUTHORS \
61   "Paul Rubin, David MacKenzie, Richard Stallman, and Jim Meyering"
62
63 void strip_trailing_slashes ();
64
65 /* Name this program was run with.  */
66 char *program_name;
67
68 static struct option const long_opts[] =
69 {
70   {"directory", no_argument, NULL, 'd'},
71   {"force", no_argument, NULL, 'f'},
72   {"interactive", no_argument, NULL, 'i'},
73   {"recursive", no_argument, NULL, 'r'},
74   {"verbose", no_argument, NULL, 'v'},
75   {GETOPT_HELP_OPTION_DECL},
76   {GETOPT_VERSION_OPTION_DECL},
77   {NULL, 0, NULL, 0}
78 };
79
80 void
81 usage (int status)
82 {
83   if (status != 0)
84     fprintf (stderr, _("Try `%s --help' for more information.\n"),
85              program_name);
86   else
87     {
88       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
89       printf (_("\
90 Remove (unlink) the FILE(s).\n\
91 \n\
92   -d, --directory       unlink directory, even if non-empty (super-user only)\n\
93   -f, --force           ignore nonexistent files, never prompt\n\
94   -i, --interactive     prompt before any removal\n\
95   -r, -R, --recursive   remove the contents of directories recursively\n\
96   -v, --verbose         explain what is being done\n\
97       --help            display this help and exit\n\
98       --version         output version information and exit\n\
99 \n\
100 To remove a file whose name starts with a `-', for example `-foo',\n\
101 use one of these commands:\n\
102   %s -- -foo\n\
103   %s ./-foo\n\
104 "),
105               program_name, program_name);
106       puts (_("\nReport bugs to <bug-fileutils@gnu.org>."));
107       close_stdout ();
108     }
109   exit (status);
110 }
111
112 static void
113 rm_option_init (struct rm_options *x)
114 {
115   x->unlink_dirs = 0;
116   x->ignore_missing_files = 0;
117   x->interactive = 0;
118   x->recursive = 0;
119   x->stdin_tty = isatty (STDIN_FILENO);
120   x->verbose = 0;
121 }
122
123 int
124 main (int argc, char **argv)
125 {
126   struct rm_options x;
127   int fail = 0;
128   int c;
129
130   program_name = argv[0];
131   setlocale (LC_ALL, "");
132   bindtextdomain (PACKAGE, LOCALEDIR);
133   textdomain (PACKAGE);
134
135   rm_option_init (&x);
136
137   while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1)
138     {
139       switch (c)
140         {
141         case 0:         /* Long option.  */
142           break;
143         case 'd':
144           x.unlink_dirs = 1;
145           break;
146         case 'f':
147           x.interactive = 0;
148           x.ignore_missing_files = 1;
149           break;
150         case 'i':
151           x.interactive = 1;
152           x.ignore_missing_files = 0;
153           break;
154         case 'r':
155         case 'R':
156           x.recursive = 1;
157           break;
158         case 'v':
159           x.verbose = 1;
160           break;
161         case_GETOPT_HELP_CHAR;
162         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
163         default:
164           usage (1);
165         }
166     }
167
168   if (optind == argc)
169     {
170       if (x.ignore_missing_files)
171         exit (0);
172       else
173         {
174           error (0, 0, _("too few arguments"));
175           usage (1);
176         }
177     }
178
179   remove_init ();
180
181   for (; optind < argc; optind++)
182     {
183       struct File_spec fs;
184       enum RM_status status;
185
186       /* Stripping slashes is harmless for rmdir;
187          if the arg is not a directory, it will fail with ENOTDIR.  */
188       strip_trailing_slashes (argv[optind]);
189       fspec_init_file (&fs, argv[optind]);
190       status = rm (&fs, 1, &x);
191       assert (VALID_STATUS (status));
192       if (status == RM_ERROR)
193         fail = 1;
194     }
195
196   remove_fini ();
197
198   if (x.verbose)
199     close_stdout ();
200   exit (fail);
201 }