logo

utils-std

Collection of commonly available Unix tools
commit: 54b0cd7abcba5c5a4502a7c44f84cd0e32dd94e4
parent 7f45e61983d77cadaaebadcfdfa2b924b9cfdf70
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Tue, 26 Mar 2024 10:09:46 +0100

cmd/rmdir: new

Diffstat:

Mcmd/rm.c1+
Acmd/rmdir.141+++++++++++++++++++++++++++++++++++++++++
Acmd/rmdir.c98+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcoreutils.txt2+-
Mlsb_commands.txt2+-
Atest-cmd/rmdir.t40++++++++++++++++++++++++++++++++++++++++
6 files changed, 182 insertions(+), 2 deletions(-)

diff --git a/cmd/rm.c b/cmd/rm.c @@ -192,6 +192,7 @@ do_unlinkat(int fd, char *name, char *acc_path) return err; } + void usage() { diff --git a/cmd/rmdir.1 b/cmd/rmdir.1 @@ -0,0 +1,41 @@ +.\" utils-std: Collection of commonly available Unix tools +.\" Copyright 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +.\" SPDX-License-Identifier: MPL-2.0 +.Dd 2024-03-26 +.Dt RMDIR 1 +.Os +.Sh NAME +.Nm rmdir +.Nd remove directories +.Sh SYNOPSIS +.Nm +.Op Fl pv +.Ar directory... +.Sh DESCRIPTION +The +.Nm +utility removes each given +.Ar directory . +.Sh OPTIONS +.Bl -tag -width aa +.It Fl p +Remove all parents directories present in the +.Ar directory +argument, but not beyond. +This is effectively the opposite of +.Cm mkdir +.Fl p +.Ar directory +.It Fl v +Verbose, print each action done +.El +.Sh EXIT STATUS +.Ex -std +.Sh SEE ALSO +.Xr rm 1 +.Sh STANDARDS +.Nm +should be compliant with +.St -p1003.1-2008 +.Sh AUTHORS +.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me diff --git a/cmd/rmdir.c b/cmd/rmdir.c @@ -0,0 +1,98 @@ +// utils-std: Collection of commonly available Unix tools +// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +// SPDX-License-Identifier: MPL-2.0 + +#define _POSIX_C_SOURCE 200809L +#include <errno.h> +#include <stdbool.h> +#include <stdio.h> // fprintf +#include <stdlib.h> // abort +#include <string.h> // strerror, strrchr +#include <unistd.h> // getopt, rmdir + +void +usage() +{ + fprintf(stderr, "Usage: rmdir [-pv] directory...\n"); +} + +int +main(int argc, char *argv[]) +{ + bool parents = false, verbose = false; + + int c = -1; + while((c = getopt(argc, argv, ":pv")) != -1) + { + switch(c) + { + case 'p': + parents = true; + break; + case 'v': + verbose = true; + break; + case ':': + fprintf(stderr, "rmdir: Error: Missing operand for option: '-%c'\n", optopt); + usage(); + return 1; + case '?': + fprintf(stderr, "rmdir: Error: Unrecognised option: '-%c'\n", optopt); + usage(); + return 1; + default: + abort(); + } + } + + argc -= optind; + argv += optind; + + if(argc == 0) + { + fprintf(stderr, "rmdir: missing operand\n"); + usage(); + return 1; + } + + int err = 0; + + for(int i = 0; i < argc; i++) + { + errno = 0; + + char *path = argv[i]; + if(rmdir(path) < 0) + { + fprintf(stderr, "rmdir: Failed removing '%s': %s\n", path, strerror(errno)); + err = 1; + continue; + } + if(verbose) fprintf(stderr, "rmdir: Removed '%s'\n", path); + + if(!parents) continue; + + while(true) + { + char *sep = strrchr(path, '/'); + if(sep == NULL) break; + + *sep = 0; + + if(*path == 0) break; + + errno = 0; + if(rmdir(path) < 0) + { + if(errno == ENOTDIR) break; + + fprintf(stderr, "rmdir: Failed removing '%s': %s\n", path, strerror(errno)); + err = 1; + break; + } + if(verbose) fprintf(stderr, "rmdir: Removed '%s'\n", path); + } + } + + return err; +} diff --git a/coreutils.txt b/coreutils.txt @@ -65,7 +65,7 @@ pwd: Done readlink: ? realpath: Done rm: Todo -rmdir: No +rmdir: Done runcon: No. (SELinux-specific util, wtf) seq: Done sha1sum: No diff --git a/lsb_commands.txt b/lsb_commands.txt @@ -103,7 +103,7 @@ pwd: Done remove_initd: ? renice: Maybe rm: Todo -rmdir: No +rmdir: Done sed: out of scope sendmail: out of scope seq: Done diff --git a/test-cmd/rmdir.t b/test-cmd/rmdir.t @@ -0,0 +1,40 @@ +#!/usr/bin/env cram +# SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me> +# SPDX-License-Identifier: MPL-2.0 + + $ export PATH="$TESTDIR/../cmd:$PATH" + + $ test "$(command -v rmdir)" = "$TESTDIR/../cmd/rmdir" + + $ mkdir -p no_p/bar + $ rmdir no_p/bar + $ test -d no_p + + $ mkdir -p p/bar + $ rmdir -p p/bar + $ test ! -e p + + $ mkdir -p v_no_p/bar + $ rmdir -v v_no_p/bar + rmdir: Removed 'v_no_p/bar' + $ test -d v_no_p + + $ mkdir -p v_p/bar + $ rmdir -pv v_p/bar + rmdir: Removed 'v_p/bar' + rmdir: Removed 'v_p' + $ test ! -e v_p + + $ touch file + $ rmdir file + rmdir: Failed removing 'file': Not a directory + [1] + $ rmdir -p file + rmdir: Failed removing 'file': Not a directory + [1] + $ rmdir -v file + rmdir: Failed removing 'file': Not a directory + [1] + $ rmdir -pv file + rmdir: Failed removing 'file': Not a directory + [1]