commit: 4cfef864133f164256fae611eeb598c66d3f10a1
parent 73090d1f5b34a8eaf35423037b1cf8ce8559c5a9
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Tue, 30 Jul 2024 14:52:05 +0200
cmd/mktemp: new
Diffstat:
6 files changed, 226 insertions(+), 2 deletions(-)
diff --git a/cmd/mktemp.1 b/cmd/mktemp.1
@@ -0,0 +1,55 @@
+.\" 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-07-30
+.Dt MKTEMP 1
+.Os
+.Sh NAME
+.Nm mktemp
+.Nd make a new randomly-named file or directory
+.Sh SYNOPSIS
+.Nm
+.Op Fl dqt
+.Op Fl p Ar tempdir
+.Op Ar template
+.Sh DESCRIPTION
+.Nm
+creates a new randomly-named file or directory,
+and prints its name.
+.Pp
+.Ar template
+must contain at least 6 consecutive
+.Ql X
+as last path component.
+If
+.Ar template
+is not specified,
+.Ql tmp.XXXXXXXXXX
+is used instead.
+.br
+Files are created with u+rw permissions, directories with u+rwx.
+.Sh OPTIONS
+.Bl -tag -width _d
+.It Fl d
+Create a directory, not a file.
+.It Fl q
+Quiet messages about file/directory creation failure.
+.It Fl p Ar tempdir
+Specify
+.Ar tempdir
+as an existing directory to create into.
+Overrides
+.Ev TMPDIR .
+.It Fl t
+Use
+.Ev TMPDIR
+or if unset,
+.Pa /tmp ,
+as an existing directory to create into.
+.El
+.Sh EXIT STATUS
+.Ex -std
+.Sh STANDARDS
+None known.
+.Sh AUTHORS
+.An Haelwenn (lanodan) Monnier Aq Mt contact+utils@hacktivis.me
diff --git a/cmd/mktemp.c b/cmd/mktemp.c
@@ -0,0 +1,99 @@
+// 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 <limits.h> // PATH_MAX
+#include <stdbool.h>
+#include <stdio.h> // fprintf
+#include <stdlib.h> // getenv
+#include <string.h> // strerror
+#include <unistd.h> // getopt
+
+int
+main(int argc, char *argv[])
+{
+ bool o_create_dir = false, o_quiet = false;
+ char template[PATH_MAX] = "tmp.XXXXXXXXXX";
+ char *tmpdir = NULL;
+
+ int c = -1;
+ while((c = getopt(argc, argv, ":dqp:t")) != -1)
+ {
+ switch(c)
+ {
+ case 'd':
+ o_create_dir = true;
+ break;
+ case 'q':
+ o_quiet = true;
+ break;
+ case 'p':
+ tmpdir = optarg;
+ break;
+ case 't':
+ if(tmpdir == NULL) tmpdir = getenv("TMPDIR");
+ if(tmpdir == NULL) tmpdir = "/tmp";
+ break;
+ default:
+ abort();
+ }
+ }
+
+ argc -= optind;
+ argv += optind;
+
+ if(argc == 1)
+ {
+ strncpy(template, *argv, PATH_MAX);
+ }
+ else if(argc > 1)
+ {
+ fprintf(stderr, "mktemp: Only one template argument is supported, got %d\n", argc);
+ return 1;
+ }
+
+ if(tmpdir)
+ {
+ if(chdir(tmpdir) < 0)
+ {
+ fprintf(stderr,
+ "mktemp: Failed changing directory into tmpdir '%s': %s\n",
+ tmpdir,
+ strerror(errno));
+ return 1;
+ }
+ }
+
+ if(o_create_dir)
+ {
+ char *dir = mkdtemp(template);
+ if(dir == NULL)
+ {
+ fprintf(stderr, "mktemp: Failed creating random directory: %s\n", strerror(errno));
+ return 1;
+ }
+
+ puts(dir);
+
+ return 0;
+ }
+
+ int fd = mkstemp(template);
+ if(fd < 0)
+ {
+ fprintf(stderr, "mktemp: Failed creating random file: %s\n", strerror(errno));
+ return 1;
+ }
+
+ puts(template);
+
+ if(close(fd) < 0)
+ {
+ fprintf(stderr, "mktemp: Failed closing file descriptor: %s\n", strerror(errno));
+ return 1;
+ }
+
+ return 0;
+}
diff --git a/coreutils.txt b/coreutils.txt
@@ -46,7 +46,7 @@ md5sum: No
mkdir: Done
mkfifo: Done
mknod: Done
-mktemp: Todo
+mktemp: Done
mv
nice: Done
nl: No, use sed
diff --git a/lsb_commands.txt b/lsb_commands.txt
@@ -80,7 +80,7 @@ md5sum: No
mkdir: Done
mkfifo: Done
mknod: Done
-mktemp: Todo
+mktemp: Done
more: No
mount: out of scope
msgfmt: out of scope
diff --git a/test-cmd/Kyuafile b/test-cmd/Kyuafile
@@ -25,6 +25,7 @@ tap_test_program{name="head.sh", required_files=basedir.."/cmd/head", timeout=1}
tap_test_program{name="id.sh", required_files=basedir.."/cmd/id", timeout=1}
tap_test_program{name="link.sh", required_files=basedir.."/cmd/link", timeout=1}
tap_test_program{name="logname.sh", required_files=basedir.."/cmd/logname", timeout=1}
+tap_test_program{name="mktemp.sh", required_files=basedir.."/cmd/mktemp", timeout=1}
tap_test_program{name="pwd.sh", required_files=basedir.."/cmd/pwd", timeout=1}
tap_test_program{name="realpath.sh", required_files=basedir.."/cmd/realpath", timeout=1}
tap_test_program{name="seq.sh", required_files=basedir.."/cmd/seq", timeout=1}
diff --git a/test-cmd/mktemp.sh b/test-cmd/mktemp.sh
@@ -0,0 +1,69 @@
+#!/bin/sh
+# SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+# SPDX-License-Identifier: MPL-2.0
+
+target="$(dirname "$0")/../cmd/mktemp"
+plans=7
+. "$(dirname "$0")/tap.sh"
+
+t_mktemp()
+{
+ count=$((count+1))
+
+ # Append a final slash so sh(1) doesn't trims final newlines
+ out="$("${target?}" $2 2>&1;r=$?;echo -n /;exit $r)"
+ ret="$?"
+ out="${out%/}"
+
+ test -f "$out"
+ test_ret="$?"
+
+ rm -f "$out"
+
+ if [ "$ret" != 0 ] && [ "$test_ret" != 0 ]; then
+ printf 'not ok %d - %s\n' "$count" "$1"
+ printf '# exit code: %d\n' "$ret"
+ printf '# test -f => %d\n' "$?"
+ err=1
+ else
+ printf 'ok %d - %s\n' "$count" "$1"
+ fi
+ printf "$out" | sed -e 's;^;# ;'
+}
+
+t_mkdtemp()
+{
+ count=$((count+1))
+
+ # Append a final slash so sh(1) doesn't trims final newlines
+ out="$("${target?}" $2 2>&1;r=$?;echo -n /;exit $r)"
+ ret="$?"
+ out="${out%/}"
+
+ test -d "$out"
+ test_ret="$?"
+
+ rm -fr "$out"
+
+ if [ "$ret" != 0 ] && [ "$test_ret" != 0 ]; then
+ printf 'not ok %d - %s\n' "$count" "$1"
+ printf '# exit code: %d\n' "$ret"
+ printf '# test -d => %d\n' "$?"
+ err=1
+ else
+ printf 'ok %d - %s\n' "$count" "$1"
+ fi
+ printf "$out" | sed -e 's;^;# ;'
+}
+
+# TODO: Figure a way to do harsher tests, for example testing patterns, directory contents, …
+
+t_mktemp noargs ''
+t_mktemp template 'template.XXXXXX'
+t_mktemp tmpdir '-t'
+t_mkdtemp dir '-d'
+t_mkdtemp dir_template '-d template.XXXXXX'
+t_mkdtemp dir_tmpdir '-dt'
+
+t --exit=1 templ2 'foo bar' 'mktemp: Only one template argument is supported, got 2
+'