maint: revert "build: update gnulib submodule to latest"
[coreutils/ericb.git] / src / rm.c
blob010b8e00bf436ec7fe491d98526a1dea6808fa49
1 /* `rm' file deletion utility for GNU.
2 Copyright (C) 1988, 1990-1991, 1994-2011 Free Software Foundation, Inc.
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 3 of the License, or
7 (at your option) any later version.
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.
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
17 /* Initially written by Paul Rubin, David MacKenzie, and Richard Stallman.
18 Reworked to use chdir and avoid recursion, and later, rewritten
19 once again, to use fts, by Jim Meyering. */
21 #include <config.h>
22 #include <stdio.h>
23 #include <getopt.h>
24 #include <sys/types.h>
25 #include <assert.h>
27 #include "system.h"
28 #include "argmatch.h"
29 #include "error.h"
30 #include "quote.h"
31 #include "quotearg.h"
32 #include "remove.h"
33 #include "root-dev-ino.h"
34 #include "yesno.h"
35 #include "priv-set.h"
37 /* The official name of this program (e.g., no `g' prefix). */
38 #define PROGRAM_NAME "rm"
40 #define AUTHORS \
41 proper_name ("Paul Rubin"), \
42 proper_name ("David MacKenzie"), \
43 proper_name ("Richard M. Stallman"), \
44 proper_name ("Jim Meyering")
46 /* For long options that have no equivalent short option, use a
47 non-character as a pseudo short option, starting with CHAR_MAX + 1. */
48 enum
50 INTERACTIVE_OPTION = CHAR_MAX + 1,
51 ONE_FILE_SYSTEM,
52 NO_PRESERVE_ROOT,
53 PRESERVE_ROOT,
54 PRESUME_INPUT_TTY_OPTION
57 enum interactive_type
59 interactive_never, /* 0: no option or --interactive=never */
60 interactive_once, /* 1: -I or --interactive=once */
61 interactive_always /* 2: default, -i or --interactive=always */
64 static struct option const long_opts[] =
66 {"force", no_argument, NULL, 'f'},
67 {"interactive", optional_argument, NULL, INTERACTIVE_OPTION},
69 {"one-file-system", no_argument, NULL, ONE_FILE_SYSTEM},
70 {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT},
71 {"preserve-root", no_argument, NULL, PRESERVE_ROOT},
73 /* This is solely for testing. Do not document. */
74 /* It is relatively difficult to ensure that there is a tty on stdin.
75 Since rm acts differently depending on that, without this option,
76 it'd be harder to test the parts of rm that depend on that setting. */
77 {"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION},
79 {"recursive", no_argument, NULL, 'r'},
80 {"verbose", no_argument, NULL, 'v'},
81 {GETOPT_HELP_OPTION_DECL},
82 {GETOPT_VERSION_OPTION_DECL},
83 {NULL, 0, NULL, 0}
86 static char const *const interactive_args[] =
88 "never", "no", "none",
89 "once",
90 "always", "yes", NULL
92 static enum interactive_type const interactive_types[] =
94 interactive_never, interactive_never, interactive_never,
95 interactive_once,
96 interactive_always, interactive_always
98 ARGMATCH_VERIFY (interactive_args, interactive_types);
100 /* Advise the user about invalid usages like "rm -foo" if the file
101 "-foo" exists, assuming ARGC and ARGV are as with `main'. */
103 static void
104 diagnose_leading_hyphen (int argc, char **argv)
106 /* OPTIND is unreliable, so iterate through the arguments looking
107 for a file name that looks like an option. */
108 int i;
110 for (i = 1; i < argc; i++)
112 char const *arg = argv[i];
113 struct stat st;
115 if (arg[0] == '-' && arg[1] && lstat (arg, &st) == 0)
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;
127 void
128 usage (int status)
130 if (status != EXIT_SUCCESS)
131 fprintf (stderr, _("Try `%s --help' for more information.\n"),
132 program_name);
133 else
135 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
136 fputs (_("\
137 Remove (unlink) the FILE(s).\n\
139 -f, --force ignore nonexistent files, never prompt\n\
140 -i prompt before every removal\n\
141 "), stdout);
142 fputs (_("\
143 -I prompt once before removing more than three files, or\n\
144 when removing recursively. Less intrusive than -i,\n\
145 while still giving protection against most mistakes\n\
146 --interactive[=WHEN] prompt according to WHEN: never, once (-I), or\n\
147 always (-i). Without WHEN, prompt always\n\
148 "), stdout);
149 fputs (_("\
150 --one-file-system when removing a hierarchy recursively, skip any\n\
151 directory that is on a file system different from\n\
152 that of the corresponding command line argument\n\
153 "), stdout);
154 fputs (_("\
155 --no-preserve-root do not treat `/' specially\n\
156 --preserve-root do not remove `/' (default)\n\
157 -r, -R, --recursive remove directories and their contents recursively\n\
158 -v, --verbose explain what is being done\n\
159 "), stdout);
160 fputs (HELP_OPTION_DESCRIPTION, stdout);
161 fputs (VERSION_OPTION_DESCRIPTION, stdout);
162 fputs (_("\
164 By default, rm does not remove directories. Use the --recursive (-r or -R)\n\
165 option to remove each listed directory, too, along with all of its contents.\n\
166 "), stdout);
167 printf (_("\
169 To remove a file whose name starts with a `-', for example `-foo',\n\
170 use one of these commands:\n\
171 %s -- -foo\n\
173 %s ./-foo\n\
175 program_name, program_name);
176 fputs (_("\
178 Note that if you use rm to remove a file, it might be possible to recover\n\
179 some of its contents, given sufficient expertise and/or time. For greater\n\
180 assurance that the contents are truly unrecoverable, consider using shred.\n\
181 "), stdout);
182 emit_ancillary_info ();
184 exit (status);
187 static void
188 rm_option_init (struct rm_options *x)
190 x->ignore_missing_files = false;
191 x->interactive = RMI_SOMETIMES;
192 x->one_file_system = false;
193 x->recursive = false;
194 x->root_dev_ino = NULL;
195 x->stdin_tty = isatty (STDIN_FILENO);
196 x->verbose = false;
198 /* Since this program exits immediately after calling `rm', rm need not
199 expend unnecessary effort to preserve the initial working directory. */
200 x->require_restore_cwd = false;
204 main (int argc, char **argv)
206 bool preserve_root = true;
207 struct rm_options x;
208 bool prompt_once = false;
209 int c;
211 initialize_main (&argc, &argv);
212 set_program_name (argv[0]);
213 setlocale (LC_ALL, "");
214 bindtextdomain (PACKAGE, LOCALEDIR);
215 textdomain (PACKAGE);
217 atexit (close_stdin);
219 rm_option_init (&x);
221 /* Try to disable the ability to unlink a directory. */
222 priv_set_remove_linkdir ();
224 while ((c = getopt_long (argc, argv, "firvIR", long_opts, NULL)) != -1)
226 switch (c)
228 case 'f':
229 x.interactive = RMI_NEVER;
230 x.ignore_missing_files = true;
231 prompt_once = false;
232 break;
234 case 'i':
235 x.interactive = RMI_ALWAYS;
236 x.ignore_missing_files = false;
237 prompt_once = false;
238 break;
240 case 'I':
241 x.interactive = RMI_NEVER;
242 x.ignore_missing_files = false;
243 prompt_once = true;
244 break;
246 case 'r':
247 case 'R':
248 x.recursive = true;
249 break;
251 case INTERACTIVE_OPTION:
253 int i;
254 if (optarg)
255 i = XARGMATCH ("--interactive", optarg, interactive_args,
256 interactive_types);
257 else
258 i = interactive_always;
259 switch (i)
261 case interactive_never:
262 x.interactive = RMI_NEVER;
263 prompt_once = false;
264 break;
266 case interactive_once:
267 x.interactive = RMI_SOMETIMES;
268 x.ignore_missing_files = false;
269 prompt_once = true;
270 break;
272 case interactive_always:
273 x.interactive = RMI_ALWAYS;
274 x.ignore_missing_files = false;
275 prompt_once = false;
276 break;
278 break;
281 case ONE_FILE_SYSTEM:
282 x.one_file_system = true;
283 break;
285 case NO_PRESERVE_ROOT:
286 preserve_root = false;
287 break;
289 case PRESERVE_ROOT:
290 preserve_root = true;
291 break;
293 case PRESUME_INPUT_TTY_OPTION:
294 x.stdin_tty = true;
295 break;
297 case 'v':
298 x.verbose = true;
299 break;
301 case_GETOPT_HELP_CHAR;
302 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
303 default:
304 diagnose_leading_hyphen (argc, argv);
305 usage (EXIT_FAILURE);
309 if (argc <= optind)
311 if (x.ignore_missing_files)
312 exit (EXIT_SUCCESS);
313 else
315 error (0, 0, _("missing operand"));
316 usage (EXIT_FAILURE);
320 if (x.recursive && preserve_root)
322 static struct dev_ino dev_ino_buf;
323 x.root_dev_ino = get_root_dev_ino (&dev_ino_buf);
324 if (x.root_dev_ino == NULL)
325 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"),
326 quote ("/"));
329 size_t n_files = argc - optind;
330 char **file = argv + optind;
332 if (prompt_once && (x.recursive || 3 < n_files))
334 fprintf (stderr,
335 (x.recursive
336 ? _("%s: remove all arguments recursively? ")
337 : _("%s: remove all arguments? ")),
338 program_name);
339 if (!yesno ())
340 exit (EXIT_SUCCESS);
343 enum RM_status status = rm (file, &x);
344 assert (VALID_STATUS (status));
345 exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS);