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)
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. */
23 #include <sys/types.h>
29 #ifdef D_INO_IN_DIRENT
30 # define D_INO(dp) ((dp)->d_ino)
32 /* Some systems don't have inodes, so fake them to avoid lots of ifdefs. */
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. */
41 struct pathstack
*next
;
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. */
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. */
76 /* If nonzero, display the name of each file removed. */
79 /* If nonzero, ignore nonexistant files. */
80 static int ignore_missing_files
;
82 /* If nonzero, recursively remove directories. */
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. */
96 /* If nonzero, display usage information and exit. */
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},
115 main (int argc
, char **argv
)
120 verbose
= ignore_missing_files
= recursive
= interactive
123 pathname
= xmalloc (pnsize
);
124 program_name
= argv
[0];
126 while ((c
= getopt_long (argc
, argv
, "dfirvR", long_opts
, (int *) 0)) != EOF
)
130 case 0: /* Long option. */
137 ignore_missing_files
= 1;
141 ignore_missing_files
= 0;
157 printf ("rm - %s\n", version_string
);
166 if (ignore_missing_files
)
170 error (0, 0, _("too few arguments"));
175 stdin_tty
= isatty (STDIN_FILENO
);
177 for (; optind
< argc
; optind
++)
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
)
188 pnsize
= 2 * (len
+ 1);
189 pathname
= xmalloc (pnsize
);
191 strcpy (pathname
, argv
[optind
]);
198 /* Remove file or directory `pathname' after checking appropriate things.
199 Return 0 if `pathname' is removed, 1 if not. */
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 `..'"));
214 if (lstat (pathname
, &path_stats
))
216 if (errno
== ENOENT
&& ignore_missing_files
)
218 error (0, errno
, "%s", pathname
);
222 if (S_ISDIR (path_stats
.st_mode
) && !unlink_dirs
)
223 return remove_dir (&path_stats
);
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. */
233 remove_file (struct stat
*statp
)
235 if (!ignore_missing_files
&& (interactive
|| stdin_tty
)
236 && euidaccess (pathname
, W_OK
)
238 && !S_ISLNK (statp
->st_mode
)
242 fprintf (stderr
, _("%s: remove %s`%s', overriding mode %04o? "),
244 S_ISDIR (statp
->st_mode
) ? _("directory ") : "",
246 (unsigned int) (statp
->st_mode
& 07777));
250 else if (interactive
)
252 fprintf (stderr
, _("%s: remove %s`%s'? "), program_name
,
253 S_ISDIR (statp
->st_mode
) ? _("directory ") : "",
260 printf ("%s\n", pathname
);
262 if (unlink (pathname
) && (errno
!= ENOENT
|| !ignore_missing_files
))
264 error (0, errno
, "%s", pathname
);
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. */
276 remove_dir (struct stat
*statp
)
282 error (0, 0, _("%s: is a directory"), pathname
);
286 if (!ignore_missing_files
&& (interactive
|| stdin_tty
)
287 && euidaccess (pathname
, W_OK
))
290 _("%s: descend directory `%s', overriding mode %04o? "),
291 program_name
, pathname
,
292 (unsigned int) (statp
->st_mode
& 07777));
296 else if (interactive
)
298 fprintf (stderr
, _("%s: descend directory `%s'? "),
299 program_name
, pathname
);
305 printf ("%s\n", pathname
);
307 err
= clear_directory (statp
);
312 fprintf (stderr
, _("%s: remove directory `%s' (might be nonempty)? "),
313 program_name
, pathname
);
315 fprintf (stderr
, _("%s: remove directory `%s'? "),
316 program_name
, pathname
);
321 if (rmdir (pathname
) && (errno
!= ENOENT
|| !ignore_missing_files
))
323 error (0, errno
, "%s", pathname
);
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. */
338 clear_directory (struct stat
*statp
)
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
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
));
364 inodep
= inode_space
;
367 dirp
= opendir (pathname
);
370 if (errno
!= ENOENT
|| !ignore_missing_files
)
372 error (0, errno
, "%s", pathname
);
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
)
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
);
418 error (0, errno
, "%s", pathname
);
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
)
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
))
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);
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. */
481 duplicate_entry (struct pathstack
*stack
, ino_t inum
)
483 #ifdef D_INO_IN_DIRENT
486 for (p
= stack
; p
!= NULL
; p
= p
->next
)
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\
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. */
502 fprintf (stderr
, _("%s: continue? "), program_name
);
511 #endif /* D_INO_IN_DIRENT */
519 fprintf (stderr
, _("Try `%s --help' for more information.\n"),
523 printf (_("Usage: %s [OPTION]... FILE...\n"), program_name
);
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"));