/* 'rm' file deletion utility for GNU. This is the rm utility
Copyright (C) 1988-2018 Free Software Foundation, Inc.
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <https://www.gnu.org/licenses/>. */ The GNUv3 license
/* Initially written by Paul Rubin, David MacKenzie, and Richard Stallman.
Reworked to use chdir and avoid recursion, and later, rewritten
once again, to use fts, by Jim Meyering. */
#include <config.h> Provides system specific information
#include <stdio.h> Provides standard I/O capability
#include <getopt.h> ...!includes auto-comment...
#include <sys/types.h> Provides system data types
#include <assert.h> ...!includes auto-comment...
#include "system.h" ...!includes auto-comment...
#include "argmatch.h" ...!includes auto-comment...
#include "die.h" ...!includes auto-comment...
#include "error.h" ...!includes auto-comment...
#include "remove.h" ...!includes auto-comment...
#include "root-dev-ino.h" ...!includes auto-comment......!includes auto-comment...
#include "yesno.h" ...!includes auto-comment...
#include "priv-set.h" ...!includes auto-comment...
/* The official name of this program (e.g., no 'g' prefix). */
#define PROGRAM_NAME "rm" Line 37
#define AUTHORS \ Line 39
proper_name ("Paul Rubin"), \ Line 40
proper_name ("David MacKenzie"), \ Line 41
proper_name ("Richard M. Stallman"), \ Line 42
proper_name ("Jim Meyering") Line 43
/* For long options that have no equivalent short option, use a
non-character as a pseudo short option, starting with CHAR_MAX + 1. */
enum Line 47
{
INTERACTIVE_OPTION = CHAR_MAX + 1, Line 49
ONE_FILE_SYSTEM, Line 50
NO_PRESERVE_ROOT, Line 51
PRESERVE_ROOT, Line 52
PRESUME_INPUT_TTY_OPTION Line 53
}; Block 1
enum interactive_type Line 56
{
interactive_never, /* 0: no option or --interactive=never */ Line 58
interactive_once, /* 1: -I or --interactive=once */ Line 59
interactive_always /* 2: default, -i or --interactive=always */ Line 60
}; Block 2
static struct option const long_opts[] = Line 63
{
{"force", no_argument, NULL, 'f'}, Line 65
{"interactive", optional_argument, NULL, INTERACTIVE_OPTION}, Line 66
{"one-file-system", no_argument, NULL, ONE_FILE_SYSTEM}, Line 68
{"no-preserve-root", no_argument, NULL, NO_PRESERVE_ROOT}, Line 69
{"preserve-root", optional_argument, NULL, PRESERVE_ROOT}, Line 70
/* This is solely for testing. Do not document. */
/* It is relatively difficult to ensure that there is a tty on stdin.
Since rm acts differently depending on that, without this option,
it'd be harder to test the parts of rm that depend on that setting. */
{"-presume-input-tty", no_argument, NULL, PRESUME_INPUT_TTY_OPTION}, Line 76
{"recursive", no_argument, NULL, 'r'}, Line 78
{"dir", no_argument, NULL, 'd'}, Line 79
{"verbose", no_argument, NULL, 'v'}, Line 80
{GETOPT_HELP_OPTION_DECL}, Line 81
{GETOPT_VERSION_OPTION_DECL}, Line 82
{NULL, 0, NULL, 0} Line 83
}; Block 3
static char const *const interactive_args[] = Line 86
{
"never", "no", "none", Line 88
"once", Line 89
"always", "yes", NULL Line 90
}; Block 4
static enum interactive_type const interactive_types[] = Line 92
{
interactive_never, interactive_never, interactive_never, Line 94
interactive_once, Line 95
interactive_always, interactive_always Line 96
}; Block 5
ARGMATCH_VERIFY (interactive_args, interactive_types); Line 98
/* Advise the user about invalid usages like "rm -foo" if the file
"-foo" exists, assuming ARGC and ARGV are as with 'main'. */
static void Line 103
diagnose_leading_hyphen (int argc, char **argv) Line 104
{
/* OPTIND is unreliable, so iterate through the arguments looking
for a file name that looks like an option. */
for (int i = 1; i < argc; i++) Line 109
{
char const *arg = argv[i]; Line 111
struct stat st; Line 112
if (arg[0] == '-' && arg[1] && lstat (arg, &st) == 0) Line 114...!syscalls auto-comment...
{
fprintf (stderr, Line 116
_("Try '%s ./%s' to remove the file %s.\n"), Line 117
argv[0], Line 118
quotearg_n_style (1, shell_escape_quoting_style, arg), Line 119
quoteaf (arg)); Line 120
break; Line 121
}
}
}
void Line 126
usage (int status) Line 127
{
if (status != EXIT_SUCCESS) Line 129
emit_try_help (); ...!common auto-comment...
else Line 131
{
printf (_("Usage: %s [OPTION]... [FILE]...\n"), program_name); Line 133
fputs (_("\ Line 134
Remove (unlink) the FILE(s).\n\ Line 135
\n\
-f, --force ignore nonexistent files and arguments, never prompt\n\ Line 137
-i prompt before every removal\n\ Line 138
"), stdout); Line 139
fputs (_("\ Line 140
-I prompt once before removing more than three files, or\n\Line 141
when removing recursively; less intrusive than -i,\n\ Line 142
while still giving protection against most mistakes\n\Line 143
--interactive[=WHEN] prompt according to WHEN: never, once (-I), or\n\ Line 144
always (-i); without WHEN, prompt always\n\ Line 145
"), stdout); Line 146
fputs (_("\ Line 147
--one-file-system when removing a hierarchy recursively, skip any\n\ Line 148
directory that is on a file system different from\n\ Line 149
that of the corresponding command line argument\n\ Line 150
"), stdout); Line 151
fputs (_("\ Line 152
--no-preserve-root do not treat '/' specially\n\ Line 153
--preserve-root[=all] do not remove '/' (default);\n\ Line 154
with 'all', reject any command line argument\n\ Line 155
on a separate device from its parent\n\ Line 156
"), stdout); Line 157
fputs (_("\ Line 158
-r, -R, --recursive remove directories and their contents recursively\n\ Line 159
-d, --dir remove empty directories\n\ Line 160
-v, --verbose explain what is being done\n\ Line 161
"), stdout); Line 162
fputs (HELP_OPTION_DESCRIPTION, stdout); Line 163
fputs (VERSION_OPTION_DESCRIPTION, stdout); Line 164
fputs (_("\ Line 165
\n\
By default, rm does not remove directories. Use the --recursive (-r or -R)\n\ Line 167
option to remove each listed directory, too, along with all of its contents.\n\ Line 168
"), stdout); Line 169
printf (_("\ Line 170
\n\
To remove a file whose name starts with a '-', for example '-foo',\n\ Line 172
use one of these commands:\n\ Line 173
%s -- -foo\n\ Line 174
\n\
%s ./-foo\n\ Line 176
"), Line 177
program_name, program_name); Line 178
fputs (_("\ Line 179
\n\
Note that if you use rm to remove a file, it might be possible to recover\n\ Line 181
some of its contents, given sufficient expertise and/or time. For greater\n\ Line 182
assurance that the contents are truly unrecoverable, consider using shred.\n\ Line 183
"), stdout); Line 184
emit_ancillary_info (PROGRAM_NAME); Line 185
}
exit (status); Line 187
} Block 7
static void Line 190
rm_option_init (struct rm_options *x) Line 191
{
x->ignore_missing_files = false; Line 193
x->interactive = RMI_SOMETIMES; Line 194
x->one_file_system = false; Line 195
x->remove_empty_directories = false; Line 196
x->recursive = false; Line 197
x->root_dev_ino = NULL; Line 198
x->preserve_all_root = false; Line 199
x->stdin_tty = isatty (STDIN_FILENO); Line 200
x->verbose = false; Line 201
/* Since this program exits immediately after calling 'rm', rm need not
expend unnecessary effort to preserve the initial working directory. */
x->require_restore_cwd = false; Line 205
} Block 8
int
main (int argc, char **argv) Line 209
{
bool preserve_root = true; Line 211
struct rm_options x; Line 212
bool prompt_once = false; Line 213
int c; Line 214
initialize_main (&argc, &argv); VMS-specific entry point handling wildcard expansion
set_program_name (argv[0]); Retains program name and discards path
setlocale (LC_ALL, ""); Sets up internationalization (i18n)
bindtextdomain (PACKAGE, LOCALEDIR); Assigns i18n directorySets text domain for _() [gettext()] function
textdomain (PACKAGE); Sets text domain for _() [gettext()] function
atexit (close_stdin); Close stdout on exit (see gnulib)
rm_option_init (&x); Line 224
/* Try to disable the ability to unlink a directory. */
priv_set_remove_linkdir (); Line 227
while ((c = getopt_long (argc, argv, "dfirvIR", long_opts, NULL)) != -1) Line 229
{
switch (c) Line 231
{
case 'd': Line 233
x.remove_empty_directories = true; Line 234
break; Line 235
case 'f': Line 237
x.interactive = RMI_NEVER; Line 238
x.ignore_missing_files = true; Line 239
prompt_once = false; Line 240
break; Line 241
case 'i': Line 243
x.interactive = RMI_ALWAYS; Line 244
x.ignore_missing_files = false; Line 245
prompt_once = false; Line 246
break; Line 247
case 'I': Line 249
x.interactive = RMI_SOMETIMES; Line 250
x.ignore_missing_files = false; Line 251
prompt_once = true; Line 252
break; Line 253
case 'r': Line 255
case 'R': Line 256
x.recursive = true; Line 257
break; Line 258
case INTERACTIVE_OPTION: Line 260
{
int i; Line 262
if (optarg) Line 263
i = XARGMATCH ("--interactive", optarg, interactive_args, Line 264
interactive_types); Line 265
else Line 266
i = interactive_always; Line 267
switch (i) Line 268
{
case interactive_never: Line 270
x.interactive = RMI_NEVER; Line 271
prompt_once = false; Line 272
break; Line 273
case interactive_once: Line 275
x.interactive = RMI_SOMETIMES; Line 276
x.ignore_missing_files = false; Line 277
prompt_once = true; Line 278
break; Line 279
case interactive_always: Line 281
x.interactive = RMI_ALWAYS; Line 282
x.ignore_missing_files = false; Line 283
prompt_once = false; Line 284
break; Line 285
}
break; Line 287
}
case ONE_FILE_SYSTEM: Line 290
x.one_file_system = true; Line 291
break; Line 292
case NO_PRESERVE_ROOT: Line 294
if (! STREQ (argv[optind - 1], "--no-preserve-root")) Line 295
die (EXIT_FAILURE, 0, Line 296
_("you may not abbreviate the --no-preserve-root option")); Line 297
preserve_root = false; Line 298
break; Line 299
case PRESERVE_ROOT: Line 301
if (optarg) Line 302
{
if STREQ (optarg, "all") Line 304
x.preserve_all_root = true; Line 305
else Line 306
{
die (EXIT_FAILURE, 0, Line 308
_("unrecognized --preserve-root argument: %s"), Line 309
quoteaf (optarg)); Line 310
}
}
preserve_root = true; Line 313
break; Line 314
case PRESUME_INPUT_TTY_OPTION: Line 316
x.stdin_tty = true; Line 317
break; Line 318
case 'v': Line 320
x.verbose = true; Line 321
break; Line 322
case_GETOPT_HELP_CHAR; Line 324
case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); Line 325
default: Line 326
diagnose_leading_hyphen (argc, argv); Line 327
usage (EXIT_FAILURE); Line 328
}
}
if (argc <= optind) Line 332
{
if (x.ignore_missing_files) Line 334
return EXIT_SUCCESS; Line 335
else Line 336
{
error (0, 0, _("missing operand")); Line 338
usage (EXIT_FAILURE); Line 339
}
}
if (x.recursive && preserve_root) Line 343
{
static struct dev_ino dev_ino_buf; Line 345
x.root_dev_ino = get_root_dev_ino (&dev_ino_buf); Line 346
if (x.root_dev_ino == NULL) Line 347
die (EXIT_FAILURE, errno, _("failed to get attributes of %s"), Line 348
quoteaf ("/")); Line 349
}
uintmax_t n_files = argc - optind; Line 352
char **file = argv + optind; Line 353
if (prompt_once && (x.recursive || 3 < n_files)) Line 355
{
fprintf (stderr, Line 357
(x.recursive Line 358
? ngettext ("%s: remove %"PRIuMAX" argument recursively? ", Line 359
"%s: remove %"PRIuMAX" arguments recursively? ", Line 360
select_plural (n_files)) Line 361
: ngettext ("%s: remove %"PRIuMAX" argument? ", Line 362
"%s: remove %"PRIuMAX" arguments? ", Line 363
select_plural (n_files))), Line 364
program_name, n_files); Line 365
if (!yesno ()) Line 366
return EXIT_SUCCESS; Line 367
}
enum RM_status status = rm (file, &x); Line 370
assert (VALID_STATUS (status)); Line 371
return status == RM_ERROR ? EXIT_FAILURE : EXIT_SUCCESS; Line 372
} Block 9