changeset 737:dc4a38a13270

New rm command.
author Rob Landley <rob@landley.net>
date Fri, 07 Dec 2012 06:26:21 -0600
parents e7694c640f36
children 075eaff297f8
files toys/posix/rm.c
diffstat 1 files changed, 77 insertions(+), 0 deletions(-) [+]
line wrap: on
line diff
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/toys/posix/rm.c	Fri Dec 07 06:26:21 2012 -0600
@@ -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);
+  }
+}