1/* `rm' file deletion utility for GNU. 2 Copyright (C) 1988, 1990-1991, 1994-2010 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 3 of the License, or 7 (at your option) 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, see <http://www.gnu.org/licenses/>. */ 16 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. */ 20 21#include <config.h> 22#include <stdio.h> 23#include <getopt.h> 24#include <sys/types.h> 25#include <assert.h> 26 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" 36 37/* The official name of this program (e.g., no `g' prefix). */ 38#define PROGRAM_NAME "rm" 39 40#define AUTHORS \ 41 proper_name ("Paul Rubin"), \ 42 proper_name ("David MacKenzie"), \ 43 proper_name ("Richard M. Stallman"), \ 44 proper_name ("Jim Meyering") 45 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. */ 48enum 49{ 50 INTERACTIVE_OPTION = CHAR_MAX + 1, 51 ONE_FILE_SYSTEM, 52 NO_PRESERVE_ROOT, 53 PRESERVE_ROOT, 54 PRESUME_INPUT_TTY_OPTION 55}; 56 57enum interactive_type 58 { 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 */ 62 }; 63 64static struct option const long_opts[] = 65{ 66 {"directory", no_argument, NULL, 'd'}, 67 {"force", no_argument, NULL, 'f'}, 68 {"interactive", optional_argument, NULL, INTERACTIVE_OPTION}, 69 70 {"one-file-system", no_argument, NULL, ONE_FILE_SYSTEM}, 71 {"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT}, 72 {"preserve-root", no_argument, NULL, PRESERVE_ROOT}, 73 74 /* This is solely for testing. Do not document. */ 75 /* It is relatively difficult to ensure that there is a tty on stdin. 76 Since rm acts differently depending on that, without this option, 77 it'd be harder to test the parts of rm that depend on that setting. */ 78 {"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION}, 79 80 {"recursive", no_argument, NULL, 'r'}, 81 {"verbose", no_argument, NULL, 'v'}, 82 {GETOPT_HELP_OPTION_DECL}, 83 {GETOPT_VERSION_OPTION_DECL}, 84 {NULL, 0, NULL, 0} 85}; 86 87static char const *const interactive_args[] = 88{ 89 "never", "no", "none", 90 "once", 91 "always", "yes", NULL 92}; 93static enum interactive_type const interactive_types[] = 94{ 95 interactive_never, interactive_never, interactive_never, 96 interactive_once, 97 interactive_always, interactive_always 98}; 99ARGMATCH_VERIFY (interactive_args, interactive_types); 100 101/* Advise the user about invalid usages like "rm -foo" if the file 102 "-foo" exists, assuming ARGC and ARGV are as with `main'. */ 103 104static void 105diagnose_leading_hyphen (int argc, char **argv) 106{ 107 /* OPTIND is unreliable, so iterate through the arguments looking 108 for a file name that looks like an option. */ 109 int i; 110 111 for (i = 1; i < argc; i++) 112 { 113 char const *arg = argv[i]; 114 struct stat st; 115 116 if (arg[0] == '-' && arg[1] && lstat (arg, &st) == 0) 117 { 118 fprintf (stderr, 119 _("Try `%s ./%s' to remove the file %s.\n"), 120 argv[0], 121 quotearg_n_style (1, shell_quoting_style, arg), 122 quote (arg)); 123 break; 124 } 125 } 126} 127 128void 129usage (int status) 130{ 131 if (status != EXIT_SUCCESS) 132 fprintf (stderr, _("Try `%s --help' for more information.\n"), 133 program_name); 134 else 135 { 136 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name); 137 fputs (_("\ 138Remove (unlink) the FILE(s).\n\ 139\n\ 140 -f, --force ignore nonexistent files, never prompt\n\ 141 -i prompt before every removal\n\ 142"), stdout); 143 fputs (_("\ 144 -I prompt once before removing more than three files, or\n\ 145 when removing recursively. Less intrusive than -i,\n\ 146 while still giving protection against most mistakes\n\ 147 --interactive[=WHEN] prompt according to WHEN: never, once (-I), or\n\ 148 always (-i). Without WHEN, prompt always\n\ 149"), stdout); 150 fputs (_("\ 151 --one-file-system when removing a hierarchy recursively, skip any\n\ 152 directory that is on a file system different from\n\ 153 that of the corresponding command line argument\n\ 154"), stdout); 155 fputs (_("\ 156 --no-preserve-root do not treat `/' specially\n\ 157 --preserve-root do not remove `/' (default)\n\ 158 -r, -R, --recursive remove directories and their contents recursively\n\ 159 -v, --verbose explain what is being done\n\ 160"), stdout); 161 fputs (HELP_OPTION_DESCRIPTION, stdout); 162 fputs (VERSION_OPTION_DESCRIPTION, stdout); 163 fputs (_("\ 164\n\ 165By default, rm does not remove directories. Use the --recursive (-r or -R)\n\ 166option to remove each listed directory, too, along with all of its contents.\n\ 167"), stdout); 168 printf (_("\ 169\n\ 170To remove a file whose name starts with a `-', for example `-foo',\n\ 171use one of these commands:\n\ 172 %s -- -foo\n\ 173\n\ 174 %s ./-foo\n\ 175"), 176 program_name, program_name); 177 fputs (_("\ 178\n\ 179Note that if you use rm to remove a file, it is usually possible to recover\n\ 180the contents of that file. If you want more assurance that the contents are\n\ 181truly unrecoverable, consider using shred.\n\ 182"), stdout); 183 emit_ancillary_info (); 184 } 185 exit (status); 186} 187 188static void 189rm_option_init (struct rm_options *x) 190{ 191 x->ignore_missing_files = false; 192 x->interactive = RMI_SOMETIMES; 193 x->one_file_system = false; 194 x->recursive = false; 195 x->root_dev_ino = NULL; 196 x->stdin_tty = isatty (STDIN_FILENO); 197 x->verbose = false; 198 199 /* Since this program exits immediately after calling `rm', rm need not 200 expend unnecessary effort to preserve the initial working directory. */ 201 x->require_restore_cwd = false; 202} 203 204int 205main (int argc, char **argv) 206{ 207 bool preserve_root = true; 208 struct rm_options x; 209 bool prompt_once = false; 210 int c; 211 212 initialize_main (&argc, &argv); 213 set_program_name (argv[0]); 214 setlocale (LC_ALL, ""); 215 bindtextdomain (PACKAGE, LOCALEDIR); 216 textdomain (PACKAGE); 217 218 atexit (close_stdin); 219 220 rm_option_init (&x); 221 222 /* Try to disable the ability to unlink a directory. */ 223 priv_set_remove_linkdir (); 224 225 while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1) 226 { 227 switch (c) 228 { 229 case 'd': 230 /* Ignore this option, for backward compatibility with 231 coreutils 5.92. FIXME: Some time after 2005, change this 232 to report an error (or perhaps behave like FreeBSD does) 233 instead of ignoring the option. */ 234 break; 235 236 case 'f': 237 x.interactive = RMI_NEVER; 238 x.ignore_missing_files = true; 239 prompt_once = false; 240 break; 241 242 case 'i': 243 x.interactive = RMI_ALWAYS; 244 x.ignore_missing_files = false; 245 prompt_once = false; 246 break; 247 248 case 'I': 249 x.interactive = RMI_NEVER; 250 x.ignore_missing_files = false; 251 prompt_once = true; 252 break; 253 254 case 'r': 255 case 'R': 256 x.recursive = true; 257 break; 258 259 case INTERACTIVE_OPTION: 260 { 261 int i; 262 if (optarg) 263 i = XARGMATCH ("--interactive", optarg, interactive_args, 264 interactive_types); 265 else 266 i = interactive_always; 267 switch (i) 268 { 269 case interactive_never: 270 x.interactive = RMI_NEVER; 271 prompt_once = false; 272 break; 273 274 case interactive_once: 275 x.interactive = RMI_SOMETIMES; 276 x.ignore_missing_files = false; 277 prompt_once = true; 278 break; 279 280 case interactive_always: 281 x.interactive = RMI_ALWAYS; 282 x.ignore_missing_files = false; 283 prompt_once = false; 284 break; 285 } 286 break; 287 } 288 289 case ONE_FILE_SYSTEM: 290 x.one_file_system = true; 291 break; 292 293 case NO_PRESERVE_ROOT: 294 preserve_root = false; 295 break; 296 297 case PRESERVE_ROOT: 298 preserve_root = true; 299 break; 300 301 case PRESUME_INPUT_TTY_OPTION: 302 x.stdin_tty = true; 303 break; 304 305 case 'v': 306 x.verbose = true; 307 break; 308 309 case_GETOPT_HELP_CHAR; 310 case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); 311 default: 312 diagnose_leading_hyphen (argc, argv); 313 usage (EXIT_FAILURE); 314 } 315 } 316 317 if (argc <= optind) 318 { 319 if (x.ignore_missing_files) 320 exit (EXIT_SUCCESS); 321 else 322 { 323 error (0, 0, _("missing operand")); 324 usage (EXIT_FAILURE); 325 } 326 } 327 328 if (x.recursive && preserve_root) 329 { 330 static struct dev_ino dev_ino_buf; 331 x.root_dev_ino = get_root_dev_ino (&dev_ino_buf); 332 if (x.root_dev_ino == NULL) 333 error (EXIT_FAILURE, errno, _("failed to get attributes of %s"), 334 quote ("/")); 335 } 336 { 337 size_t n_files = argc - optind; 338 char **file = argv + optind; 339 340 if (prompt_once && (x.recursive || 3 < n_files)) 341 { 342 fprintf (stderr, 343 (x.recursive 344 ? _("%s: remove all arguments recursively? ") 345 : _("%s: remove all arguments? ")), 346 program_name); 347 if (!yesno ()) 348 exit (EXIT_SUCCESS); 349 } 350 351 { 352 enum RM_status status = rm (file, &x); 353 assert (VALID_STATUS (status)); 354 exit (status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS); 355 } 356 } 357} 358