diff options
Diffstat (limited to 'toys/posix')
-rw-r--r-- | toys/posix/rm.c | 77 |
1 files changed, 77 insertions, 0 deletions
diff --git a/toys/posix/rm.c b/toys/posix/rm.c new file mode 100644 index 00000000..de565a76 --- /dev/null +++ b/toys/posix/rm.c @@ -0,0 +1,77 @@ +/* rm.c - remove files + * + * Copyright 2012 Rob Landley <rob@landley.net> + * + * See http://pubs.opengroup.org/onlinepubs/9699919799/utilities/rm.html + +USE_RM(NEWTOY(rm, "<1fiRr[-fi]", TOYFLAG_BIN)) + +config RM + bool "rm" + default y + help + usage: rm [-fiRr] FILE... + + Remove each argument from the filesystem. + + -f force: remove without confirmation, no error if it doesn't exist + -i interactive: prompt for confirmation + -rR recursive: remove directory contents +*/ + +#define FOR_rm +#include "toys.h" + +static int do_rm(struct dirtree *try) +{ + int fd = dirtree_parentfd(try), flags = toys.optflags; + int or = 0, using = 0; + + // Skip . and .. (yes, even explicitly on the command line: posix says to) + if (!dirtree_notdotdot(try)) return 0; + + if (S_ISDIR(try->st.st_mode)) { + if (flags & (FLAG_r|FLAG_R)) { + if (try->data != -1) return DIRTREE_COMEAGAIN; + using = AT_REMOVEDIR; + } + } + + // Prompt if necessary + if (!(flags & FLAG_f) && faccessat(fd, try->name, W_OK, AT_SYMLINK_NOFOLLOW)) + or++; + + if ((or && isatty(0)) || (flags & FLAG_i)) { + fprintf(stderr, "rm %s", or ? "ro " : ""); + if (!yesno(try->name, 2)) return 0; + } + + // Intentionally fail non-recursive attempts to remove even an empty dir + // because POSIX says to. + if (unlinkat(fd, try->name, using)) { + perror_msg("%s", try->name); + toys.exitval = 1; + } + + return 0; +} + +void rm_main(void) +{ + char **s; + + for (s = toys.optargs; *s; s++) { + if (!strcmp(*s, "/")) { + error_msg("rm /. if you mean it"); + toys.exitval = 1; + continue; + } + + // There's a race here where a file removed between this access and + // dirtree's stat would report the nonexistence as an error, but that's + // not a normal "it didn't exist" so I'm ok with it. + if ((toys.optflags & FLAG_f) && (access(*s, F_OK) && errno == ENOENT)) + continue; + dirtree_read(*s, do_rm); + } +} |