logo

utils-std

Collection of commonly available Unix tools
commit: c5bcb40a3b0676dab5aeddf9bc287444c67d16fa
parent 38a895b9b5a24bc3414a2e70cd61c746d3f1ec89
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date:   Mon,  8 Apr 2024 08:06:37 +0200

cmd/mkdir: new

Diffstat:

MMakefile4++++
Acmd/mkdir.149+++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/mkdir.c128+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Mcoreutils.txt2+-
Mlsb_commands.txt2+-
Mposix_utilities.txt2+-
Atest-cmd/mkdir.t77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
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 . + .