commit: c5bcb40a3b0676dab5aeddf9bc287444c67d16fa
parent 38a895b9b5a24bc3414a2e70cd61c746d3f1ec89
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Mon, 8 Apr 2024 08:06:37 +0200
cmd/mkdir: new
Diffstat:
7 files changed, 261 insertions(+), 3 deletions(-)
diff --git a/Makefile b/Makefile
@@ -88,6 +88,10 @@ cmd/chmod: cmd/chmod.c lib/mode.c lib/symbolize_mode.c Makefile
rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
$(CC) -std=c99 $(CFLAGS) -o $@ cmd/chmod.c lib/mode.c lib/symbolize_mode.c $(LDFLAGS) $(LDSTATIC)
+cmd/mkdir: cmd/mkdir.c lib/mode.c Makefile
+ rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
+ $(CC) -std=c99 $(CFLAGS) -o $@ cmd/mkdir.c lib/mode.c $(LDFLAGS) $(LDSTATIC)
+
cmd/seq: cmd/seq.c lib/absu.h Makefile
rm -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
$(CC) -std=c99 $(CFLAGS) -o $@ cmd/seq.c -lm $(LDFLAGS) $(LDSTATIC)
diff --git a/cmd/mkdir.1 b/cmd/mkdir.1
@@ -0,0 +1,49 @@
+.\" 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-04-08
+.Dt MKDIR 1
+.Os
+.Sh NAME
+.Nm mkdir
+.Nd make directories
+.Sh SYNOPSIS
+.Nm
+.Op Fl pv
+.Op Fl m Ar mode
+.Ar dir...
+.Sh DESCRIPTION
+.Nm
+creates each given
+.Ar dir
+in the order specified.
+.Sh OPTIONS
+.Bl -tag -width _m_mode
+.It Fl m Ar mode
+Set permission of new directories specified by
+.Ar dir ,
+relative to a=rwx using the same format as
+.Xr chmod 1 .
+.It Fl p
+Parents mode, create missing directories present in the path given by
+.Ar dir .
+As required by POSIX, parents are created with u=wx|~umask as their default mode, ignoring
+.Fl m Ar mode .
+.It Fl v
+Verbose mode, print each successfully made directory.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr chmod 1
+.Sh STANDARDS
+.Nm
+should be compliant with the
+.St -p1003.1-2008
+specification.
+.Pp
+The
+.Fl v
+option is an extension also present in GNU coreutils and FreeBSD.
+.Sh AUTHORS
+.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me
diff --git a/cmd/mkdir.c b/cmd/mkdir.c
@@ -0,0 +1,128 @@
+// 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 "../lib/mode.h"
+
+#include <assert.h>
+#include <errno.h>
+#include <limits.h> // PATH_MAX
+#include <stdbool.h>
+#include <stdio.h> // fprintf
+#include <string.h> // strerror, strrchr
+#include <sys/stat.h> // mkdir
+#include <unistd.h> // getopt
+
+bool parents = false, verbose = false;
+mode_t filemask;
+
+static int
+do_mkdir(char *path, mode_t mode)
+{
+ for(int i = strlen(path) - 1; i >= 0; i--)
+ {
+ if(path[i] != '/')
+ break;
+ else
+ path[i] = 0;
+ }
+
+ if(parents)
+ {
+ assert(errno == 0);
+ /* flawfinder: ignore */
+ if(access(path, F_OK) == 0) return 0;
+
+ errno = 0;
+
+ char parent[PATH_MAX] = "";
+ strncpy(parent, path, PATH_MAX);
+
+ for(int i = strlen(parent); i >= 0; i--)
+ {
+ char c = parent[i];
+ parent[i] = 0;
+ if(c == '/') break;
+ }
+
+ /* flawfinder: ignore */
+ if(parent[0] != 0 && access(parent, F_OK) != 0)
+ {
+ errno = 0;
+ mode_t parent_mode = (S_IWUSR | S_IXUSR | ~filemask) & 0777;
+ if(do_mkdir(parent, parent_mode) < 0) return -1;
+ }
+ }
+
+ assert(errno == 0);
+ if(mkdir(path, mode) < 0)
+ {
+ fprintf(stderr, "mkdir: Failed making directory '%s': %s\n", path, strerror(errno));
+ errno = 0;
+ return -1;
+ }
+
+ if(verbose) fprintf(stderr, "mkdir: Made directory: %s\n", path);
+
+ return 0;
+}
+
+static void
+usage()
+{
+ fprintf(stderr, "Usage: mkdir [-pv] [-m mode] path ...\n");
+}
+
+int
+main(int argc, char *argv[])
+{
+ const char *errstr = NULL;
+
+ filemask = umask(0);
+ umask(filemask);
+
+ mode_t mode = (S_IRWXU | S_IRWXG | S_IRWXO | ~filemask) & 0777;
+
+ int c = -1;
+ while((c = getopt(argc, argv, ":pvm:")) != -1)
+ {
+ switch(c)
+ {
+ case 'p':
+ parents = true;
+ break;
+ case 'v':
+ verbose = true;
+ break;
+ case 'm':
+ mode = new_mode(optarg, 0777, &errstr);
+ if(errstr != NULL)
+ {
+ fprintf(stderr, "mkdir: Failed parsing '%s': %s\n", optarg, errstr);
+ return 1;
+ }
+ break;
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ assert(errno == 0);
+
+ if(argc < 1)
+ {
+ fprintf(stderr, "mkdir: Missing operand\n");
+ usage();
+ return 1;
+ }
+
+ for(int i = 0; i < argc; i++)
+ {
+ if(do_mkdir(argv[i], mode) < 0) return 1;
+ }
+
+ return 0;
+}
diff --git a/coreutils.txt b/coreutils.txt
@@ -43,7 +43,7 @@ ln: Todo
logname: Done
ls: ?
md5sum: No
-mkdir: Todo
+mkdir: Done
mkfifo: ?
mknod: Todo
mktemp: Todo
diff --git a/lsb_commands.txt b/lsb_commands.txt
@@ -77,7 +77,7 @@ mailx: No
make: out of scope
man: out of scope
md5sum: No
-mkdir: Todo
+mkdir: Done
mkfifo: ?
mknod: Todo
mktemp: Todo
diff --git a/posix_utilities.txt b/posix_utilities.txt
@@ -80,7 +80,7 @@ mailx: no
make: no
man: no
mesg
-mkdir
+mkdir: done
mkfifo
more: no?
mv
diff --git a/test-cmd/mkdir.t b/test-cmd/mkdir.t
@@ -0,0 +1,77 @@
+#!/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 mkdir)" = "$TESTDIR/../cmd/mkdir"
+
+# Using chmod -v + to get permission bits, + with no perm nor who doesn't changes mode bits
+ $ test "$(command -v chmod)" = "$TESTDIR/../cmd/chmod"
+
+ $ mkdir
+ mkdir: Missing operand
+ Usage: mkdir [-pv] [-m mode] path ...
+ [1]
+
+ $ umask 002
+
+ $ test ! -e foo
+ $ mkdir foo
+ $ test -d foo
+ $ rm -r foo
+
+ $ test ! -e enoent
+ $ mkdir enoent/file
+ mkdir: Failed making directory 'enoent/file': No such file or directory
+ [1]
+ $ test ! -e enoent
+
+ $ test ! -e gaia
+ $ mkdir -p gaia/zeus
+ $ chmod -v + gaia gaia/zeus
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'gaia'
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'gaia/zeus'
+ $ rm -r gaia
+
+ $ mkdir -v verbose1 verbose2
+ mkdir: Made directory: verbose1
+ mkdir: Made directory: verbose2
+ $ chmod -v + verbose1 verbose2
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'verbose1'
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'verbose2'
+ $ mkdir -v verbose_foo/verbose_bar
+ mkdir: Failed making directory 'verbose_foo/verbose_bar': No such file or directory
+ [1]
+ $ test ! -e verbose_foo
+ $ mkdir -vp verbose_foop/verbose_barp
+ mkdir: Made directory: verbose_foop
+ mkdir: Made directory: verbose_foop/verbose_barp
+ $ chmod -v + verbose_foop verbose_foop/verbose_barp
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'verbose_foop'
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'verbose_foop/verbose_barp'
+ $ rm -r verbose1 verbose2 verbose_foop
+
+ $ mkdir -m +x plus_x
+ $ chmod -v + plus_x
+ chmod: Permissions already set to 00775/drwxrwxr-x for 'plus_x'
+ $ rm -r plus_x
+ $ mkdir -m go-x go_minus_x
+ $ chmod -v + go_minus_x
+ chmod: Permissions already set to 00764/drwxrw-r-- for 'go_minus_x'
+ $ rm -r go_minus_x
+ $ mkdir -m -x minus_x
+ $ chmod -v + minus_x
+ chmod: Permissions already set to 00664/drw-rw-r-- for 'minus_x'
+ $ rm -r minus_x
+
+ $ umask 024
+ $ mkdir -p zero_two/four
+ $ chmod -v + zero_two zero_two/four
+ chmod: Permissions already set to 00753/drwxr-x-wx for 'zero_two'
+ chmod: Permissions already set to 00753/drwxr-x-wx for 'zero_two/four'
+ $ rm -r zero_two
+
+No files should be left
+ $ find .
+ .