sbase

suckless unix tools
git clone git://git.suckless.org/sbase
Log | Files | Refs | README | LICENSE

commit 0df8cdc12d7a5600ad0c2b9420a14be4e2af340b
parent 948e5161902920705f0c3a6458533fc017452173
Author: Roberto E. Vargas Caballero <k0ga@shike2.com>
Date:   Wed, 23 Apr 2025 22:12:52 +0200

rm: Add -i and cleanup rm()

POSIX mandates that if the input of rm is a tty and it does not have
write rights over a file/dir then it should ask for confirmation, in
the same way that is done with the -i flag. To accomodate both things
the code has been rearrenged a bit to have only one case instead of
having two. Also, this rework adds the error message when a directory
is removed without a -r flag.

Diffstat:
MREADME | 2+-
Mfs.h | 2++
Mlibutil/rm.c | 41+++++++++++++++++++++++++++++------------
Mmv.c | 2+-
Mrm.c | 9++++++---
5 files changed, 39 insertions(+), 17 deletions(-)

diff --git a/README b/README @@ -102,7 +102,7 @@ The following tools are implemented: 0=*|x readlink . 0=*|o renice . 0#* x rev . -0=*|o rm (-i) +0=*|o rm . 0=*|o rmdir . # sed . 0=*|x seq . diff --git a/fs.h b/fs.h @@ -24,6 +24,8 @@ enum { SAMEDEV = 1 << 0, DIRFIRST = 1 << 1, SILENT = 1 << 2, + CONFIRM = 1 << 3, + IGNORE = 1 << 4, }; extern int cp_aflag; diff --git a/libutil/rm.c b/libutil/rm.c @@ -14,19 +14,36 @@ int rm_status = 0; void rm(int dirfd, const char *name, struct stat *st, void *data, struct recursor *r) { - if (!r->maxdepth && S_ISDIR(st->st_mode)) { + int quiet, ask, write, flags, ignore; + + ignore = r->flags & IGNORE; + quiet = r->flags & SILENT; + ask = r->flags & CONFIRM; + write = faccessat(dirfd, name, W_OK, 0) == 0; + flags = 0; + + if (S_ISDIR(st->st_mode) && r->maxdepth) { + errno = EISDIR; + goto err; + } + + if (!quiet && (!write && isatty(0) || ask)) { + if (!confirm("remove file '%s'", r->path)); + return; + } + + if (S_ISDIR(st->st_mode)) { + flags = AT_REMOVEDIR; recurse(dirfd, name, NULL, r); + } + + if (unlinkat(dirfd, name, flags) < 0) + goto err; + return; - if (unlinkat(dirfd, name, AT_REMOVEDIR) < 0) { - if (!(r->flags & SILENT)) - weprintf("rmdir %s:", r->path); - if (!((r->flags & SILENT) && errno == ENOENT)) - rm_status = 1; - } - } else if (unlinkat(dirfd, name, 0) < 0) { - if (!(r->flags & SILENT)) - weprintf("unlink %s:", r->path); - if (!((r->flags & SILENT) && errno == ENOENT)) - rm_status = 1; +err: + if (!ignore) { + weprintf("cannot remove '%s':", r->path); + rm_status = 1; } } diff --git a/mv.c b/mv.c @@ -13,7 +13,7 @@ static int mv_status = 0; static int mv(const char *s1, const char *s2, int depth) { - struct recursor r = { .fn = rm, .follow = 'P' }; + struct recursor r = { .fn = rm, .follow = 'P', .flags = SILENT }; if (!rename(s1, s2)) return 0; diff --git a/rm.c b/rm.c @@ -7,7 +7,7 @@ static void usage(void) { - eprintf("usage: %s [-f] [-Rr] file ...\n", argv0); + eprintf("usage: %s [-f] [-iRr] file ...\n", argv0); } int @@ -17,7 +17,10 @@ main(int argc, char *argv[]) ARGBEGIN { case 'f': - r.flags |= SILENT; + r.flags |= SILENT | IGNORE; + break; + case 'i': + r.flags |= CONFIRM; break; case 'R': case 'r': @@ -28,7 +31,7 @@ main(int argc, char *argv[]) } ARGEND if (!argc) { - if (!(r.flags & SILENT)) + if (!(r.flags & IGNORE)) usage(); else return 0;