1 |
/* `rm' file deletion utility for GNU. |
2 |
Copyright (C) 88, 90, 91, 1994-2005 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., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. */ |
17 |
|
18 |
/* Written by Paul Rubin, David MacKenzie, and Richard Stallman. |
19 |
Reworked to use chdir and avoid recursion 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 lazily. See lib/cycle-check.c. |
35 |
|
36 |
RM is careful to avoid forming full file names whenever possible. |
37 |
A full file name is formed only when it is about to be used -- e.g. |
38 |
in a diagnostic or in an interactive-mode prompt. |
39 |
|
40 |
RM minimizes the number of lstat system calls it makes. On systems |
41 |
that have valid d_type data in directory entries, RM makes only one |
42 |
lstat call per command line argument -- regardless of the depth of |
43 |
the hierarchy. */ |
44 |
|
45 |
#include <config.h> |
46 |
#include <stdio.h> |
47 |
#include <getopt.h> |
48 |
#include <sys/types.h> |
49 |
#include <assert.h> |
50 |
|
51 |
#include "system.h" |
52 |
#include "dirname.h" |
53 |
#include "error.h" |
54 |
#include "lstat.h" |
55 |
#include "quote.h" |
56 |
#include "quotearg.h" |
57 |
#include "remove.h" |
58 |
#include "root-dev-ino.h" |
59 |
|
60 |
/* The official name of this program (e.g., no `g' prefix). */ |
61 |
#define PROGRAM_NAME "rm" |
62 |
|
63 |
#define AUTHORS \ |
64 |
"Paul Rubin", "David MacKenzie, Richard Stallman", "Jim Meyering" |
65 |
|
66 |
/* Name this program was run with. */ |
67 |
char *program_name; |
68 |
|
69 |
/* For long options that have no equivalent short option, use a |
70 |
non-character as a pseudo short option, starting with CHAR_MAX + 1. */ |
71 |
enum |
72 |
{ |
73 |
NO_PRESERVE_ROOT = CHAR_MAX + 1, |
74 |
PRESERVE_ROOT, |
75 |
PRESUME_INPUT_TTY_OPTION |
76 |
}; |
77 |
|
78 |
static struct option const long_opts[] = |
79 |
{ |
80 |
{"directory", no_argument, NULL, 'd'}, |
81 |
{"force", no_argument, NULL, 'f'}, |
82 |
{"interactive", no_argument, NULL, 'i'}, |
83 |
|
84 |
{"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT}, |
85 |
{"preserve-root", no_argument, NULL, PRESERVE_ROOT}, |
86 |
|
87 |
/* This is solely for testing. Do not document. */ |
88 |
/* It is relatively difficult to ensure that there is a tty on stdin. |
89 |
Since rm acts differently depending on that, without this option, |
90 |
it'd be harder to test the parts of rm that depend on that setting. */ |
91 |
{"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION}, |
92 |
|
93 |
{"recursive", no_argument, NULL, 'r'}, |
94 |
{"verbose", no_argument, NULL, 'v'}, |
95 |
{GETOPT_HELP_OPTION_DECL}, |
96 |
{GETOPT_VERSION_OPTION_DECL}, |
97 |
{NULL, 0, NULL, 0} |
98 |
}; |
99 |
|
100 |
/* Advise the user about invalid usages like "rm -foo" if the file |
101 |
"-foo" exists, assuming ARGC and ARGV are as with `main'. */ |
102 |
|
103 |
static void |
104 |
diagnose_leading_hyphen (int argc, char **argv) |
105 |
{ |
106 |
/* OPTIND is unreliable, so iterate through the arguments looking |
107 |
for a file name that looks like an option. */ |
108 |
int i; |
109 |
|
110 |
for (i = 1; i < argc; i++) |
111 |
{ |
112 |
char const *arg = argv[i]; |
113 |
struct stat st; |
114 |
|
115 |
if (arg[0] == '-' && arg[1] && lstat (arg, &st) == 0) |
116 |
{ |
117 |
fprintf (stderr, |
118 |
_("Try `%s ./%s' to remove the file %s.\n"), |
119 |
argv[0], |
120 |
quotearg_n_style (1, shell_quoting_style, arg), |
121 |
quote (arg)); |
122 |
break; |
123 |
} |
124 |
} |
125 |
} |
126 |
|
127 |
void |
128 |
usage (int status) |
129 |
{ |
130 |
if (status != EXIT_SUCCESS) |
131 |
fprintf (stderr, _("Try `%s --help' for more information.\n"), |
132 |
program_name); |
133 |
else |
134 |
{ |
135 |
char *base = base_name (program_name); |
136 |
printf (_("Usage: %s [OPTION]... FILE...\n"), program_name); |
137 |
fputs (_("\ |
138 |
Remove (unlink) the FILE(s).\n\ |
139 |
\n\ |
140 |
-f, --force ignore nonexistent files, never prompt\n\ |
141 |
-i, --interactive prompt before any removal\n\ |
142 |
"), stdout); |
143 |
fputs (_("\ |
144 |
--no-preserve-root do not treat `/' specially (the default)\n\ |
145 |
--preserve-root fail to operate recursively on `/'\n\ |
146 |
-r, -R, --recursive remove directories and their contents recursively\n\ |
147 |
-v, --verbose explain what is being done\n\ |
148 |
"), stdout); |
149 |
fputs (HELP_OPTION_DESCRIPTION, stdout); |
150 |
fputs (VERSION_OPTION_DESCRIPTION, stdout); |
151 |
fputs (_("\ |
152 |
\n\ |
153 |
By default, rm does not remove directories. Use the --recursive (-r or -R)\n\ |
154 |
option to remove each listed directory, too, along with all of its contents.\n\ |
155 |
"), stdout); |
156 |
printf (_("\ |
157 |
\n\ |
158 |
To remove a file whose name starts with a `-', for example `-foo',\n\ |
159 |
use one of these commands:\n\ |
160 |
%s -- -foo\n\ |
161 |
\n\ |
162 |
%s ./-foo\n\ |
163 |
"), |
164 |
base, base); |
165 |
fputs (_("\ |
166 |
\n\ |
167 |
Note that if you use rm to remove a file, it is usually possible to recover\n\ |
168 |
the contents of that file. If you want more assurance that the contents are\n\ |
169 |
truly unrecoverable, consider using shred.\n\ |
170 |
"), stdout); |
171 |
printf (_("\nReport bugs to <%s>.\n"), PACKAGE_BUGREPORT); |
172 |
} |
173 |
exit (status); |
174 |
} |
175 |
|
176 |
static void |
177 |
rm_option_init (struct rm_options *x) |
178 |
{ |
179 |
x->ignore_missing_files = false; |
180 |
x->interactive = false; |
181 |
x->recursive = false; |
182 |
x->root_dev_ino = NULL; |
183 |
x->stdin_tty = isatty (STDIN_FILENO); |
184 |
x->verbose = false; |
185 |
|
186 |
/* Since this program exits immediately after calling `rm', rm need not |
187 |
expend unnecessary effort to preserve the initial working directory. */ |
188 |
x->require_restore_cwd = false; |
189 |
} |
190 |
|
191 |
int |
192 |
main (int argc, char **argv) |
193 |
{ |
194 |
bool preserve_root = false; |
195 |
struct rm_options x; |
196 |
int c; |
197 |
|
198 |
initialize_main (&argc, &argv); |
199 |
program_name = argv[0]; |
200 |
setlocale (LC_ALL, ""); |
201 |
bindtextdomain (PACKAGE, LOCALEDIR); |
202 |
textdomain (PACKAGE); |
203 |
|
204 |
atexit (close_stdout); |
205 |
|
206 |
rm_option_init (&x); |
207 |
|
208 |
while ((c = getopt_long (argc, argv, "dfirvR", long_opts, NULL)) != -1) |
209 |
{ |
210 |
switch (c) |
211 |
{ |
212 |
case 'd': |
213 |
/* Ignore this option, for backward compatibility with |
214 |
coreutils 5.92. Some time after 2005, we'll change this |
215 |
to report an error (or perhaps behave like FreeBSD does) |
216 |
instead of ignoring the option. */ |
217 |
break; |
218 |
|
219 |
case 'f': |
220 |
x.interactive = false; |
221 |
x.ignore_missing_files = true; |
222 |
break; |
223 |
|
224 |
case 'i': |
225 |
x.interactive = true; |
226 |
x.ignore_missing_files = false; |
227 |
break; |
228 |
|
229 |
case 'r': |
230 |
case 'R': |
231 |
x.recursive = true; |
232 |
break; |
233 |
|
234 |
case NO_PRESERVE_ROOT: |
235 |
preserve_root = false; |
236 |
break; |
237 |
|
238 |
case PRESERVE_ROOT: |
239 |
preserve_root = true; |
240 |
break; |
241 |
|
242 |
case PRESUME_INPUT_TTY_OPTION: |
243 |
x.stdin_tty = true; |
244 |
break; |
245 |
|
246 |
case 'v': |
247 |
x.verbose = true; |
248 |
break; |
249 |
|
250 |
case_GETOPT_HELP_CHAR; |
251 |
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); |
252 |
default: |
253 |
diagnose_leading_hyphen (argc, argv); |
254 |
usage (EXIT_FAILURE); |
255 |
} |
256 |
} |
257 |
|
258 |
if (argc <= optind) |
259 |
{ |
260 |
if (x.ignore_missing_files) |
261 |
exit (EXIT_SUCCESS); |
262 |
else |
263 |
{ |
264 |
error (0, 0, _("missing operand")); |
265 |
usage (EXIT_FAILURE); |
266 |
} |
267 |
} |
268 |
|
269 |
if (x.recursive & preserve_root) |
270 |
{ |
271 |
static struct dev_ino dev_ino_buf; |
272 |
x.root_dev_ino = get_root_dev_ino (&dev_ino_buf); |
273 |
if (x.root_dev_ino == NULL) |
274 |
error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), |
275 |
quote ("/")); |
276 |
} |
277 |
|
278 |
{ |
279 |
size_t n_files = argc - optind; |
280 |
char const *const *file = (char const *const *) argv + optind; |
281 |
|
282 |
enum RM_status status = rm (n_files, file, &x); |
283 |
assert (VALID_STATUS (status)); |
284 |
exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS); |
285 |
} |
286 |
} |