commit: 54b0cd7abcba5c5a4502a7c44f84cd0e32dd94e4
parent 7f45e61983d77cadaaebadcfdfa2b924b9cfdf70
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Tue, 26 Mar 2024 10:09:46 +0100
cmd/rmdir: new
Diffstat:
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]