1 /* 'rm' file deletion utility for GNU.
2 Copyright (C) 1988-2013 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. */
24 #include <sys/types.h>
33 #include "root-dev-ino.h"
37 /* The official name of this program (e.g., no 'g' prefix). */
38 #define PROGRAM_NAME "rm"
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. */
50 INTERACTIVE_OPTION
= CHAR_MAX
+ 1,
54 PRESUME_INPUT_TTY_OPTION
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 {"dir", no_argument
, NULL
, 'd'},
81 {"verbose", no_argument
, NULL
, 'v'},
82 {GETOPT_HELP_OPTION_DECL
},
83 {GETOPT_VERSION_OPTION_DECL
},
87 static char const *const interactive_args
[] =
89 "never", "no", "none",
93 static enum interactive_type
const interactive_types
[] =
95 interactive_never
, interactive_never
, interactive_never
,
97 interactive_always
, interactive_always
99 ARGMATCH_VERIFY (interactive_args
, interactive_types
);
101 /* Advise the user about invalid usages like "rm -foo" if the file
102 "-foo" exists, assuming ARGC and ARGV are as with 'main'. */
105 diagnose_leading_hyphen (int argc
, char **argv
)
107 /* OPTIND is unreliable, so iterate through the arguments looking
108 for a file name that looks like an option. */
111 for (i
= 1; i
< argc
; i
++)
113 char const *arg
= argv
[i
];
116 if (arg
[0] == '-' && arg
[1] && lstat (arg
, &st
) == 0)
119 _("Try '%s ./%s' to remove the file %s.\n"),
121 quotearg_n_style (1, shell_quoting_style
, arg
),
131 if (status
!= EXIT_SUCCESS
)
135 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
137 Remove (unlink) the FILE(s).\n\
139 -f, --force ignore nonexistent files and arguments, never prompt\n\
140 -i prompt before every removal\n\
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\
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\
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 -d, --dir remove empty directories\n\
159 -v, --verbose explain what is being done\n\
161 fputs (HELP_OPTION_DESCRIPTION
, stdout
);
162 fputs (VERSION_OPTION_DESCRIPTION
, stdout
);
165 By default, rm does not remove directories. Use the --recursive (-r or -R)\n\
166 option to remove each listed directory, too, along with all of its contents.\n\
170 To remove a file whose name starts with a '-', for example '-foo',\n\
171 use one of these commands:\n\
176 program_name
, program_name
);
179 Note that if you use rm to remove a file, it might be possible to recover\n\
180 some of its contents, given sufficient expertise and/or time. For greater\n\
181 assurance that the contents are truly unrecoverable, consider using shred.\n\
183 emit_ancillary_info ();
189 rm_option_init (struct rm_options
*x
)
191 x
->ignore_missing_files
= false;
192 x
->interactive
= RMI_SOMETIMES
;
193 x
->one_file_system
= false;
194 x
->remove_empty_directories
= false;
195 x
->recursive
= false;
196 x
->root_dev_ino
= NULL
;
197 x
->stdin_tty
= isatty (STDIN_FILENO
);
200 /* Since this program exits immediately after calling 'rm', rm need not
201 expend unnecessary effort to preserve the initial working directory. */
202 x
->require_restore_cwd
= false;
206 main (int argc
, char **argv
)
208 bool preserve_root
= true;
210 bool prompt_once
= false;
213 initialize_main (&argc
, &argv
);
214 set_program_name (argv
[0]);
215 setlocale (LC_ALL
, "");
216 bindtextdomain (PACKAGE
, LOCALEDIR
);
217 textdomain (PACKAGE
);
219 atexit (close_stdin
);
223 /* Try to disable the ability to unlink a directory. */
224 priv_set_remove_linkdir ();
226 while ((c
= getopt_long (argc
, argv
, "dfirvIR", long_opts
, NULL
)) != -1)
231 x
.remove_empty_directories
= true;
235 x
.interactive
= RMI_NEVER
;
236 x
.ignore_missing_files
= true;
241 x
.interactive
= RMI_ALWAYS
;
242 x
.ignore_missing_files
= false;
247 x
.interactive
= RMI_NEVER
;
248 x
.ignore_missing_files
= false;
257 case INTERACTIVE_OPTION
:
261 i
= XARGMATCH ("--interactive", optarg
, interactive_args
,
264 i
= interactive_always
;
267 case interactive_never
:
268 x
.interactive
= RMI_NEVER
;
272 case interactive_once
:
273 x
.interactive
= RMI_SOMETIMES
;
274 x
.ignore_missing_files
= false;
278 case interactive_always
:
279 x
.interactive
= RMI_ALWAYS
;
280 x
.ignore_missing_files
= false;
287 case ONE_FILE_SYSTEM
:
288 x
.one_file_system
= true;
291 case NO_PRESERVE_ROOT
:
292 preserve_root
= false;
296 preserve_root
= true;
299 case PRESUME_INPUT_TTY_OPTION
:
307 case_GETOPT_HELP_CHAR
;
308 case_GETOPT_VERSION_CHAR (PROGRAM_NAME
, AUTHORS
);
310 diagnose_leading_hyphen (argc
, argv
);
311 usage (EXIT_FAILURE
);
317 if (x
.ignore_missing_files
)
321 error (0, 0, _("missing operand"));
322 usage (EXIT_FAILURE
);
326 if (x
.recursive
&& preserve_root
)
328 static struct dev_ino dev_ino_buf
;
329 x
.root_dev_ino
= get_root_dev_ino (&dev_ino_buf
);
330 if (x
.root_dev_ino
== NULL
)
331 error (EXIT_FAILURE
, errno
, _("failed to get attributes of %s"),
335 size_t n_files
= argc
- optind
;
336 char **file
= argv
+ optind
;
338 if (prompt_once
&& (x
.recursive
|| 3 < n_files
))
342 ? _("%s: remove all arguments recursively? ")
343 : _("%s: remove all arguments? ")),
349 enum RM_status status
= rm (file
, &x
);
350 assert (VALID_STATUS (status
));
351 exit (status
== RM_ERROR
? EXIT_FAILURE
: EXIT_SUCCESS
);