aboutsummaryrefslogtreecommitdiff
path: root/toys/posix/rm.c
diff options
context:
space:
mode:
Diffstat (limited to 'toys/posix/rm.c')
-rw-r--r--toys/posix/rm.c77
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);
+ }
+}