view toys/posix/rm.c @ 818:264b9da809df

Simplify license text, as mentioned on the mailing list. Reasoning: it was never my intent to require anybody to copy license text into another project if they cut and pasted something out of toybox. The "permission for any purpose" is as close to public domain as you can get in our current screwed up legal system without making people uncomfortable the _other_ way. (Besides, my initial reading of that was "all copies of the source code" but that's not what it says, and somebody pointed out that Android has "show license text" options because paranoid lawyers think that sort of thing applies to the BINARY version, which is nuts.)
author Rob Landley <rob@landley.net>
date Thu, 14 Mar 2013 09:02:37 -0500
parents 6cc69be43c42
children 673fe8f927b0
line wrap: on
line source

/* 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, "fiRr[-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 dir = S_ISDIR(try->st.st_mode), or = 0, using = 0;

  // Skip . and .. (yes, even explicitly on the command line: posix says to)
  if (!dirtree_notdotdot(try)) return 0;

  // Intentionally fail non-recursive attempts to remove even an empty dir
  // (via wrong flags to unlinkat) because POSIX says to.
  if (dir && !(flags & (FLAG_r|FLAG_R))) goto skip;

  // This is either the posix section 2(b) prompt or the section 3 prompt.
  if (!(flags & FLAG_f)
    && (!S_ISLNK(try->st.st_mode) && faccessat(fd, try->name, W_OK, 0))) or++;
  if (!(dir && try->data == -1) && ((or && isatty(0)) || (flags & FLAG_i))) {
    char *s = dirtree_path(try, 0);
    fprintf(stderr, "rm %s%s", or ? "ro " : "", dir ? "dir " : "");
    or = yesno(s, 0);
    free(s);
    if (!or) goto nodelete;
  }

  // handle directory recursion
  if (dir) {

    if (try->data != -1) return DIRTREE_COMEAGAIN;
    using = AT_REMOVEDIR;
    if (try->symlink) goto nodelete;
    if (flags & FLAG_i) {
      char *s = dirtree_path(try, 0);
      // This is the section 2(d) prompt. (Yes, posix says to prompt twice.)
      fprintf(stderr, "rmdir ");
      or = yesno(s, 0);
      free(s);
      if (!or) goto nodelete;
    }
  }

skip:
  if (unlinkat(fd, try->name, using)) {
    perror_msg("%s", try->name);
nodelete:
    if (try->parent) try->parent->symlink = (char *)1;
  }

  return 0;
}

void rm_main(void)
{
  char **s;

  // Can't use <1 in optstring because zero arguments with -f isn't an error
  if (!toys.optc && !(toys.optflags & FLAG_f)) error_exit("Needs 1 argument");

  for (s = toys.optargs; *s; s++) {
    if (!strcmp(*s, "/")) {
      error_msg("rm /. if you mean it");
      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);
  }
}