.
[coreutils.git] / src / rm.c
blob7a95032581e9b83f47331e87bee70805a2225898
1 /* `rm' file deletion utility for GNU.
2 Copyright (C) 1988, 1990, 1991, 1994, 1995 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 2, or (at your option)
7 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, write to the Free Software
16 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */
18 /* Written by Paul Rubin, David MacKenzie, and Richard Stallman. */
20 #include <config.h>
21 #include <stdio.h>
22 #include <getopt.h>
23 #include <sys/types.h>
25 #include "system.h"
26 #include "version.h"
27 #include "error.h"
29 #ifdef D_INO_IN_DIRENT
30 # define D_INO(dp) ((dp)->d_ino)
31 #else
32 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
33 # define D_INO(dp) 1
34 #endif
36 /* An element in a stack of pointers into `pathname'.
37 `pathp' points to where in `pathname' the terminating '\0' goes
38 for this level's directory name. */
39 struct pathstack
41 struct pathstack *next;
42 char *pathp;
43 ino_t inum;
46 char *basename ();
47 char *stpcpy ();
48 char *xmalloc ();
49 char *xrealloc ();
50 int euidaccess ();
51 int yesno ();
52 void strip_trailing_slashes ();
54 static int clear_directory __P ((struct stat *statp));
55 static int duplicate_entry __P ((struct pathstack *stack, ino_t inum));
56 static int remove_dir __P ((struct stat *statp));
57 static int remove_file __P ((struct stat *statp));
58 static int rm __P ((void));
59 static void usage __P ((int status));
61 /* Name this program was run with. */
62 char *program_name;
64 /* Linked list of pathnames of directories in progress in recursive rm.
65 The entries actually contain pointers into `pathname'.
66 `pathstack' is the current deepest level. */
67 static struct pathstack *pathstack = NULL;
69 /* Path of file now being processed; extended as necessary. */
70 static char *pathname;
72 /* Number of bytes currently allocated for `pathname';
73 made larger when necessary, but never smaller. */
74 static int pnsize;
76 /* If nonzero, display the name of each file removed. */
77 static int verbose;
79 /* If nonzero, ignore nonexistant files. */
80 static int ignore_missing_files;
82 /* If nonzero, recursively remove directories. */
83 static int recursive;
85 /* If nonzero, query the user about whether to remove each file. */
86 static int interactive;
88 /* If nonzero, remove directories with unlink instead of rmdir, and don't
89 require a directory to be empty before trying to unlink it.
90 Only works for the super-user. */
91 static int unlink_dirs;
93 /* If nonzero, stdin is a tty. */
94 static int stdin_tty;
96 /* If nonzero, display usage information and exit. */
97 static int show_help;
99 /* If nonzero, print the version on standard output and exit. */
100 static int show_version;
102 static struct option const long_opts[] =
104 {"directory", no_argument, &unlink_dirs, 1},
105 {"force", no_argument, NULL, 'f'},
106 {"interactive", no_argument, NULL, 'i'},
107 {"recursive", no_argument, &recursive, 1},
108 {"verbose", no_argument, &verbose, 1},
109 {"help", no_argument, &show_help, 1},
110 {"version", no_argument, &show_version, 1},
111 {NULL, 0, NULL, 0}
114 void
115 main (int argc, char **argv)
117 int err = 0;
118 int c;
120 verbose = ignore_missing_files = recursive = interactive
121 = unlink_dirs = 0;
122 pnsize = 256;
123 pathname = xmalloc (pnsize);
124 program_name = argv[0];
126 while ((c = getopt_long (argc, argv, "dfirvR", long_opts, (int *) 0)) != EOF)
128 switch (c)
130 case 0: /* Long option. */
131 break;
132 case 'd':
133 unlink_dirs = 1;
134 break;
135 case 'f':
136 interactive = 0;
137 ignore_missing_files = 1;
138 break;
139 case 'i':
140 interactive = 1;
141 ignore_missing_files = 0;
142 break;
143 case 'r':
144 case 'R':
145 recursive = 1;
146 break;
147 case 'v':
148 verbose = 1;
149 break;
150 default:
151 usage (1);
155 if (show_version)
157 printf ("rm - %s\n", version_string);
158 exit (0);
161 if (show_help)
162 usage (0);
164 if (optind == argc)
166 if (ignore_missing_files)
167 exit (0);
168 else
170 error (0, 0, _("too few arguments"));
171 usage (1);
175 stdin_tty = isatty (STDIN_FILENO);
177 for (; optind < argc; optind++)
179 int len;
181 /* Stripping slashes is harmless for rmdir;
182 if the arg is not a directory, it will fail with ENOTDIR. */
183 strip_trailing_slashes (argv[optind]);
184 len = strlen (argv[optind]);
185 if (len + 1 > pnsize)
187 free (pathname);
188 pnsize = 2 * (len + 1);
189 pathname = xmalloc (pnsize);
191 strcpy (pathname, argv[optind]);
192 err += rm ();
195 exit (err > 0);
198 /* Remove file or directory `pathname' after checking appropriate things.
199 Return 0 if `pathname' is removed, 1 if not. */
201 static int
202 rm (void)
204 struct stat path_stats;
205 char *base = basename (pathname);
207 if (base[0] == '.' && (base[1] == '\0'
208 || (base[1] == '.' && base[2] == '\0')))
210 error (0, 0, _("cannot remove `.' or `..'"));
211 return 1;
214 if (lstat (pathname, &path_stats))
216 if (errno == ENOENT && ignore_missing_files)
217 return 0;
218 error (0, errno, "%s", pathname);
219 return 1;
222 if (S_ISDIR (path_stats.st_mode) && !unlink_dirs)
223 return remove_dir (&path_stats);
224 else
225 return remove_file (&path_stats);
228 /* Query the user if appropriate, and if ok try to remove the
229 non-directory `pathname', which STATP contains info about.
230 Return 0 if `pathname' is removed, 1 if not. */
232 static int
233 remove_file (struct stat *statp)
235 if (!ignore_missing_files && (interactive || stdin_tty)
236 && euidaccess (pathname, W_OK)
237 #ifdef S_ISLNK
238 && !S_ISLNK (statp->st_mode)
239 #endif
242 fprintf (stderr, _("%s: remove %s`%s', overriding mode %04o? "),
243 program_name,
244 S_ISDIR (statp->st_mode) ? _("directory ") : "",
245 pathname,
246 (unsigned int) (statp->st_mode & 07777));
247 if (!yesno ())
248 return 1;
250 else if (interactive)
252 fprintf (stderr, _("%s: remove %s`%s'? "), program_name,
253 S_ISDIR (statp->st_mode) ? _("directory ") : "",
254 pathname);
255 if (!yesno ())
256 return 1;
259 if (verbose)
260 printf ("%s\n", pathname);
262 if (unlink (pathname) && (errno != ENOENT || !ignore_missing_files))
264 error (0, errno, "%s", pathname);
265 return 1;
267 return 0;
270 /* If not in recursive mode, print an error message and return 1.
271 Otherwise, query the user if appropriate, then try to recursively
272 remove directory `pathname', which STATP contains info about.
273 Return 0 if `pathname' is removed, 1 if not. */
275 static int
276 remove_dir (struct stat *statp)
278 int err;
280 if (!recursive)
282 error (0, 0, _("%s: is a directory"), pathname);
283 return 1;
286 if (!ignore_missing_files && (interactive || stdin_tty)
287 && euidaccess (pathname, W_OK))
289 fprintf (stderr,
290 _("%s: descend directory `%s', overriding mode %04o? "),
291 program_name, pathname,
292 (unsigned int) (statp->st_mode & 07777));
293 if (!yesno ())
294 return 1;
296 else if (interactive)
298 fprintf (stderr, _("%s: descend directory `%s'? "),
299 program_name, pathname);
300 if (!yesno ())
301 return 1;
304 if (verbose)
305 printf ("%s\n", pathname);
307 err = clear_directory (statp);
309 if (interactive)
311 if (err)
312 fprintf (stderr, _("%s: remove directory `%s' (might be nonempty)? "),
313 program_name, pathname);
314 else
315 fprintf (stderr, _("%s: remove directory `%s'? "),
316 program_name, pathname);
317 if (!yesno ())
318 return 1;
321 if (rmdir (pathname) && (errno != ENOENT || !ignore_missing_files))
323 error (0, errno, "%s", pathname);
324 return 1;
326 return 0;
329 /* Read directory `pathname' and remove all of its entries,
330 avoiding use of chdir.
331 On entry, STATP points to the results of stat on `pathname'.
332 Return 0 for success, error count for failure.
333 Upon return, `pathname' will have the same contents as before,
334 but its address might be different; in that case, `pnsize' will
335 be larger, as well. */
337 static int
338 clear_directory (struct stat *statp)
340 DIR *dirp;
341 struct dirent *dp;
342 char *name_space; /* Copy of directory's filenames. */
343 char *namep; /* Current entry in `name_space'. */
344 unsigned name_size; /* Bytes allocated for `name_space'. */
345 int name_length; /* Length of filename in `namep' plus '\0'. */
346 int pathname_length; /* Length of `pathname'. */
347 ino_t *inode_space; /* Copy of directory's inodes. */
348 ino_t *inodep; /* Current entry in `inode_space'. */
349 unsigned n_inodes_allocated; /* There is space for this many inodes
350 in `inode_space'. */
351 int err = 0; /* Return status. */
352 struct pathstack pathframe; /* New top of stack. */
353 struct pathstack *pp; /* Temporary. */
355 name_size = statp->st_size;
356 name_space = (char *) xmalloc (name_size);
358 n_inodes_allocated = (statp->st_size + sizeof (ino_t) - 1) / sizeof (ino_t);
359 inode_space = (ino_t *) xmalloc (n_inodes_allocated * sizeof (ino_t));
363 namep = name_space;
364 inodep = inode_space;
366 errno = 0;
367 dirp = opendir (pathname);
368 if (dirp == NULL)
370 if (errno != ENOENT || !ignore_missing_files)
372 error (0, errno, "%s", pathname);
373 err = 1;
375 free (name_space);
376 free (inode_space);
377 return err;
380 while ((dp = readdir (dirp)) != NULL)
382 /* Skip "." and "..". */
383 if (dp->d_name[0] != '.'
384 || (dp->d_name[1] != '\0'
385 && (dp->d_name[1] != '.' || dp->d_name[2] != '\0')))
387 unsigned size_needed = (namep - name_space) + NLENGTH (dp) + 2;
389 if (size_needed > name_size)
391 char *new_name_space;
393 while (size_needed > name_size)
394 name_size += 1024;
396 new_name_space = xrealloc (name_space, name_size);
397 namep += new_name_space - name_space;
398 name_space = new_name_space;
400 namep = stpcpy (namep, dp->d_name) + 1;
402 if (inodep == inode_space + n_inodes_allocated)
404 ino_t *new_inode_space;
406 n_inodes_allocated += 1024;
407 new_inode_space = (ino_t *) xrealloc (inode_space,
408 n_inodes_allocated * sizeof (ino_t));
409 inodep += new_inode_space - inode_space;
410 inode_space = new_inode_space;
412 *inodep++ = D_INO (dp);
415 *namep = '\0';
416 if (CLOSEDIR (dirp))
418 error (0, errno, "%s", pathname);
419 err = 1;
422 pathname_length = strlen (pathname);
424 for (namep = name_space, inodep = inode_space; *namep != '\0';
425 namep += name_length, inodep++)
427 name_length = strlen (namep) + 1;
429 /* Handle arbitrarily long filenames. */
430 if (pathname_length + 1 + name_length > pnsize)
432 char *new_pathname;
434 pnsize = (pathname_length + 1 + name_length) * 2;
435 new_pathname = xrealloc (pathname, pnsize);
436 /* Update all pointers in the stack to use the new area. */
437 for (pp = pathstack; pp != NULL; pp = pp->next)
438 pp->pathp += new_pathname - pathname;
439 pathname = new_pathname;
442 /* Add a new frame to the top of the path stack. */
443 pathframe.pathp = pathname + pathname_length;
444 pathframe.inum = *inodep;
445 pathframe.next = pathstack;
446 pathstack = &pathframe;
448 /* Append '/' and the filename to current pathname, take care of
449 the file (which could result in recursive calls), and take
450 the filename back off. */
452 *pathstack->pathp = '/';
453 strcpy (pathstack->pathp + 1, namep);
455 /* If the i-number has already appeared, there's an error. */
456 if (duplicate_entry (pathstack->next, pathstack->inum))
457 err++;
458 else if (rm ())
459 err++;
461 *pathstack->pathp = '\0';
462 pathstack = pathstack->next; /* Pop the stack. */
465 /* Keep trying while there are still files to remove. */
466 while (namep > name_space && err == 0);
468 free (name_space);
469 free (inode_space);
470 return err;
473 /* If STACK does not already have an entry with the same i-number as INUM,
474 return 0. Otherwise, ask the user whether to continue;
475 if yes, return 1, and if no, exit.
476 This assumes that no one tries to remove filesystem mount points;
477 doing so could cause duplication of i-numbers that would not indicate
478 a corrupted file system. */
480 static int
481 duplicate_entry (struct pathstack *stack, ino_t inum)
483 #ifdef D_INO_IN_DIRENT
484 struct pathstack *p;
486 for (p = stack; p != NULL; p = p->next)
488 if (p->inum == inum)
490 fprintf (stderr, _("\
491 %s: WARNING: Circular directory structure.\n\
492 This almost certainly means that you have a corrupted file system.\n\
493 NOTIFY YOUR SYSTEM MANAGER.\n\
494 Cycle detected:\n\
495 %s\n\
496 is the same file as\n"), program_name, pathname);
497 *p->pathp = '\0'; /* Truncate pathname. */
498 fprintf (stderr, "%s\n", pathname);
499 *p->pathp = '/'; /* Put it back. */
500 if (interactive)
502 fprintf (stderr, _("%s: continue? "), program_name);
503 if (!yesno ())
504 exit (1);
505 return 1;
507 else
508 exit (1);
511 #endif /* D_INO_IN_DIRENT */
512 return 0;
515 static void
516 usage (int status)
518 if (status != 0)
519 fprintf (stderr, _("Try `%s --help' for more information.\n"),
520 program_name);
521 else
523 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
524 printf (_("\
525 Remove (unlink) the FILE(s).\n\
527 -d, --directory unlink directory, even if non-empty (super-user only)\n\
528 -f, --force ignore nonexistent files, never prompt\n\
529 -i, --interactive prompt before any removal\n\
530 -v, --verbose explain what is being done\n\
531 -r, -R, --recursive remove the contents of directories recursively\n\
532 --help display this help and exit\n\
533 --version output version information and exit\n"));
535 exit (status);