commit: 00afd8cbb84b80cbddb6ea7a225e01fbd5c2b8ab
parent d0ea89a5b18bf63f4a3dbf1516d1605137399559
Author: Haelwenn (lanodan) Monnier <contact@hacktivis.me>
Date: Wed, 25 Jun 2025 02:54:10 +0200
Move libs for libutils.a into libutils
And keep header-only and ones used by a single-command into lib/
to avoid needlessly invalidating libutils.a
Note: Technically libutils/humanize is only used by cmd/df but it
will also have to be used by cmd/du, cmd/ls, … when they land.
Diffstat:
99 files changed, 1196 insertions(+), 1192 deletions(-)
diff --git a/.gitignore b/.gitignore
@@ -17,9 +17,11 @@
/build/
/test-lib/*
-!/test-lib/Kuafile
!/test-lib/*.c
!/test-lib/*.h
+/test-libutils/*
+!/test-libutils/*.c
+!/test-libutils/*.h
/test-cmd/pathchk-getlimits
diff --git a/Makefile b/Makefile
@@ -32,7 +32,7 @@ selfcheck-cmds: $(EXE) $(TEST_CMDS)
LDSTATIC="$(LDSTATIC)" ./check-cmds.sh
.PHONY: selfcheck-libs
-TEST_LIBS = test-lib/t_mode test-lib/t_strtodur test-lib/t_symbolize_mode test-lib/t_truncation test-lib/t_sha1 test-lib/t_sha256 test-lib/t_sha512 test-lib/t_humanize
+TEST_LIBS = test-libutils/t_mode test-libutils/t_strtodur test-libutils/t_symbolize_mode test-libutils/t_truncation test-lib/t_sha1 test-lib/t_sha256 test-lib/t_sha512 test-libutils/t_humanize
selfcheck-libs: $(TEST_LIBS)
LDSTATIC="$(LDSTATIC)" ./check-libs.sh $(TEST_LIBS)
@@ -61,7 +61,7 @@ lint: $(MAN1SO)
.PHONY: clean
clean:
- $(RM) -fr $(EXE) ${EXE:=.c.gcov} ${EXE:=.gcda} ${EXE:=.gcno} cmd/expr.tab.c $(MAN1SO) $(TEST_CMDS) $(TEST_LIBS) ${LIBUTILS_O} build
+ $(RM) -fr $(EXE) ${EXE:=.c.gcov} ${EXE:=.gcda} ${EXE:=.gcno} cmd/expr.tab.c $(MAN1SO) $(TEST_CMDS) $(TEST_LIBS) lib/*.o libutils/*.o libutils/libutils.a build
install: all
mkdir -p ${DESTDIR}${BINDIR}/
@@ -73,7 +73,7 @@ install: all
coverage:
$(GCOV) -b $(EXE)
-C_SOURCES = cmd/*.c lib/*.h lib/*.c test-lib/*.c configure.d/*.c
+C_SOURCES = cmd/*.c lib/*.h lib/*.c libutils/*.c libutils/*.h test-libutils/*.c configure.d/*.c
format: $(C_SOURCES)
clang-format -style=file -assume-filename=.clang-format -i $(C_SOURCES)
@@ -83,14 +83,14 @@ build/sys_signame.c: lib/sys_signame.sh cmd/cat cmd/printf cmd/mkdir
lib/sys_signame.sh >|build/sys_signame.c
LIBUTILS_O = ${LIBUTILS_C:.c=.o}
-lib/utils.a: ${LIBUTILS_O} ${LIBUTILS_H} config.mk config.h Makefile
- ${AR} rc ${ARFLAGS} lib/utils.a ${LIBUTILS_O}
+libutils/libutils.a: ${LIBUTILS_O} ${LIBUTILS_H} config.mk config.h Makefile
+ ${AR} rc ${ARFLAGS} libutils/libutils.a ${LIBUTILS_O}
-build/cmd/date.1: cmd/date.1.in lib/datetime_parse.mdoc cmd/mkdir
+build/cmd/date.1: cmd/date.1.in libutils/datetime_parse.mdoc cmd/mkdir
mkdir -p build/cmd
$(M4) cmd/date.1.in > build/cmd/date.1
-build/cmd/touch.1: cmd/touch.1.in lib/datetime_parse.mdoc cmd/mkdir
+build/cmd/touch.1: cmd/touch.1.in libutils/datetime_parse.mdoc cmd/mkdir
mkdir -p build/cmd
$(M4) cmd/touch.1.in > build/cmd/touch.1
@@ -98,81 +98,82 @@ cmd/expr.tab.c: cmd/expr.y Makefile
$(YACC) -b cmd/expr cmd/expr.y
# Needs -D_POSIX_C_SOURCE=200809L due to OpenBSD yacc
-cmd/expr: cmd/expr.tab.c lib/utils.a
+cmd/expr: cmd/expr.tab.c libutils/libutils.a
$(RM) -f cmd/expr.tab.c.gcov cmd/expr.tab.o.gcda cmd/expr.tab.o.gcno
- $(CC) -std=c99 $(CFLAGS) -D_POSIX_C_SOURCE=200809L -o cmd/expr cmd/expr.tab.c lib/utils.a $(LDFLAGS) $(LDSTATIC)
+ $(CC) -std=c99 $(CFLAGS) -D_POSIX_C_SOURCE=200809L -o cmd/expr cmd/expr.tab.c libutils/libutils.a $(LDFLAGS) $(LDSTATIC)
# Needs -lm
-cmd/seq: cmd/seq.c Makefile lib/utils.a
+cmd/seq: cmd/seq.c Makefile libutils/libutils.a
$(RM) -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
- $(CC) -std=c99 $(CFLAGS) -o $@ cmd/seq.c lib/utils.a -lm $(LDFLAGS) $(LDSTATIC)
+ $(CC) -std=c99 $(CFLAGS) -o $@ cmd/seq.c libutils/libutils.a -lm $(LDFLAGS) $(LDSTATIC)
cmd/getconf_vars.h: cmd/getconf_vars.m4
$(M4) cmd/getconf_vars.m4 > cmd/getconf_vars.h
# cmd/getconf_vars.h needs to invalidate cmd/getconf but not be in command
-cmd/getconf: cmd/getconf.c cmd/getconf_vars.h lib/utils.a
+cmd/getconf: cmd/getconf.c cmd/getconf_vars.h libutils/libutils.a
$(RM) -f ${<:=.gcov} ${@:=.gcda} ${@:=.gcno}
- $(CC) -std=c99 $(CFLAGS) -o cmd/getconf cmd/getconf.c lib/utils.a $(LDFLAGS) $(LDSTATIC)
-
-cmd/base64: cmd/base64.c lib/utils.a
-cmd/basename: cmd/basename.c lib/utils.a
-cmd/chmod: cmd/chmod.c lib/utils.a
-cmd/chown: cmd/chown.c lib/utils.a
-cmd/cmp: cmd/cmp.c lib/utils.a
-cmd/cut: cmd/cut.c lib/utils.a
-cmd/date: cmd/date.c lib/utils.a
-cmd/df: cmd/df.c lib/utils.a
-cmd/env: cmd/env.c lib/utils.a
-cmd/head: cmd/head.c lib/utils.a
-cmd/id: cmd/id.c lib/utils.a
-cmd/install: cmd/install.c lib/utils.a
-cmd/join: cmd/join.c lib/utils.a
-cmd/ln: cmd/ln.c lib/utils.a
-cmd/mkdir: cmd/mkdir.c lib/utils.a
-cmd/mkfifo: cmd/mkfifo.c lib/utils.a
-cmd/mknod: cmd/mknod.c lib/utils.a
-cmd/mktemp: cmd/mktemp.c lib/utils.a
-cmd/mv: cmd/mv.c lib/utils.a
-cmd/nice: cmd/nice.c lib/utils.a
-cmd/nproc: cmd/nproc.c lib/utils.a
-cmd/od: cmd/od.c lib/utils.a
-cmd/paste: cmd/paste.c lib/utils.a
-cmd/pathchk: cmd/pathchk.c lib/utils.a
-cmd/pwd: cmd/pwd.c lib/utils.a
-cmd/realpath: cmd/realpath.c lib/utils.a
-cmd/renice: cmd/renice.c lib/utils.a
-cmd/rmdir: cmd/rmdir.c lib/utils.a
-cmd/sha1sum: cmd/sha1sum.c lib/utils.a
-cmd/sha256sum: cmd/sha256sum.c lib/utils.a
-cmd/sha512sum: cmd/sha512sum.c lib/utils.a
-cmd/shuf: cmd/shuf.c lib/utils.a
-cmd/sleep: cmd/sleep.c lib/utils.a
-cmd/split: cmd/split.c lib/utils.a
-cmd/strings: cmd/strings.c lib/utils.a
-cmd/sync: cmd/sync.c lib/utils.a
-cmd/tee: cmd/tee.c lib/utils.a
-cmd/time: cmd/time.c lib/utils.a
-cmd/timeout: cmd/timeout.c build/sys_signame.c lib/utils.a
-cmd/touch: cmd/touch.c lib/utils.a
-cmd/truncate: cmd/truncate.c lib/utils.a
-cmd/uname: cmd/uname.c lib/utils.a
-cmd/uniq: cmd/uniq.c lib/utils.a
-cmd/wc: cmd/wc.c lib/utils.a
-cmd/which: cmd/which.c lib/utils.a
-cmd/whoami: cmd/whoami.c lib/utils.a
-
-lib/mode.o: lib/mode.c lib/mode.h
+ $(CC) -std=c99 $(CFLAGS) -o cmd/getconf cmd/getconf.c libutils/libutils.a $(LDFLAGS) $(LDSTATIC)
+
+cmd/base64: cmd/base64.c libutils/libutils.a
+cmd/basename: cmd/basename.c libutils/libutils.a
+cmd/chmod: cmd/chmod.c libutils/libutils.a
+cmd/chown: cmd/chown.c libutils/libutils.a
+cmd/cmp: cmd/cmp.c libutils/libutils.a
+cmd/cut: cmd/cut.c libutils/libutils.a
+cmd/date: cmd/date.c libutils/libutils.a
+cmd/df: cmd/df.c libutils/libutils.a
+cmd/env: cmd/env.c libutils/libutils.a
+cmd/head: cmd/head.c libutils/libutils.a
+cmd/id: cmd/id.c libutils/libutils.a
+cmd/install: cmd/install.c libutils/libutils.a
+cmd/join: cmd/join.c libutils/libutils.a
+cmd/ln: cmd/ln.c libutils/libutils.a
+cmd/mkdir: cmd/mkdir.c libutils/libutils.a
+cmd/mkfifo: cmd/mkfifo.c libutils/libutils.a
+cmd/mknod: cmd/mknod.c libutils/libutils.a
+cmd/mktemp: cmd/mktemp.c libutils/libutils.a
+cmd/mv: cmd/mv.c libutils/libutils.a
+cmd/nice: cmd/nice.c libutils/libutils.a
+cmd/nproc: cmd/nproc.c libutils/libutils.a
+cmd/od: cmd/od.c libutils/libutils.a
+cmd/paste: cmd/paste.c libutils/libutils.a
+cmd/pathchk: cmd/pathchk.c libutils/libutils.a
+cmd/pwd: cmd/pwd.c libutils/libutils.a
+cmd/realpath: cmd/realpath.c libutils/libutils.a
+cmd/renice: cmd/renice.c libutils/libutils.a
+cmd/rmdir: cmd/rmdir.c libutils/libutils.a
+cmd/sha1sum: cmd/sha1sum.c libutils/libutils.a lib/bytes2hex.o lib/sha1.o
+cmd/sha256sum: cmd/sha256sum.c libutils/libutils.a lib/bytes2hex.o lib/sha256.o
+cmd/sha512sum: cmd/sha512sum.c libutils/libutils.a lib/bytes2hex.o lib/sha512.o
+cmd/shuf: cmd/shuf.c libutils/libutils.a
+cmd/sleep: cmd/sleep.c libutils/libutils.a
+cmd/split: cmd/split.c libutils/libutils.a
+cmd/strings: cmd/strings.c libutils/libutils.a
+cmd/sync: cmd/sync.c libutils/libutils.a
+cmd/tee: cmd/tee.c libutils/libutils.a
+cmd/time: cmd/time.c libutils/libutils.a
+cmd/timeout: cmd/timeout.c build/sys_signame.c libutils/libutils.a
+cmd/touch: cmd/touch.c libutils/libutils.a
+cmd/truncate: cmd/truncate.c libutils/libutils.a
+cmd/uname: cmd/uname.c libutils/libutils.a
+cmd/uniq: cmd/uniq.c libutils/libutils.a
+cmd/wc: cmd/wc.c libutils/libutils.a
+cmd/which: cmd/which.c libutils/libutils.a
+cmd/whoami: cmd/whoami.c libutils/libutils.a
+
+libutils/mode.o: libutils/mode.c libutils/mode.h
+
lib/sha1.o: lib/sha1.c lib/sha1.h
lib/sha256.o: lib/sha256.c lib/sha256.h
lib/sha512.o: lib/sha512.c lib/sha512.h
lib/bytes2hex.o: lib/bytes2hex.c lib/strconv.h
-test-lib/t_humanize: test-lib/t_humanize.c lib/humanize.o
-test-lib/t_mode: test-lib/t_mode.c lib/mode.o
test-lib/t_sha1: test-lib/t_sha1.c lib/sha1.c lib/bytes2hex.o
test-lib/t_sha256: test-lib/t_sha256.c lib/sha256.c lib/bytes2hex.o
test-lib/t_sha512: test-lib/t_sha512.c lib/sha512.c lib/bytes2hex.o
-test-lib/t_strtodur: test-lib/t_strtodur.c lib/strtodur.o
-test-lib/t_symbolize_mode: test-lib/t_symbolize_mode.c lib/symbolize_mode.o
-test-lib/t_truncation: test-lib/t_truncation.c lib/truncation.o
+test-libutils/t_humanize: test-libutils/t_humanize.c libutils/humanize.o
+test-libutils/t_mode: test-libutils/t_mode.c libutils/mode.o
+test-libutils/t_strtodur: test-libutils/t_strtodur.c libutils/strtodur.o
+test-libutils/t_symbolize_mode: test-libutils/t_symbolize_mode.c libutils/symbolize_mode.o
+test-libutils/t_truncation: test-libutils/t_truncation.c libutils/truncation.o
diff --git a/check-libs.sh b/check-libs.sh
@@ -20,7 +20,7 @@ then
wrap_test "$runner"
done
else
- for src in test-lib/*.c
+ for src in test-lib/*.c test-libutils/*.c
do
runner="${src%.c}"
wrap_test "$runner"
diff --git a/cmd/base64.c b/cmd/base64.c
@@ -4,7 +4,7 @@
// SPDX-License-Identifier: MPL-2.0 AND BSD-2-Clause
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <assert.h> /* assert */
#include <ctype.h> /* isspace */
diff --git a/cmd/basename.c b/cmd/basename.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <libgen.h> // basename
#include <stdbool.h>
diff --git a/cmd/cat.c b/cmd/cat.c
@@ -3,8 +3,8 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/fs.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/fs.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <fcntl.h> // open, O_RDONLY
diff --git a/cmd/chmod.c b/cmd/chmod.c
@@ -10,8 +10,8 @@
#endif
#include "../config.h" // HAS_*
-#include "../lib/getopt_nolong.h"
-#include "../lib/mode.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/mode.h"
#include <dirent.h> // fdopendir, readdir, closedir
#include <errno.h>
diff --git a/cmd/chown.c b/cmd/chown.c
@@ -10,9 +10,9 @@
#endif
#include "../config.h" // HAS_*
-#include "../lib/fs.h"
-#include "../lib/getopt_nolong.h"
-#include "../lib/user_group_parse.h"
+#include "../libutils/fs.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/user_group_parse.h"
#include <dirent.h> // fdopendir, readdir, closedir
#include <errno.h>
diff --git a/cmd/cmp.c b/cmd/cmp.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <assert.h>
#include <errno.h>
diff --git a/cmd/cut.c b/cmd/cut.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 202405L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include "../lib/reallocarray.h"
#include <assert.h>
diff --git a/cmd/date.1.in b/cmd/date.1.in
@@ -36,7 +36,7 @@ will print the datetime in a user-defined way.
Use
.Ar datetime
instead of current datetime.
-include(lib/datetime_parse.mdoc)
+include(libutils/datetime_parse.mdoc)
.It Fl f Ar now_format
Use
.Ar now_format
diff --git a/cmd/date.c b/cmd/date.c
@@ -6,8 +6,8 @@
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700 // strptime is in XSI
-#include "../lib/datetime_parse.h" /* datetime_parse */
-#include "../lib/getopt_nolong.h"
+#include "../libutils/datetime_parse.h" /* datetime_parse */
+#include "../libutils/getopt_nolong.h"
#include <assert.h>
#include <errno.h>
diff --git a/cmd/df.c b/cmd/df.c
@@ -5,8 +5,8 @@
#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE // mntent in glibc 2.19+
-#include "../lib/getopt_nolong.h"
-#include "../lib/humanize.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/humanize.h"
#include <ctype.h> // iscntrl, isspace
#include <errno.h> // errno
diff --git a/cmd/env.c b/cmd/env.c
@@ -6,7 +6,7 @@
#define _XOPEN_SOURCE 800 // wordexp
#include "../config.h" // HAS_*
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <assert.h> // assert
#include <errno.h> // errno
diff --git a/cmd/expr.y b/cmd/expr.y
@@ -18,8 +18,8 @@
#include <assert.h>
#include <sys/types.h>
#include <ctype.h>
-#include "../lib/err.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/err.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <inttypes.h>
#include <limits.h>
diff --git a/cmd/getconf.c b/cmd/getconf.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 202405L
#define _XOPEN_SOURCE 800
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <assert.h>
#include <errno.h>
diff --git a/cmd/head.c b/cmd/head.c
@@ -4,9 +4,9 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/fs.h" // auto_fd_copy
-#include "../lib/getopt_nolong.h"
-#include "../lib/truncation.h" // apply_size_suffix
+#include "../libutils/fs.h" // auto_fd_copy
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/truncation.h" // apply_size_suffix
#include <ctype.h> // isdigit
#include <errno.h>
diff --git a/cmd/id.c b/cmd/id.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _DEFAULT_SOURCE // getgrouplist (4.4BSD+)
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <grp.h> // getgrgid, getgroups, getgrouplist(glibc)
#include <pwd.h> // getpwuid
diff --git a/cmd/install.c b/cmd/install.c
@@ -7,11 +7,11 @@
#define _DEFAULT_SOURCE // FreeBSD
#include "../config.h" // HAS_*
-#include "../lib/fs.h"
-#include "../lib/getopt_nolong.h"
-#include "../lib/lib_mkdir.h"
-#include "../lib/mode.h"
-#include "../lib/user_group_parse.h"
+#include "../libutils/fs.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/lib_mkdir.h"
+#include "../libutils/mode.h"
+#include "../libutils/user_group_parse.h"
#include <assert.h>
#include <errno.h>
diff --git a/cmd/join.c b/cmd/join.c
@@ -33,11 +33,12 @@
* SUCH DAMAGE.
*/
+#define _POSIX_C_SOURCE 202405L
#define _BSD_SOURCE // fgetln, strsep
#include "../lib/reallocarray.h"
-#include "../lib/err.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/err.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <limits.h>
diff --git a/cmd/ln.c b/cmd/ln.c
@@ -5,7 +5,7 @@
#define _POSIX_C_SOURCE 200809L
#include "../lib/bitmasks.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
// NetBSD (9.3 and 10) hides symlink behind _XOPEN_SOURCE / _NETBSD_SOURCE
#ifdef __NetBSD__
diff --git a/cmd/mkdir.c b/cmd/mkdir.c
@@ -4,9 +4,9 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
-#include "../lib/lib_mkdir.h"
-#include "../lib/mode.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/lib_mkdir.h"
+#include "../libutils/mode.h"
#include <errno.h>
#include <stdbool.h>
diff --git a/cmd/mkfifo.c b/cmd/mkfifo.c
@@ -4,8 +4,8 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
-#include "../lib/mode.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/mode.h"
#include <errno.h>
#include <stdio.h> // fprintf
diff --git a/cmd/mknod.c b/cmd/mknod.c
@@ -5,8 +5,8 @@
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700 // mknod is in XSI
-#include "../lib/getopt_nolong.h"
-#include "../lib/mode.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/mode.h"
#include <errno.h>
#include <stdio.h> // fprintf
diff --git a/cmd/mktemp.c b/cmd/mktemp.c
@@ -5,7 +5,7 @@
// getentropy got in POSIX.1-2024 but defining _POSIX_C_SOURCE causes too much side-effects
#define _DEFAULT_SOURCE // getentropy
#include "../config.h" // HAS_*
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <limits.h> // PATH_MAX
diff --git a/cmd/mv.c b/cmd/mv.c
@@ -11,9 +11,9 @@
#define _NETBSD_SOURCE
#endif
-#include "../lib/consent.h"
-#include "../lib/fs.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/consent.h"
+#include "../libutils/fs.h"
+#include "../libutils/getopt_nolong.h"
#include <dirent.h> // fdopendir
#include <errno.h>
diff --git a/cmd/nice.c b/cmd/nice.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700 // nice() is in XSI
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <assert.h>
#include <errno.h>
diff --git a/cmd/nproc.c b/cmd/nproc.c
@@ -9,7 +9,7 @@
// Sadly {Free,Net}BSD hides _SC_NPROCESSORS_{CONF,ONLN} if _POSIX_C_SOURCE is defined *sigh*
// #define _POSIX_C_SOURCE 202405L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <stdio.h> // printf
diff --git a/cmd/paste.c b/cmd/paste.c
@@ -34,8 +34,8 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/err.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/err.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <limits.h>
diff --git a/cmd/pathchk.c b/cmd/pathchk.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <limits.h> // PATH_MAX
diff --git a/cmd/pwd.c b/cmd/pwd.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <limits.h> // PATH_MAX
#include <stdbool.h>
diff --git a/cmd/realpath.c b/cmd/realpath.c
@@ -5,8 +5,8 @@
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700 // realpath is in XSI
-#include "../lib/fs.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/fs.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <limits.h> // PATH_MAX
diff --git a/cmd/renice.c b/cmd/renice.c
@@ -5,7 +5,7 @@
#define _POSIX_C_SOURCE 200809L
#define _XOPEN_SOURCE 700 // getpriority, setpriority
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <stdio.h> // fprintf
diff --git a/cmd/rm.c b/cmd/rm.c
@@ -9,8 +9,8 @@
#define _NETBSD_SOURCE
#endif
-#include "../lib/consent.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/consent.h"
+#include "../libutils/getopt_nolong.h"
#include <ctype.h> // isprint
#include <dirent.h> // fdopendir, readdir, closedir
diff --git a/cmd/rmdir.c b/cmd/rmdir.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 200809L
#include "../config.h" // HAS_*
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <stdbool.h>
diff --git a/cmd/seq.c b/cmd/seq.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <ctype.h> // isdigit
#include <errno.h> // errno
diff --git a/cmd/sha1sum.c b/cmd/sha1sum.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include "../lib/sha1.h" // sha1_*
#include "../lib/strconv.h" // bytes2hex
diff --git a/cmd/sha256sum.c b/cmd/sha256sum.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include "../lib/sha256.h" // sha256_*
#include "../lib/strconv.h" // bytes2hex
diff --git a/cmd/sha512sum.c b/cmd/sha512sum.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include "../lib/sha512.h" // sha512_*
#include "../lib/strconv.h" // bytes2hex
diff --git a/cmd/shuf.c b/cmd/shuf.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <stdbool.h>
diff --git a/cmd/sleep.c b/cmd/sleep.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/strtodur.h"
+#include "../libutils/strtodur.h"
#include <errno.h> // errno
#include <stdio.h> // fprintf, perror
diff --git a/cmd/split.c b/cmd/split.c
@@ -4,9 +4,9 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/fs.h" // auto_file_copy
-#include "../lib/getopt_nolong.h"
-#include "../lib/truncation.h" // apply_size_suffix
+#include "../libutils/fs.h" // auto_file_copy
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/truncation.h" // apply_size_suffix
#include <errno.h>
#include <fcntl.h> // open
diff --git a/cmd/strings.c b/cmd/strings.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <ctype.h> /* isprint() */
#include <errno.h> /* errno */
diff --git a/cmd/sync.c b/cmd/sync.c
@@ -7,7 +7,7 @@
#define _XOPEN_SOURCE 700 // sync
#include "../config.h" // HAS_*
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <fcntl.h> // open, O_*
diff --git a/cmd/tee.c b/cmd/tee.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <assert.h> /* assert() */
#include <errno.h> /* errno */
diff --git a/cmd/time.c b/cmd/time.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h> // errno
#include <stdio.h> // perror, fprintf
diff --git a/cmd/timeout.c b/cmd/timeout.c
@@ -4,8 +4,8 @@
#define _POSIX_C_SOURCE 200809L
#define _DEFAULT_SOURCE // For NSIG in sys_signame.h, thanks glibc
-#include "../lib/getopt_nolong.h"
-#include "../lib/strtodur.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/strtodur.h"
#include "../lib/sys_signame.h"
#include <ctype.h>
diff --git a/cmd/touch.1.in b/cmd/touch.1.in
@@ -36,7 +36,7 @@ in 2011,
in 2012.
.\" Let's not publicly document too much that illumos still supports it
.It Fl d Ar isotime
-include(lib/datetime_parse.mdoc)
+include(libutils/datetime_parse.mdoc)
.It Fl h
Do not follow symlinks.
.It Fl m
diff --git a/cmd/touch.c b/cmd/touch.c
@@ -7,8 +7,8 @@
#define _POSIX_C_SOURCE 200809L // O_NOFOLLOW, st_atim/st_mtim
#include "../lib/bitmasks.h" /* FIELD_* */
-#include "../lib/datetime_parse.h" /* datetime_parse */
-#include "../lib/getopt_nolong.h"
+#include "../libutils/datetime_parse.h" /* datetime_parse */
+#include "../libutils/getopt_nolong.h"
#include <assert.h>
#include <errno.h> /* errno */
diff --git a/cmd/tr.c b/cmd/tr.c
@@ -32,8 +32,8 @@
#define _DEFAULT_SOURCE
-#include "../lib/err.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/err.h"
+#include "../libutils/getopt_nolong.h"
#include "../lib/tr_str.h"
#include <stdio.h>
diff --git a/cmd/truncate.c b/cmd/truncate.c
@@ -5,8 +5,8 @@
#define _POSIX_C_SOURCE 200809L
#include "../lib/bitmasks.h" // FIELD_CLR
-#include "../lib/getopt_nolong.h"
-#include "../lib/truncation.h" // parse_size
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/truncation.h" // parse_size
#include <errno.h>
#include <fcntl.h> // open
diff --git a/cmd/uname.c b/cmd/uname.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 200809L
#include "../lib/bitmasks.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <errno.h>
#include <stdio.h> // printf
diff --git a/cmd/uniq.c b/cmd/uniq.c
@@ -3,7 +3,7 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <ctype.h> // isblank
#include <errno.h>
diff --git a/cmd/wc.c b/cmd/wc.c
@@ -5,7 +5,7 @@
#define _POSIX_C_SOURCE 200809L
#include "../config.h" // HAS_*
#include "../lib/bitmasks.h"
-#include "../lib/getopt_nolong.h"
+#include "../libutils/getopt_nolong.h"
#include <ctype.h> // isspace
#include <errno.h>
diff --git a/cmd/which.c b/cmd/which.c
@@ -3,8 +3,8 @@
// SPDX-License-Identifier: MPL-2.0
#define _POSIX_C_SOURCE 200809L
-#include "../lib/getopt_nolong.h"
-#include "../lib/strchrnul.h"
+#include "../libutils/getopt_nolong.h"
+#include "../libutils/strchrnul.h"
#include <errno.h>
#include <limits.h> // PATH_MAX
diff --git a/cmd/whoami.c b/cmd/whoami.c
@@ -4,7 +4,7 @@
#define _POSIX_C_SOURCE 200809L
-#include "../lib/err.h"
+#include "../libutils/err.h"
#include <errno.h>
#include <pwd.h> // getpwuid
diff --git a/common.mk b/common.mk
@@ -2,11 +2,11 @@
# SPDX-License-Identifier: MPL-2.0
# bootstrap.mk targets should be kept simple
-lib/fs.o: lib/fs.c lib/fs.h config.h
-lib/err.o: lib/err.c lib/err.h
-lib/consent.o: lib/consent.c lib/consent.h
+libutils/fs.o: libutils/fs.c libutils/fs.h config.h
+libutils/err.o: libutils/err.c libutils/err.h
+libutils/consent.o: libutils/consent.c libutils/consent.h
lib/tr_str.o: lib/tr_str.c lib/tr_str.h
-cmd/cat: cmd/cat.c lib/fs.o lib/getopt_nolong.o
+cmd/cat: cmd/cat.c libutils/fs.o libutils/getopt_nolong.o
cmd/printf: cmd/printf.c
-cmd/rm: cmd/rm.c lib/consent.o lib/getopt_nolong.o
-cmd/tr: cmd/tr.c lib/tr_str.o lib/err.o lib/getopt_nolong.o
+cmd/rm: cmd/rm.c libutils/consent.o libutils/getopt_nolong.o
+cmd/tr: cmd/tr.c lib/tr_str.o libutils/err.o libutils/getopt_nolong.o
diff --git a/configure b/configure
@@ -73,11 +73,11 @@ gen_targets() {
echo
printf 'LIBUTILS_C = '
- echo lib/*.c
+ echo libutils/*.c
echo
printf 'LIBUTILS_H = '
- echo lib/*.h
+ echo libutils/*.h
echo
}
diff --git a/lib/humanize.c b/lib/humanize.c
@@ -1,41 +0,0 @@
-// 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/humanize.h"
-
-#include <math.h> // fabs
-#include <stdio.h> // snprintf
-
-struct si_scale
-dtosi(double num, bool iec)
-{
-#define PFX 11
- const char *si_prefixes[PFX] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"};
- const char *iec_prefixes[PFX] = {
- "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"};
-
- int div = iec ? 1024 : 1000;
- const char **prefixes = iec ? iec_prefixes : si_prefixes;
- bool neg = num < 0;
-
- struct si_scale ret = {
- .number = fabs(num),
- .prefix = "",
- .exponent = 0,
- };
-
- while(ret.number > div && ret.exponent < (PFX - 1))
- {
- ret.number /= div;
- ret.exponent += 1;
- }
-
- ret.prefix = prefixes[ret.exponent];
-
- if(neg) ret.number = -ret.number;
-
- return ret;
-}
diff --git a/lib/mode.c b/lib/mode.c
@@ -1,312 +0,0 @@
-// Collection of Unix tools, comparable to coreutils
-// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
-// SPDX-License-Identifier: MPL-2.0
-
-#define _POSIX_C_SOURCE 200809L
-#define _XOPEN_SOURCE 700 // S_ISVTX
-#include "mode.h"
-
-#include "bitmasks.h"
-
-#include <errno.h>
-#include <stdbool.h>
-#include <stdint.h> // int8_t
-#include <stdio.h> // strtol
-#include <stdlib.h> // abort
-#include <string.h> // strlen
-#include <sys/stat.h> // umask
-
-enum perm_who_e
-{
- WHO_RST = 0, // 0b000
- WHO_USER = 1, // 0b001
- WHO_GROUP = 2, // 0b010
- WHO_OTHER = 4, // 0b100
- WHO_ALL = 7, // 0b111
-};
-
-enum operation_e
-{
- OP_RST,
- OP_ADD,
- OP_DEL,
- OP_SET,
-};
-
-struct modes
-{
- int8_t set;
- int8_t user;
- int8_t group;
- int8_t other;
-};
-
-struct modes_meta
-{
- enum perm_who_e dest;
- enum operation_e op;
- struct modes new_modes;
-};
-
-static void
-apply(struct modes_meta *meta, int8_t mode)
-{
- // get old, then reset to old. Yay to POSIX nonsense
- mode_t mask = umask(0);
- umask(mask);
-
- // negate to get a normal bitmask
- mask ^= 0777;
-
- switch(meta->op)
- {
- case OP_ADD:
- if(meta->dest == WHO_RST)
- {
- FIELD_SET(meta->new_modes.user, mode & ((mask >> 6) & 07));
- FIELD_SET(meta->new_modes.group, mode & ((mask >> 3) & 07));
- FIELD_SET(meta->new_modes.other, mode & ((mask >> 0) & 07));
- }
- if(FIELD_MATCH(meta->dest, WHO_USER)) FIELD_SET(meta->new_modes.user, mode);
- if(FIELD_MATCH(meta->dest, WHO_GROUP)) FIELD_SET(meta->new_modes.group, mode);
- if(FIELD_MATCH(meta->dest, WHO_OTHER)) FIELD_SET(meta->new_modes.other, mode);
- break;
- case OP_DEL:
- if(meta->dest == WHO_RST)
- {
- FIELD_CLR(meta->new_modes.user, mode & ((mask >> 6) & 07));
- FIELD_CLR(meta->new_modes.group, mode & ((mask >> 3) & 07));
- FIELD_CLR(meta->new_modes.other, mode & ((mask >> 0) & 07));
- }
- if(FIELD_MATCH(meta->dest, WHO_USER)) FIELD_CLR(meta->new_modes.user, mode);
- if(FIELD_MATCH(meta->dest, WHO_GROUP)) FIELD_CLR(meta->new_modes.group, mode);
- if(FIELD_MATCH(meta->dest, WHO_OTHER)) FIELD_CLR(meta->new_modes.other, mode);
- break;
- case OP_SET:
- if(meta->dest == WHO_RST)
- {
- meta->new_modes.user = mode & ((mask >> 6) & 07);
- meta->new_modes.group = mode & ((mask >> 3) & 07);
- meta->new_modes.other = mode & ((mask >> 0) & 07);
- }
- if(FIELD_MATCH(meta->dest, WHO_USER)) meta->new_modes.user = mode;
- if(FIELD_MATCH(meta->dest, WHO_GROUP)) meta->new_modes.group = mode;
- if(FIELD_MATCH(meta->dest, WHO_OTHER)) meta->new_modes.other = mode;
-
- // So a=rw is a=r+w rather than a=r,a=w
- meta->op = OP_ADD;
- break;
- default:
- abort();
- }
-}
-
-mode_t
-new_mode(const char *mode, mode_t old, const char **errstr)
-{
- // NULL? Then nothing to change :)
- if(mode == NULL) return old;
-
- size_t mode_len = strlen(mode);
-
- if(mode_len == 0) return old;
-
- bool symbolic = false;
- for(size_t i = 0; i < mode_len; i++)
- {
- if(mode[i] < '0' || mode[i] > '7')
- {
- if(mode[i] == '8' || mode[i] == '9')
- {
- *errstr = "contains digit outside of [0-7]";
- return old;
- }
- symbolic = true;
- break;
- }
- }
-
- if(!symbolic)
- {
- char *endptr = NULL;
-
- long new = strtol(mode, &endptr, 8);
-
- if(errno != 0)
- {
- *errstr = strerror(errno);
- errno = 0;
- return old;
- }
-
- if(new < 0)
- {
- *errstr = "can't be negative";
- return old;
- }
-
- if(new > 07777)
- {
- *errstr = "can't be higher than 0o7777";
- return old;
- }
-
- return (old & 0770000) | new;
- }
-
- // ((^|,)[ugoa]*([+-=]|[ugo]|[rwxXst]+)+)+
- struct modes_meta meta = {.dest = WHO_RST,
- .op = OP_RST,
- .new_modes = {
- .set = (old & 07000) >> 9,
- .user = (old & 00700) >> 6,
- .group = (old & 00070) >> 3,
- .other = (old & 00007) >> 0,
- }};
-
- for(size_t i = 0; i < mode_len; i++)
- {
- char m = mode[i];
-
- switch(m)
- {
- // separator
- case ',':
- meta.dest = WHO_RST;
- meta.op = OP_RST;
- break;
-
- // who
- case 'u':
- if(meta.op == OP_RST)
- FIELD_SET(meta.dest, WHO_USER);
- else
- apply(&meta, meta.new_modes.user);
- break;
- case 'g':
- if(meta.op == OP_RST)
- FIELD_SET(meta.dest, WHO_GROUP);
- else
- apply(&meta, meta.new_modes.group);
- break;
- case 'o':
- if(meta.op == OP_RST)
- FIELD_SET(meta.dest, WHO_OTHER);
- else
- apply(&meta, meta.new_modes.other);
- break;
- case 'a':
- if(meta.op == OP_RST) FIELD_SET(meta.dest, WHO_ALL);
- // ignore =a, POSIX doesn't allows "a" in permcopy (would be a noop anyway)
- break;
-
- // op
- // Implementation-note: When another operator was set, override it
- case '+':
- meta.op = OP_ADD;
- break;
- case '-':
- meta.op = OP_DEL;
- break;
- case '=':
- meta.op = OP_SET;
- apply(&meta, 00);
- break;
-
- // perm
- case 'r':
- if(meta.op == OP_RST)
- {
- *errstr = "perm 'r' given without operator";
- return 0;
- }
- apply(&meta, 04);
- break;
- case 'w':
- if(meta.op == OP_RST)
- {
- *errstr = "perm 'w' given without operator";
- return 0;
- }
- apply(&meta, 02);
- break;
- case 'x':
- if(meta.op == OP_RST)
- {
- *errstr = "perm 'x' given without operator";
- return 0;
- }
- apply(&meta, 01);
- break;
- case 'X':
- if(meta.op == OP_RST)
- {
- *errstr = "perm 'X' given without operator";
- return 0;
- }
- if(S_ISDIR(old)) apply(&meta, 01);
- break;
- case 's':
- {
- if(meta.op == OP_RST)
- {
- *errstr = "perm 's' given without operator";
- return 0;
- }
- mode_t set_id = S_ISUID >> 9;
- if(FIELD_MATCH(meta.dest, WHO_GROUP)) set_id = S_ISGID >> 9;
-
- switch(meta.op)
- {
- case OP_ADD:
- FIELD_SET(meta.new_modes.set, set_id);
- break;
- case OP_DEL:
- FIELD_CLR(meta.new_modes.set, set_id);
- break;
- case OP_SET:
- meta.new_modes.set = set_id;
- break;
- default:
- abort();
- }
- break;
- }
- case 't':
- // Implementation defined behavior outside of directories and !(WHO_ALL|WHO_RST)
- switch(meta.op)
- {
- case OP_RST:
- *errstr = "perm 't' given without operator";
- return 0;
- case OP_ADD:
- FIELD_SET(meta.new_modes.set, S_ISVTX >> 9);
- break;
- case OP_DEL:
- FIELD_CLR(meta.new_modes.set, S_ISVTX >> 9);
- break;
- case OP_SET:
- meta.new_modes.set = S_ISVTX >> 9;
- break;
- default:
- abort();
- }
- break;
-
- default:
- // Would need to either be non-thread-safe with a shared read-write buffer
- // or only allocate in this situation, meaning a memory leak.
- *errstr = "syntax error, got invalid character";
-
- return 0;
- }
- }
-
- // clang-format off
- return (old & 0770000)
- | ((meta.new_modes.set & 07) << 9)
- | ((meta.new_modes.user & 07) << 6)
- | ((meta.new_modes.group & 07) << 3)
- | ((meta.new_modes.other & 07) << 0);
- // clang-format on
-}
diff --git a/lib/symbolize_mode.c b/lib/symbolize_mode.c
@@ -1,65 +0,0 @@
-// Collection of Unix tools, comparable to coreutils
-// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
-// SPDX-License-Identifier: MPL-2.0
-
-#define _POSIX_C_SOURCE 200809L
-#define _XOPEN_SOURCE 700 // S_ISVTX, S_IFMT, S_IFDIR, …
-#include "bitmasks.h"
-#include "mode.h"
-
-#include <sys/stat.h>
-
-void
-symbolize_mode(mode_t mode, char str[11])
-{
- switch(mode & S_IFMT)
- {
- case S_IFDIR:
- str[0] = 'd';
- break;
- case S_IFCHR:
- str[0] = 'c';
- break;
- case S_IFBLK:
- str[0] = 'b';
- break;
- case S_IFREG:
- str[0] = '-';
- break;
- case S_IFIFO:
- str[0] = 'f';
- break;
- case S_IFLNK:
- str[0] = 'l';
- break;
- case S_IFSOCK:
- str[0] = 's';
- break;
- default:
- str[0] = '?';
- break;
- }
-
- str[1] = FIELD_MATCH(mode, S_IRUSR) ? 'r' : '-';
- str[2] = FIELD_MATCH(mode, S_IWUSR) ? 'w' : '-';
- if(FIELD_MATCH(mode, S_ISUID))
- str[3] = FIELD_MATCH(mode, S_IXUSR) ? 's' : 'S';
- else
- str[3] = FIELD_MATCH(mode, S_IXUSR) ? 'x' : '-';
-
- str[4] = FIELD_MATCH(mode, S_IRGRP) ? 'r' : '-';
- str[5] = FIELD_MATCH(mode, S_IWGRP) ? 'w' : '-';
- if(FIELD_MATCH(mode, S_ISGID))
- str[6] = FIELD_MATCH(mode, S_IXGRP) ? 's' : 'S';
- else
- str[6] = FIELD_MATCH(mode, S_IXGRP) ? 'x' : '-';
-
- str[7] = FIELD_MATCH(mode, S_IROTH) ? 'r' : '-';
- str[8] = FIELD_MATCH(mode, S_IWOTH) ? 'w' : '-';
- if(FIELD_MATCH(mode, S_ISVTX))
- str[9] = FIELD_MATCH(mode, S_IXOTH) ? 't' : 'T';
- else
- str[9] = FIELD_MATCH(mode, S_IXOTH) ? 'x' : '-';
-
- str[10] = '\0';
-}
diff --git a/lib/tr_str.c b/lib/tr_str.c
@@ -39,7 +39,7 @@
#include "./tr_str.h"
// clang-format on
-#include "./err.h"
+#include "../libutils/err.h"
#include <assert.h>
#include <ctype.h>
diff --git a/lib/consent.c b/libutils/consent.c
diff --git a/lib/consent.h b/libutils/consent.h
diff --git a/lib/datetime_parse.c b/libutils/datetime_parse.c
diff --git a/lib/datetime_parse.h b/libutils/datetime_parse.h
diff --git a/lib/datetime_parse.mdoc b/libutils/datetime_parse.mdoc
diff --git a/lib/err.c b/libutils/err.c
diff --git a/lib/err.h b/libutils/err.h
diff --git a/lib/fs.c b/libutils/fs.c
diff --git a/lib/fs.h b/libutils/fs.h
diff --git a/lib/getopt_nolong.c b/libutils/getopt_nolong.c
diff --git a/lib/getopt_nolong.h b/libutils/getopt_nolong.h
diff --git a/libutils/humanize.c b/libutils/humanize.c
@@ -0,0 +1,41 @@
+// 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 "../libutils/humanize.h"
+
+#include <math.h> // fabs
+#include <stdio.h> // snprintf
+
+struct si_scale
+dtosi(double num, bool iec)
+{
+#define PFX 11
+ const char *si_prefixes[PFX] = {"", "k", "M", "G", "T", "P", "E", "Z", "Y", "R", "Q"};
+ const char *iec_prefixes[PFX] = {
+ "B", "KiB", "MiB", "GiB", "TiB", "PiB", "EiB", "ZiB", "YiB", "RiB", "QiB"};
+
+ int div = iec ? 1024 : 1000;
+ const char **prefixes = iec ? iec_prefixes : si_prefixes;
+ bool neg = num < 0;
+
+ struct si_scale ret = {
+ .number = fabs(num),
+ .prefix = "",
+ .exponent = 0,
+ };
+
+ while(ret.number > div && ret.exponent < (PFX - 1))
+ {
+ ret.number /= div;
+ ret.exponent += 1;
+ }
+
+ ret.prefix = prefixes[ret.exponent];
+
+ if(neg) ret.number = -ret.number;
+
+ return ret;
+}
diff --git a/lib/humanize.h b/libutils/humanize.h
diff --git a/lib/lib_mkdir.c b/libutils/lib_mkdir.c
diff --git a/lib/lib_mkdir.h b/libutils/lib_mkdir.h
diff --git a/libutils/mode.c b/libutils/mode.c
@@ -0,0 +1,312 @@
+// Collection of Unix tools, comparable to coreutils
+// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 700 // S_ISVTX
+#include "mode.h"
+
+#include "../lib/bitmasks.h"
+
+#include <errno.h>
+#include <stdbool.h>
+#include <stdint.h> // int8_t
+#include <stdio.h> // strtol
+#include <stdlib.h> // abort
+#include <string.h> // strlen
+#include <sys/stat.h> // umask
+
+enum perm_who_e
+{
+ WHO_RST = 0, // 0b000
+ WHO_USER = 1, // 0b001
+ WHO_GROUP = 2, // 0b010
+ WHO_OTHER = 4, // 0b100
+ WHO_ALL = 7, // 0b111
+};
+
+enum operation_e
+{
+ OP_RST,
+ OP_ADD,
+ OP_DEL,
+ OP_SET,
+};
+
+struct modes
+{
+ int8_t set;
+ int8_t user;
+ int8_t group;
+ int8_t other;
+};
+
+struct modes_meta
+{
+ enum perm_who_e dest;
+ enum operation_e op;
+ struct modes new_modes;
+};
+
+static void
+apply(struct modes_meta *meta, int8_t mode)
+{
+ // get old, then reset to old. Yay to POSIX nonsense
+ mode_t mask = umask(0);
+ umask(mask);
+
+ // negate to get a normal bitmask
+ mask ^= 0777;
+
+ switch(meta->op)
+ {
+ case OP_ADD:
+ if(meta->dest == WHO_RST)
+ {
+ FIELD_SET(meta->new_modes.user, mode & ((mask >> 6) & 07));
+ FIELD_SET(meta->new_modes.group, mode & ((mask >> 3) & 07));
+ FIELD_SET(meta->new_modes.other, mode & ((mask >> 0) & 07));
+ }
+ if(FIELD_MATCH(meta->dest, WHO_USER)) FIELD_SET(meta->new_modes.user, mode);
+ if(FIELD_MATCH(meta->dest, WHO_GROUP)) FIELD_SET(meta->new_modes.group, mode);
+ if(FIELD_MATCH(meta->dest, WHO_OTHER)) FIELD_SET(meta->new_modes.other, mode);
+ break;
+ case OP_DEL:
+ if(meta->dest == WHO_RST)
+ {
+ FIELD_CLR(meta->new_modes.user, mode & ((mask >> 6) & 07));
+ FIELD_CLR(meta->new_modes.group, mode & ((mask >> 3) & 07));
+ FIELD_CLR(meta->new_modes.other, mode & ((mask >> 0) & 07));
+ }
+ if(FIELD_MATCH(meta->dest, WHO_USER)) FIELD_CLR(meta->new_modes.user, mode);
+ if(FIELD_MATCH(meta->dest, WHO_GROUP)) FIELD_CLR(meta->new_modes.group, mode);
+ if(FIELD_MATCH(meta->dest, WHO_OTHER)) FIELD_CLR(meta->new_modes.other, mode);
+ break;
+ case OP_SET:
+ if(meta->dest == WHO_RST)
+ {
+ meta->new_modes.user = mode & ((mask >> 6) & 07);
+ meta->new_modes.group = mode & ((mask >> 3) & 07);
+ meta->new_modes.other = mode & ((mask >> 0) & 07);
+ }
+ if(FIELD_MATCH(meta->dest, WHO_USER)) meta->new_modes.user = mode;
+ if(FIELD_MATCH(meta->dest, WHO_GROUP)) meta->new_modes.group = mode;
+ if(FIELD_MATCH(meta->dest, WHO_OTHER)) meta->new_modes.other = mode;
+
+ // So a=rw is a=r+w rather than a=r,a=w
+ meta->op = OP_ADD;
+ break;
+ default:
+ abort();
+ }
+}
+
+mode_t
+new_mode(const char *mode, mode_t old, const char **errstr)
+{
+ // NULL? Then nothing to change :)
+ if(mode == NULL) return old;
+
+ size_t mode_len = strlen(mode);
+
+ if(mode_len == 0) return old;
+
+ bool symbolic = false;
+ for(size_t i = 0; i < mode_len; i++)
+ {
+ if(mode[i] < '0' || mode[i] > '7')
+ {
+ if(mode[i] == '8' || mode[i] == '9')
+ {
+ *errstr = "contains digit outside of [0-7]";
+ return old;
+ }
+ symbolic = true;
+ break;
+ }
+ }
+
+ if(!symbolic)
+ {
+ char *endptr = NULL;
+
+ long new = strtol(mode, &endptr, 8);
+
+ if(errno != 0)
+ {
+ *errstr = strerror(errno);
+ errno = 0;
+ return old;
+ }
+
+ if(new < 0)
+ {
+ *errstr = "can't be negative";
+ return old;
+ }
+
+ if(new > 07777)
+ {
+ *errstr = "can't be higher than 0o7777";
+ return old;
+ }
+
+ return (old & 0770000) | new;
+ }
+
+ // ((^|,)[ugoa]*([+-=]|[ugo]|[rwxXst]+)+)+
+ struct modes_meta meta = {.dest = WHO_RST,
+ .op = OP_RST,
+ .new_modes = {
+ .set = (old & 07000) >> 9,
+ .user = (old & 00700) >> 6,
+ .group = (old & 00070) >> 3,
+ .other = (old & 00007) >> 0,
+ }};
+
+ for(size_t i = 0; i < mode_len; i++)
+ {
+ char m = mode[i];
+
+ switch(m)
+ {
+ // separator
+ case ',':
+ meta.dest = WHO_RST;
+ meta.op = OP_RST;
+ break;
+
+ // who
+ case 'u':
+ if(meta.op == OP_RST)
+ FIELD_SET(meta.dest, WHO_USER);
+ else
+ apply(&meta, meta.new_modes.user);
+ break;
+ case 'g':
+ if(meta.op == OP_RST)
+ FIELD_SET(meta.dest, WHO_GROUP);
+ else
+ apply(&meta, meta.new_modes.group);
+ break;
+ case 'o':
+ if(meta.op == OP_RST)
+ FIELD_SET(meta.dest, WHO_OTHER);
+ else
+ apply(&meta, meta.new_modes.other);
+ break;
+ case 'a':
+ if(meta.op == OP_RST) FIELD_SET(meta.dest, WHO_ALL);
+ // ignore =a, POSIX doesn't allows "a" in permcopy (would be a noop anyway)
+ break;
+
+ // op
+ // Implementation-note: When another operator was set, override it
+ case '+':
+ meta.op = OP_ADD;
+ break;
+ case '-':
+ meta.op = OP_DEL;
+ break;
+ case '=':
+ meta.op = OP_SET;
+ apply(&meta, 00);
+ break;
+
+ // perm
+ case 'r':
+ if(meta.op == OP_RST)
+ {
+ *errstr = "perm 'r' given without operator";
+ return 0;
+ }
+ apply(&meta, 04);
+ break;
+ case 'w':
+ if(meta.op == OP_RST)
+ {
+ *errstr = "perm 'w' given without operator";
+ return 0;
+ }
+ apply(&meta, 02);
+ break;
+ case 'x':
+ if(meta.op == OP_RST)
+ {
+ *errstr = "perm 'x' given without operator";
+ return 0;
+ }
+ apply(&meta, 01);
+ break;
+ case 'X':
+ if(meta.op == OP_RST)
+ {
+ *errstr = "perm 'X' given without operator";
+ return 0;
+ }
+ if(S_ISDIR(old)) apply(&meta, 01);
+ break;
+ case 's':
+ {
+ if(meta.op == OP_RST)
+ {
+ *errstr = "perm 's' given without operator";
+ return 0;
+ }
+ mode_t set_id = S_ISUID >> 9;
+ if(FIELD_MATCH(meta.dest, WHO_GROUP)) set_id = S_ISGID >> 9;
+
+ switch(meta.op)
+ {
+ case OP_ADD:
+ FIELD_SET(meta.new_modes.set, set_id);
+ break;
+ case OP_DEL:
+ FIELD_CLR(meta.new_modes.set, set_id);
+ break;
+ case OP_SET:
+ meta.new_modes.set = set_id;
+ break;
+ default:
+ abort();
+ }
+ break;
+ }
+ case 't':
+ // Implementation defined behavior outside of directories and !(WHO_ALL|WHO_RST)
+ switch(meta.op)
+ {
+ case OP_RST:
+ *errstr = "perm 't' given without operator";
+ return 0;
+ case OP_ADD:
+ FIELD_SET(meta.new_modes.set, S_ISVTX >> 9);
+ break;
+ case OP_DEL:
+ FIELD_CLR(meta.new_modes.set, S_ISVTX >> 9);
+ break;
+ case OP_SET:
+ meta.new_modes.set = S_ISVTX >> 9;
+ break;
+ default:
+ abort();
+ }
+ break;
+
+ default:
+ // Would need to either be non-thread-safe with a shared read-write buffer
+ // or only allocate in this situation, meaning a memory leak.
+ *errstr = "syntax error, got invalid character";
+
+ return 0;
+ }
+ }
+
+ // clang-format off
+ return (old & 0770000)
+ | ((meta.new_modes.set & 07) << 9)
+ | ((meta.new_modes.user & 07) << 6)
+ | ((meta.new_modes.group & 07) << 3)
+ | ((meta.new_modes.other & 07) << 0);
+ // clang-format on
+}
diff --git a/lib/mode.h b/libutils/mode.h
diff --git a/lib/offline_realpath.c b/libutils/offline_realpath.c
diff --git a/lib/strchrnul.c b/libutils/strchrnul.c
diff --git a/lib/strchrnul.h b/libutils/strchrnul.h
diff --git a/lib/strtodur.c b/libutils/strtodur.c
diff --git a/lib/strtodur.h b/libutils/strtodur.h
diff --git a/libutils/symbolize_mode.c b/libutils/symbolize_mode.c
@@ -0,0 +1,65 @@
+// Collection of Unix tools, comparable to coreutils
+// SPDX-FileCopyrightText: 2017 Haelwenn (lanodan) Monnier <contact+utils@hacktivis.me>
+// SPDX-License-Identifier: MPL-2.0
+
+#define _POSIX_C_SOURCE 200809L
+#define _XOPEN_SOURCE 700 // S_ISVTX, S_IFMT, S_IFDIR, …
+#include "../lib/bitmasks.h"
+#include "mode.h"
+
+#include <sys/stat.h>
+
+void
+symbolize_mode(mode_t mode, char str[11])
+{
+ switch(mode & S_IFMT)
+ {
+ case S_IFDIR:
+ str[0] = 'd';
+ break;
+ case S_IFCHR:
+ str[0] = 'c';
+ break;
+ case S_IFBLK:
+ str[0] = 'b';
+ break;
+ case S_IFREG:
+ str[0] = '-';
+ break;
+ case S_IFIFO:
+ str[0] = 'f';
+ break;
+ case S_IFLNK:
+ str[0] = 'l';
+ break;
+ case S_IFSOCK:
+ str[0] = 's';
+ break;
+ default:
+ str[0] = '?';
+ break;
+ }
+
+ str[1] = FIELD_MATCH(mode, S_IRUSR) ? 'r' : '-';
+ str[2] = FIELD_MATCH(mode, S_IWUSR) ? 'w' : '-';
+ if(FIELD_MATCH(mode, S_ISUID))
+ str[3] = FIELD_MATCH(mode, S_IXUSR) ? 's' : 'S';
+ else
+ str[3] = FIELD_MATCH(mode, S_IXUSR) ? 'x' : '-';
+
+ str[4] = FIELD_MATCH(mode, S_IRGRP) ? 'r' : '-';
+ str[5] = FIELD_MATCH(mode, S_IWGRP) ? 'w' : '-';
+ if(FIELD_MATCH(mode, S_ISGID))
+ str[6] = FIELD_MATCH(mode, S_IXGRP) ? 's' : 'S';
+ else
+ str[6] = FIELD_MATCH(mode, S_IXGRP) ? 'x' : '-';
+
+ str[7] = FIELD_MATCH(mode, S_IROTH) ? 'r' : '-';
+ str[8] = FIELD_MATCH(mode, S_IWOTH) ? 'w' : '-';
+ if(FIELD_MATCH(mode, S_ISVTX))
+ str[9] = FIELD_MATCH(mode, S_IXOTH) ? 't' : 'T';
+ else
+ str[9] = FIELD_MATCH(mode, S_IXOTH) ? 'x' : '-';
+
+ str[10] = '\0';
+}
diff --git a/lib/truncation.c b/libutils/truncation.c
diff --git a/lib/truncation.h b/libutils/truncation.h
diff --git a/lib/user_group_parse.c b/libutils/user_group_parse.c
diff --git a/lib/user_group_parse.h b/libutils/user_group_parse.h
diff --git a/test-lib/t_humanize.c b/test-lib/t_humanize.c
@@ -1,78 +0,0 @@
-// 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/humanize.h"
-
-#include <assert.h>
-#include <stdio.h> // printf
-
-const char *argv0 = "t_humanize";
-int counter = 0;
-int err = 0;
-
-static void
-t_dtosi(double in, bool iec, double out, unsigned exponent)
-{
- int id = ++counter;
-
- struct si_scale ret = dtosi(in, iec);
-
- if(ret.number == out && ret.exponent == exponent)
- {
- printf("ok %d - dtosi(%f, %d) => {.number = %g, .exponent = %d}\n",
- id,
- in,
- iec,
- ret.number,
- ret.exponent);
- return;
- }
-
- err = 1;
- printf("not ok %d - dtosi(%f, %d) => {.number = %g, .exponent = %d}\n",
- id,
- in,
- iec,
- ret.number,
- ret.exponent);
- printf("# Expected: {.number = %g, .exponent = %d}\n", out, exponent);
-}
-
-int
-main(void)
-{
- int plan = 18;
- printf("1..%d\n", plan);
-
- t_dtosi(0, false, 0, 0);
- t_dtosi(0, true, 0, 0);
-
- t_dtosi(1000, false, 1000, 0);
- t_dtosi(10000, false, 10, 1);
-
- t_dtosi(-1000, false, -1000, 0);
- t_dtosi(-10000, false, -10, 1);
-
- t_dtosi(1024, true, 1024, 0);
- t_dtosi(10240, true, 10240 / 1024, 1);
-
- t_dtosi(-1024, true, -1024, 0);
- t_dtosi(-10240, true, -10240 / 1024, 1);
-
-#define IEC_QiB 1267650600228229401496703205376.0 /* 1024^10 */
- t_dtosi(1024 * IEC_QiB, true, 1024, 10);
- t_dtosi(-1024 * IEC_QiB, true, -1024, 10);
- t_dtosi(1024 * 1024 * IEC_QiB, true, 1024 * 1024, 10);
- t_dtosi(-1024 * 1024 * IEC_QiB, true, -1024 * 1024, 10);
-
-#define SI_Q 1000000000000000000000000000000.0 /* 1000^10 */
- t_dtosi(1000 * SI_Q, false, 1000, 10);
- t_dtosi(-1000 * SI_Q, false, -1000, 10);
- t_dtosi(1000 * 1000 * SI_Q, false, 1000 * 1000, 10);
- t_dtosi(-1000 * 1000 * SI_Q, false, -1000 * 1000, 10);
-
- assert(counter == plan);
- return err;
-}
diff --git a/test-lib/t_mode.c b/test-lib/t_mode.c
@@ -1,325 +0,0 @@
-// 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 <stdio.h> // printf
-#include <string.h> // strcmp
-#include <sys/stat.h> // umask
-
-int counter = 0;
-int t_err = 0;
-
-static void
-t_mode(const char *str, mode_t old, mode_t expect)
-{
- int id = ++counter;
- const char *errstr = NULL;
- mode_t got = new_mode(str, old, &errstr);
- if(got == expect && errstr == NULL)
- {
- printf("ok %d - new_mode(\"%s\", 0%06o) == 0%06o\n", id, str, old, expect);
- return;
- }
-
- t_err = 1;
- printf("not ok %d - new_mode(\"%s\", 0%06o) == 0%06o\n", id, str, old, expect);
- if(got != expect) printf("# Got: 0%06o\n", got);
- if(errstr != NULL) printf("# Errstr: \"%s\"\n", errstr);
-}
-
-static void
-t_mode_errstr(const char *str, mode_t old, const char *expect_errstr)
-{
- assert(expect_errstr != NULL);
-
- int id = ++counter;
- const char *errstr = NULL;
- (void)new_mode(str, old, &errstr);
-
- if(errstr != NULL && strcmp(errstr, expect_errstr) == 0)
- {
- printf("ok %d - new_mode(\"%s\", 0%06o) -> %s\n", id, str, old, expect_errstr);
- }
- else
- {
- printf("not ok %d - new_mode(\"%s\", 0%06o) -> %s\n", id, str, old, expect_errstr);
- printf("# Got errstr: \"%s\"\n", errstr);
- t_err = 1;
- }
-}
-
-static void
-add_read(void)
-{
- printf("# => add_read\n");
- umask(0044);
-
- t_mode("+r", 0, 00400);
- t_mode("a+r", 0, 00444);
- t_mode("u+r", 0, 00400);
- t_mode("g+r", 0, 00040);
- t_mode("o+r", 0, 00004);
- t_mode("ug+r", 0, 00440);
- t_mode("go+r", 0, 00044);
- t_mode("uo+r", 0, 00404);
- t_mode("u+r,g+r", 0, 00440);
- t_mode("g+r,o+r", 0, 00044);
- t_mode("u+r,o+r", 0, 00404);
-
- t_mode("+r", 00777, 00777);
- t_mode("a+r", 00777, 00777);
- t_mode("u+r", 00777, 00777);
- t_mode("g+r", 00777, 00777);
- t_mode("o+r", 00777, 00777);
- t_mode("ug+r", 00777, 00777);
- t_mode("go+r", 00777, 00777);
- t_mode("uo+r", 00777, 00777);
- t_mode("u+r,g+r", 00777, 00777);
- t_mode("g+r,o+r", 00777, 00777);
- t_mode("u+r,o+r", 00777, 00777);
-}
-
-static void
-set_read(void)
-{
- printf("# => set_read\n");
- umask(0044);
-
- t_mode("=r", 0, 00400);
- t_mode("a=r", 0, 00444);
- t_mode("u=r", 0, 00400);
- t_mode("g=r", 0, 00040);
- t_mode("o=r", 0, 00004);
- t_mode("ug=r", 0, 00440);
- t_mode("go=r", 0, 00044);
- t_mode("uo=r", 0, 00404);
- t_mode("u=r,g=r", 0, 00440);
- t_mode("g=r,o=r", 0, 00044);
- t_mode("u=r,o=r", 0, 00404);
-
- t_mode("go=,u=xr", 0, 00500);
- t_mode("go=,u=xr", 00777, 00500);
-
- t_mode("=r", 00777, 00400);
- t_mode("a=r", 00777, 00444);
- t_mode("u=r", 00777, 00477);
- t_mode("g=r", 00777, 00747);
- t_mode("o=r", 00777, 00774);
- t_mode("ug=r", 00777, 00447);
- t_mode("go=r", 00777, 00744);
- t_mode("uo=r", 00777, 00474);
- t_mode("u=r,g=r", 00777, 00447);
- t_mode("g=r,o=r", 00777, 00744);
- t_mode("u=r,o=r", 00777, 00474);
-}
-
-static void
-del_read(void)
-{
- printf("# => del_read\n");
- umask(0044);
-
- t_mode("-r", 0, 0);
- t_mode("a-r", 0, 0);
- t_mode("u-r", 0, 0);
- t_mode("g-r", 0, 0);
- t_mode("o-r", 0, 0);
- t_mode("ug-r", 0, 0);
- t_mode("go-r", 0, 0);
- t_mode("uo-r", 0, 0);
- t_mode("u-r,g-r", 0, 0);
- t_mode("g-r,o-r", 0, 0);
- t_mode("u-r,o-r", 0, 0);
-
- t_mode("-r", 00777, 00377);
- t_mode("a-r", 00777, 00333);
- t_mode("u-r", 00777, 00377);
- t_mode("g-r", 00777, 00737);
- t_mode("o-r", 00777, 00773);
- t_mode("ug-r", 00777, 00337);
- t_mode("go-r", 00777, 00733);
- t_mode("uo-r", 00777, 00373);
- t_mode("u-r,g-r", 00777, 00337);
- t_mode("g-r,o-r", 00777, 00733);
- t_mode("u-r,o-r", 00777, 00373);
-}
-
-static void
-search(void)
-{
- printf("# => search\n");
- t_mode("-X", 0, 0);
- t_mode("a-X", 0, 0);
- t_mode("u-X", 0, 0);
- t_mode("g-X", 0, 0);
- t_mode("o-X", 0, 0);
-
- t_mode("+X", 0, 0);
- t_mode("a+X", 0, 0);
- t_mode("u+X", 0, 0);
- t_mode("g+X", 0, 0);
- t_mode("o+X", 0, 0);
-
- t_mode("=X", 0, 0);
- t_mode("a=X", 0, 0);
- t_mode("u=X", 0, 0);
- t_mode("g=X", 0, 0);
- t_mode("o=X", 0, 0);
-
- // S_IFDIR = 0040000
- t_mode("-X", 0040777, 0040666);
- t_mode("a-X", 0040777, 0040666);
- t_mode("u-X", 0040777, 0040677);
- t_mode("g-X", 0040777, 0040767);
- t_mode("o-X", 0040777, 0040776);
-
- t_mode("+X", 0040777, 0040777);
- t_mode("a+X", 0040777, 0040777);
- t_mode("u+X", 0040777, 0040777);
- t_mode("g+X", 0040777, 0040777);
- t_mode("o+X", 0040777, 0040777);
-
- t_mode("=X", 0040777, 0040111);
- t_mode("a=X", 0040777, 0040111);
- t_mode("u=X", 0040777, 0040177);
- t_mode("g=X", 0040777, 0040717);
- t_mode("o=X", 0040777, 0040771);
-
- t_mode("-X", 0040000, 0040000);
- t_mode("a-X", 0040000, 0040000);
- t_mode("u-X", 0040000, 0040000);
- t_mode("g-X", 0040000, 0040000);
- t_mode("o-X", 0040000, 0040000);
-
- t_mode("+X", 0040000, 0040111);
- t_mode("a+X", 0040000, 0040111);
- t_mode("u+X", 0040000, 0040100);
- t_mode("g+X", 0040000, 0040010);
- t_mode("o+X", 0040000, 0040001);
-
- t_mode("=X", 0040000, 0040111);
- t_mode("a=X", 0040000, 0040111);
- t_mode("u=X", 0040000, 0040100);
- t_mode("g=X", 0040000, 0040010);
- t_mode("o=X", 0040000, 0040001);
-}
-
-static void
-syntax(void)
-{
- printf("# => syntax\n");
-
- t_mode("=", 0, 0);
- t_mode("=", 0040777, 0040000);
-
- t_mode("-", 0, 0);
- t_mode("-", 0040777, 0040777);
-
- t_mode("+", 0, 0);
- t_mode("+", 0040777, 0040777);
-
- t_mode_errstr("/", 0, "syntax error, got invalid character");
-
- t_mode_errstr("foo", 0, "syntax error, got invalid character");
-
- t_mode_errstr("77777", 0, "can't be higher than 0o7777");
-
- t_mode_errstr("-0", 0, "syntax error, got invalid character");
-}
-
-static void
-posix_examples(void)
-{
- // Examples as given by POSIX in chmod(1p)
- printf("# => posix_examples\n");
-
- // clears all file mode bits.
- t_mode("a+=", 0040777, 0040000);
- t_mode("a+=", 0040000, 0040000);
-
- // clears group and other write bits
- t_mode("go+-w", 0040777, 0040755);
- t_mode("go+-w", 0040000, 0040000);
-
- // sets group bit to match other bits and then clears group write bit.
- t_mode("g=o-w", 0040777, 0040757);
- t_mode("g=o-w", 0040642, 0040602);
- t_mode("g=o-w", 0040246, 0040246);
- t_mode("g=o-w", 0040000, 0040000);
-
- // clears group read bit and sets group write bit.
- t_mode("g-r+w", 0040777, 0040737);
- t_mode("g-r+w", 0040000, 0040020);
-
- // Sets owner bits to match group bits and sets other bits to match group bits.
- t_mode("uo=g", 0040777, 0040777);
- t_mode("uo=g", 0040642, 0040444);
- t_mode("uo=g", 0040000, 0040000);
-}
-
-static void
-non_symbolic(void)
-{
- printf("# => non_symbolic\n");
-
- t_mode("0", 0, 0);
- t_mode("0", 0040777, 0040000);
-
- t_mode("7", 0, 07);
- t_mode("7", 0040777, 0040007);
-
- t_mode("77", 0, 077);
- t_mode("77", 0040777, 0040077);
-
- t_mode("777", 0, 0777);
- t_mode("777", 0040777, 0040777);
-
- t_mode("700", 0, 0700);
- t_mode("700", 0040777, 0040700);
-
- t_mode("7777", 0, 07777);
- t_mode("7777", 0040777, 0047777);
-}
-
-int
-main(void)
-{
- int plan = 161;
- printf("1..%d\n", plan);
-
- t_mode(NULL, 0, 0);
- t_mode(NULL, 00777, 00777);
- t_mode("", 0, 0);
- t_mode("", 00777, 00777);
- t_mode(",", 0, 0);
- t_mode(",", 00777, 00777);
-
- t_mode("-w,+x", 00666, 00577);
-
- t_mode("a-wx,u+x,u+s", 00666, 04544);
- t_mode("a-wx,ug+x,g+s", 00666, 02554);
- t_mode("a-wx,ug+x,+t", 00666, 01554);
-
- t_mode("-s", 04544, 00544);
- t_mode("g-s", 02544, 00544);
- t_mode("-t", 01544, 00544);
-
- add_read();
- set_read();
- del_read();
-
- search();
-
- posix_examples();
-
- syntax();
-
- non_symbolic();
-
- assert(counter == plan);
- return t_err;
-}
diff --git a/test-lib/t_strtodur.c b/test-lib/t_strtodur.c
@@ -1,83 +0,0 @@
-// 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/strtodur.h"
-
-#include <assert.h>
-#include <stdio.h> // printf
-
-const char *argv0 = "t_strtodur";
-int counter = 0;
-int err = 0;
-
-static void
-t_strtodur(char *str, time_t ex_sec, long ex_nsec)
-{
- int id = ++counter;
-
- struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
- int ret = strtodur(str, &dur);
-
- if(dur.tv_sec == ex_sec && dur.tv_nsec == ex_nsec && ret == 0)
- {
- printf("ok %d - strtodur(\"%s\", _) -> {.tv_sec = %ld, .tv_nsec = %ld}\n",
- id,
- str,
- dur.tv_sec,
- dur.tv_nsec);
- return;
- }
-
- err = 1;
- printf("not ok %d - strtodur(\"%s\", _) -> {.tv_sec = %ld, .tv_nsec = %ld}\n",
- id,
- str,
- dur.tv_sec,
- dur.tv_nsec);
- if(dur.tv_sec != ex_sec || dur.tv_nsec != ex_nsec)
- printf("# Expected: {.tv_sec = %ld, .tv_nsec = %ld}\n", ex_sec, ex_nsec);
- if(ret != 0) printf("# Exit status: %d\n", ret);
-}
-
-int
-main(void)
-{
- int plan = 18;
- printf("1..%d\n", plan);
-
- // TODO: Capture errors, say with open_memstream(3)
-
-#define T_NSEC 1000000000
-#define T_MIN 60
-#define T_HOUR 60 * T_MIN
-#define T_DAY 24 * T_HOUR
-
- t_strtodur(NULL, 0, 0);
- t_strtodur((char *)"", 0, 0);
- t_strtodur((char *)",", 0, 0);
-
- t_strtodur((char *)"1", 1, 0);
- t_strtodur((char *)"1.", 1, 0);
- t_strtodur((char *)"1,", 1, 0);
-
- t_strtodur((char *)".1", 0, T_NSEC * 0.1);
- t_strtodur((char *)"0.1", 0, T_NSEC * 0.1);
-
- t_strtodur((char *)"1s", 1, 0);
- t_strtodur((char *)"1m", T_MIN, 0);
- t_strtodur((char *)"1h", 1 * T_HOUR, 0);
- t_strtodur((char *)"1d", 1 * T_DAY, 0);
- t_strtodur((char *)"1d1h1m1s", (1 * T_DAY) + (1 * T_HOUR) + (1 * T_MIN) + 1, 0);
-
- t_strtodur((char *)"1.5s", 1, T_NSEC * 0.5);
- t_strtodur((char *)"1.5m", 1.5 * T_MIN, 0);
- t_strtodur((char *)"1.5h", 1.5 * T_HOUR, 0);
- t_strtodur((char *)"1.5d", 1.5 * T_DAY, 0);
- t_strtodur(
- (char *)"1.5d1.5h1.5m1.5s", (1.5 * T_DAY) + (1.5 * T_HOUR) + (1.5 * T_MIN) + 1, T_NSEC * 0.5);
-
- assert(counter == plan);
- return err;
-}
diff --git a/test-lib/t_symbolize_mode.c b/test-lib/t_symbolize_mode.c
@@ -1,47 +0,0 @@
-// 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 <stdio.h> // printf
-#include <string.h> // strcmp
-
-int counter = 0;
-int err = 0;
-
-static void
-t_symbolize_mode(mode_t mode, const char *expected)
-{
- char str[11] = "";
- symbolize_mode(mode, str);
- if(strcmp(str, expected) == 0)
- {
- printf("ok %d - %7o -> %s\n", ++counter, mode, expected);
- return;
- }
-
- err = 1;
- printf("not ok %d - %7o -> %s\n", ++counter, mode, expected);
- printf("# Got: %s\n", str);
-}
-
-int
-main(void)
-{
- int plan = 6;
- printf("1..%d\n", plan);
-
- t_symbolize_mode(0040000, "d---------");
- t_symbolize_mode(0040755, "drwxr-xr-x");
- t_symbolize_mode(0020644, "crw-r--r--");
-
- t_symbolize_mode(0024644, "crwSr--r--");
- t_symbolize_mode(0022774, "crwxrwsr--");
- t_symbolize_mode(0021744, "crwxr--r-T");
-
- assert(counter == plan);
- return err;
-}
diff --git a/test-lib/t_truncation.c b/test-lib/t_truncation.c
@@ -1,80 +0,0 @@
-// 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/truncation.h"
-
-#include <assert.h>
-#include <stdio.h> // printf
-
-const char *argv0 = "test-lib/truncation";
-
-int counter = 0;
-int err = 0;
-
-static void
-t_parse_size(const char *str, off_t size, enum operation_e op)
-{
- int id = ++counter;
-
- struct truncation tr;
- int ret = parse_size(str, &tr);
- if(ret == 0 && tr.size == size && tr.op == op)
- {
- printf("ok %d - parse_size(\"%s\", _) -> {.size = %ld, .op = %d}\n", id, str, tr.size, tr.op);
- return;
- }
-
- err = 1;
- printf("not ok %d - parse_size(\"%s\", _) -> {.size = %ld, .op = %d}\n", id, str, tr.size, tr.op);
- if(tr.size != size || tr.op != op) printf("# Expected: {.size = %ld, .op = %d}\n", size, op);
- if(ret != 0) printf("# Exit status: %d\n", ret);
-}
-
-static void
-set(void)
-{
- t_parse_size("0", 0L, OP_SET);
- t_parse_size("666", 666L, OP_SET);
- t_parse_size("666M", 666 * 1024 * 1024L, OP_SET);
- t_parse_size("666MB", 666 * 1000 * 1000L, OP_SET);
- t_parse_size("666MiB", 666 * 1024 * 1024L, OP_SET);
-}
-
-static void
-inc(void)
-{
- t_parse_size("+0", 0L, OP_INC);
- t_parse_size("+666", 666L, OP_INC);
- t_parse_size("+666M", 666 * 1024 * 1024L, OP_INC);
- t_parse_size("+666MB", 666 * 1000 * 1000L, OP_INC);
- t_parse_size("+666MiB", 666 * 1024 * 1024L, OP_INC);
-}
-
-static void
-dec(void)
-{
- t_parse_size("-0", 0L, OP_DEC);
- t_parse_size("-666", 666L, OP_DEC);
- t_parse_size("-666M", 666 * 1024 * 1024L, OP_DEC);
- t_parse_size("-666MB", 666 * 1000 * 1000L, OP_DEC);
- t_parse_size("-666MiB", 666 * 1024 * 1024L, OP_DEC);
-}
-
-int
-main(void)
-{
- int plan = 15;
- printf("1..%d\n", plan);
-
- // needs standard error capture
- //t_parse_size("", 0, OP_SET);
-
- set();
- inc();
- dec();
-
- assert(counter == plan);
- return err;
-}
diff --git a/test-libutils/t_humanize.c b/test-libutils/t_humanize.c
@@ -0,0 +1,78 @@
+// 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 "../libutils/humanize.h"
+
+#include <assert.h>
+#include <stdio.h> // printf
+
+const char *argv0 = "t_humanize";
+int counter = 0;
+int err = 0;
+
+static void
+t_dtosi(double in, bool iec, double out, unsigned exponent)
+{
+ int id = ++counter;
+
+ struct si_scale ret = dtosi(in, iec);
+
+ if(ret.number == out && ret.exponent == exponent)
+ {
+ printf("ok %d - dtosi(%f, %d) => {.number = %g, .exponent = %d}\n",
+ id,
+ in,
+ iec,
+ ret.number,
+ ret.exponent);
+ return;
+ }
+
+ err = 1;
+ printf("not ok %d - dtosi(%f, %d) => {.number = %g, .exponent = %d}\n",
+ id,
+ in,
+ iec,
+ ret.number,
+ ret.exponent);
+ printf("# Expected: {.number = %g, .exponent = %d}\n", out, exponent);
+}
+
+int
+main(void)
+{
+ int plan = 18;
+ printf("1..%d\n", plan);
+
+ t_dtosi(0, false, 0, 0);
+ t_dtosi(0, true, 0, 0);
+
+ t_dtosi(1000, false, 1000, 0);
+ t_dtosi(10000, false, 10, 1);
+
+ t_dtosi(-1000, false, -1000, 0);
+ t_dtosi(-10000, false, -10, 1);
+
+ t_dtosi(1024, true, 1024, 0);
+ t_dtosi(10240, true, 10240 / 1024, 1);
+
+ t_dtosi(-1024, true, -1024, 0);
+ t_dtosi(-10240, true, -10240 / 1024, 1);
+
+#define IEC_QiB 1267650600228229401496703205376.0 /* 1024^10 */
+ t_dtosi(1024 * IEC_QiB, true, 1024, 10);
+ t_dtosi(-1024 * IEC_QiB, true, -1024, 10);
+ t_dtosi(1024 * 1024 * IEC_QiB, true, 1024 * 1024, 10);
+ t_dtosi(-1024 * 1024 * IEC_QiB, true, -1024 * 1024, 10);
+
+#define SI_Q 1000000000000000000000000000000.0 /* 1000^10 */
+ t_dtosi(1000 * SI_Q, false, 1000, 10);
+ t_dtosi(-1000 * SI_Q, false, -1000, 10);
+ t_dtosi(1000 * 1000 * SI_Q, false, 1000 * 1000, 10);
+ t_dtosi(-1000 * 1000 * SI_Q, false, -1000 * 1000, 10);
+
+ assert(counter == plan);
+ return err;
+}
diff --git a/test-libutils/t_mode.c b/test-libutils/t_mode.c
@@ -0,0 +1,325 @@
+// 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 "../libutils/mode.h"
+
+#include <assert.h>
+#include <stdio.h> // printf
+#include <string.h> // strcmp
+#include <sys/stat.h> // umask
+
+int counter = 0;
+int t_err = 0;
+
+static void
+t_mode(const char *str, mode_t old, mode_t expect)
+{
+ int id = ++counter;
+ const char *errstr = NULL;
+ mode_t got = new_mode(str, old, &errstr);
+ if(got == expect && errstr == NULL)
+ {
+ printf("ok %d - new_mode(\"%s\", 0%06o) == 0%06o\n", id, str, old, expect);
+ return;
+ }
+
+ t_err = 1;
+ printf("not ok %d - new_mode(\"%s\", 0%06o) == 0%06o\n", id, str, old, expect);
+ if(got != expect) printf("# Got: 0%06o\n", got);
+ if(errstr != NULL) printf("# Errstr: \"%s\"\n", errstr);
+}
+
+static void
+t_mode_errstr(const char *str, mode_t old, const char *expect_errstr)
+{
+ assert(expect_errstr != NULL);
+
+ int id = ++counter;
+ const char *errstr = NULL;
+ (void)new_mode(str, old, &errstr);
+
+ if(errstr != NULL && strcmp(errstr, expect_errstr) == 0)
+ {
+ printf("ok %d - new_mode(\"%s\", 0%06o) -> %s\n", id, str, old, expect_errstr);
+ }
+ else
+ {
+ printf("not ok %d - new_mode(\"%s\", 0%06o) -> %s\n", id, str, old, expect_errstr);
+ printf("# Got errstr: \"%s\"\n", errstr);
+ t_err = 1;
+ }
+}
+
+static void
+add_read(void)
+{
+ printf("# => add_read\n");
+ umask(0044);
+
+ t_mode("+r", 0, 00400);
+ t_mode("a+r", 0, 00444);
+ t_mode("u+r", 0, 00400);
+ t_mode("g+r", 0, 00040);
+ t_mode("o+r", 0, 00004);
+ t_mode("ug+r", 0, 00440);
+ t_mode("go+r", 0, 00044);
+ t_mode("uo+r", 0, 00404);
+ t_mode("u+r,g+r", 0, 00440);
+ t_mode("g+r,o+r", 0, 00044);
+ t_mode("u+r,o+r", 0, 00404);
+
+ t_mode("+r", 00777, 00777);
+ t_mode("a+r", 00777, 00777);
+ t_mode("u+r", 00777, 00777);
+ t_mode("g+r", 00777, 00777);
+ t_mode("o+r", 00777, 00777);
+ t_mode("ug+r", 00777, 00777);
+ t_mode("go+r", 00777, 00777);
+ t_mode("uo+r", 00777, 00777);
+ t_mode("u+r,g+r", 00777, 00777);
+ t_mode("g+r,o+r", 00777, 00777);
+ t_mode("u+r,o+r", 00777, 00777);
+}
+
+static void
+set_read(void)
+{
+ printf("# => set_read\n");
+ umask(0044);
+
+ t_mode("=r", 0, 00400);
+ t_mode("a=r", 0, 00444);
+ t_mode("u=r", 0, 00400);
+ t_mode("g=r", 0, 00040);
+ t_mode("o=r", 0, 00004);
+ t_mode("ug=r", 0, 00440);
+ t_mode("go=r", 0, 00044);
+ t_mode("uo=r", 0, 00404);
+ t_mode("u=r,g=r", 0, 00440);
+ t_mode("g=r,o=r", 0, 00044);
+ t_mode("u=r,o=r", 0, 00404);
+
+ t_mode("go=,u=xr", 0, 00500);
+ t_mode("go=,u=xr", 00777, 00500);
+
+ t_mode("=r", 00777, 00400);
+ t_mode("a=r", 00777, 00444);
+ t_mode("u=r", 00777, 00477);
+ t_mode("g=r", 00777, 00747);
+ t_mode("o=r", 00777, 00774);
+ t_mode("ug=r", 00777, 00447);
+ t_mode("go=r", 00777, 00744);
+ t_mode("uo=r", 00777, 00474);
+ t_mode("u=r,g=r", 00777, 00447);
+ t_mode("g=r,o=r", 00777, 00744);
+ t_mode("u=r,o=r", 00777, 00474);
+}
+
+static void
+del_read(void)
+{
+ printf("# => del_read\n");
+ umask(0044);
+
+ t_mode("-r", 0, 0);
+ t_mode("a-r", 0, 0);
+ t_mode("u-r", 0, 0);
+ t_mode("g-r", 0, 0);
+ t_mode("o-r", 0, 0);
+ t_mode("ug-r", 0, 0);
+ t_mode("go-r", 0, 0);
+ t_mode("uo-r", 0, 0);
+ t_mode("u-r,g-r", 0, 0);
+ t_mode("g-r,o-r", 0, 0);
+ t_mode("u-r,o-r", 0, 0);
+
+ t_mode("-r", 00777, 00377);
+ t_mode("a-r", 00777, 00333);
+ t_mode("u-r", 00777, 00377);
+ t_mode("g-r", 00777, 00737);
+ t_mode("o-r", 00777, 00773);
+ t_mode("ug-r", 00777, 00337);
+ t_mode("go-r", 00777, 00733);
+ t_mode("uo-r", 00777, 00373);
+ t_mode("u-r,g-r", 00777, 00337);
+ t_mode("g-r,o-r", 00777, 00733);
+ t_mode("u-r,o-r", 00777, 00373);
+}
+
+static void
+search(void)
+{
+ printf("# => search\n");
+ t_mode("-X", 0, 0);
+ t_mode("a-X", 0, 0);
+ t_mode("u-X", 0, 0);
+ t_mode("g-X", 0, 0);
+ t_mode("o-X", 0, 0);
+
+ t_mode("+X", 0, 0);
+ t_mode("a+X", 0, 0);
+ t_mode("u+X", 0, 0);
+ t_mode("g+X", 0, 0);
+ t_mode("o+X", 0, 0);
+
+ t_mode("=X", 0, 0);
+ t_mode("a=X", 0, 0);
+ t_mode("u=X", 0, 0);
+ t_mode("g=X", 0, 0);
+ t_mode("o=X", 0, 0);
+
+ // S_IFDIR = 0040000
+ t_mode("-X", 0040777, 0040666);
+ t_mode("a-X", 0040777, 0040666);
+ t_mode("u-X", 0040777, 0040677);
+ t_mode("g-X", 0040777, 0040767);
+ t_mode("o-X", 0040777, 0040776);
+
+ t_mode("+X", 0040777, 0040777);
+ t_mode("a+X", 0040777, 0040777);
+ t_mode("u+X", 0040777, 0040777);
+ t_mode("g+X", 0040777, 0040777);
+ t_mode("o+X", 0040777, 0040777);
+
+ t_mode("=X", 0040777, 0040111);
+ t_mode("a=X", 0040777, 0040111);
+ t_mode("u=X", 0040777, 0040177);
+ t_mode("g=X", 0040777, 0040717);
+ t_mode("o=X", 0040777, 0040771);
+
+ t_mode("-X", 0040000, 0040000);
+ t_mode("a-X", 0040000, 0040000);
+ t_mode("u-X", 0040000, 0040000);
+ t_mode("g-X", 0040000, 0040000);
+ t_mode("o-X", 0040000, 0040000);
+
+ t_mode("+X", 0040000, 0040111);
+ t_mode("a+X", 0040000, 0040111);
+ t_mode("u+X", 0040000, 0040100);
+ t_mode("g+X", 0040000, 0040010);
+ t_mode("o+X", 0040000, 0040001);
+
+ t_mode("=X", 0040000, 0040111);
+ t_mode("a=X", 0040000, 0040111);
+ t_mode("u=X", 0040000, 0040100);
+ t_mode("g=X", 0040000, 0040010);
+ t_mode("o=X", 0040000, 0040001);
+}
+
+static void
+syntax(void)
+{
+ printf("# => syntax\n");
+
+ t_mode("=", 0, 0);
+ t_mode("=", 0040777, 0040000);
+
+ t_mode("-", 0, 0);
+ t_mode("-", 0040777, 0040777);
+
+ t_mode("+", 0, 0);
+ t_mode("+", 0040777, 0040777);
+
+ t_mode_errstr("/", 0, "syntax error, got invalid character");
+
+ t_mode_errstr("foo", 0, "syntax error, got invalid character");
+
+ t_mode_errstr("77777", 0, "can't be higher than 0o7777");
+
+ t_mode_errstr("-0", 0, "syntax error, got invalid character");
+}
+
+static void
+posix_examples(void)
+{
+ // Examples as given by POSIX in chmod(1p)
+ printf("# => posix_examples\n");
+
+ // clears all file mode bits.
+ t_mode("a+=", 0040777, 0040000);
+ t_mode("a+=", 0040000, 0040000);
+
+ // clears group and other write bits
+ t_mode("go+-w", 0040777, 0040755);
+ t_mode("go+-w", 0040000, 0040000);
+
+ // sets group bit to match other bits and then clears group write bit.
+ t_mode("g=o-w", 0040777, 0040757);
+ t_mode("g=o-w", 0040642, 0040602);
+ t_mode("g=o-w", 0040246, 0040246);
+ t_mode("g=o-w", 0040000, 0040000);
+
+ // clears group read bit and sets group write bit.
+ t_mode("g-r+w", 0040777, 0040737);
+ t_mode("g-r+w", 0040000, 0040020);
+
+ // Sets owner bits to match group bits and sets other bits to match group bits.
+ t_mode("uo=g", 0040777, 0040777);
+ t_mode("uo=g", 0040642, 0040444);
+ t_mode("uo=g", 0040000, 0040000);
+}
+
+static void
+non_symbolic(void)
+{
+ printf("# => non_symbolic\n");
+
+ t_mode("0", 0, 0);
+ t_mode("0", 0040777, 0040000);
+
+ t_mode("7", 0, 07);
+ t_mode("7", 0040777, 0040007);
+
+ t_mode("77", 0, 077);
+ t_mode("77", 0040777, 0040077);
+
+ t_mode("777", 0, 0777);
+ t_mode("777", 0040777, 0040777);
+
+ t_mode("700", 0, 0700);
+ t_mode("700", 0040777, 0040700);
+
+ t_mode("7777", 0, 07777);
+ t_mode("7777", 0040777, 0047777);
+}
+
+int
+main(void)
+{
+ int plan = 161;
+ printf("1..%d\n", plan);
+
+ t_mode(NULL, 0, 0);
+ t_mode(NULL, 00777, 00777);
+ t_mode("", 0, 0);
+ t_mode("", 00777, 00777);
+ t_mode(",", 0, 0);
+ t_mode(",", 00777, 00777);
+
+ t_mode("-w,+x", 00666, 00577);
+
+ t_mode("a-wx,u+x,u+s", 00666, 04544);
+ t_mode("a-wx,ug+x,g+s", 00666, 02554);
+ t_mode("a-wx,ug+x,+t", 00666, 01554);
+
+ t_mode("-s", 04544, 00544);
+ t_mode("g-s", 02544, 00544);
+ t_mode("-t", 01544, 00544);
+
+ add_read();
+ set_read();
+ del_read();
+
+ search();
+
+ posix_examples();
+
+ syntax();
+
+ non_symbolic();
+
+ assert(counter == plan);
+ return t_err;
+}
diff --git a/test-libutils/t_strtodur.c b/test-libutils/t_strtodur.c
@@ -0,0 +1,83 @@
+// 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 "../libutils/strtodur.h"
+
+#include <assert.h>
+#include <stdio.h> // printf
+
+const char *argv0 = "t_strtodur";
+int counter = 0;
+int err = 0;
+
+static void
+t_strtodur(char *str, time_t ex_sec, long ex_nsec)
+{
+ int id = ++counter;
+
+ struct timespec dur = {.tv_sec = 0, .tv_nsec = 0};
+ int ret = strtodur(str, &dur);
+
+ if(dur.tv_sec == ex_sec && dur.tv_nsec == ex_nsec && ret == 0)
+ {
+ printf("ok %d - strtodur(\"%s\", _) -> {.tv_sec = %ld, .tv_nsec = %ld}\n",
+ id,
+ str,
+ dur.tv_sec,
+ dur.tv_nsec);
+ return;
+ }
+
+ err = 1;
+ printf("not ok %d - strtodur(\"%s\", _) -> {.tv_sec = %ld, .tv_nsec = %ld}\n",
+ id,
+ str,
+ dur.tv_sec,
+ dur.tv_nsec);
+ if(dur.tv_sec != ex_sec || dur.tv_nsec != ex_nsec)
+ printf("# Expected: {.tv_sec = %ld, .tv_nsec = %ld}\n", ex_sec, ex_nsec);
+ if(ret != 0) printf("# Exit status: %d\n", ret);
+}
+
+int
+main(void)
+{
+ int plan = 18;
+ printf("1..%d\n", plan);
+
+ // TODO: Capture errors, say with open_memstream(3)
+
+#define T_NSEC 1000000000
+#define T_MIN 60
+#define T_HOUR 60 * T_MIN
+#define T_DAY 24 * T_HOUR
+
+ t_strtodur(NULL, 0, 0);
+ t_strtodur((char *)"", 0, 0);
+ t_strtodur((char *)",", 0, 0);
+
+ t_strtodur((char *)"1", 1, 0);
+ t_strtodur((char *)"1.", 1, 0);
+ t_strtodur((char *)"1,", 1, 0);
+
+ t_strtodur((char *)".1", 0, T_NSEC * 0.1);
+ t_strtodur((char *)"0.1", 0, T_NSEC * 0.1);
+
+ t_strtodur((char *)"1s", 1, 0);
+ t_strtodur((char *)"1m", T_MIN, 0);
+ t_strtodur((char *)"1h", 1 * T_HOUR, 0);
+ t_strtodur((char *)"1d", 1 * T_DAY, 0);
+ t_strtodur((char *)"1d1h1m1s", (1 * T_DAY) + (1 * T_HOUR) + (1 * T_MIN) + 1, 0);
+
+ t_strtodur((char *)"1.5s", 1, T_NSEC * 0.5);
+ t_strtodur((char *)"1.5m", 1.5 * T_MIN, 0);
+ t_strtodur((char *)"1.5h", 1.5 * T_HOUR, 0);
+ t_strtodur((char *)"1.5d", 1.5 * T_DAY, 0);
+ t_strtodur(
+ (char *)"1.5d1.5h1.5m1.5s", (1.5 * T_DAY) + (1.5 * T_HOUR) + (1.5 * T_MIN) + 1, T_NSEC * 0.5);
+
+ assert(counter == plan);
+ return err;
+}
diff --git a/test-libutils/t_symbolize_mode.c b/test-libutils/t_symbolize_mode.c
@@ -0,0 +1,47 @@
+// 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 "../libutils/mode.h"
+
+#include <assert.h>
+#include <stdio.h> // printf
+#include <string.h> // strcmp
+
+int counter = 0;
+int err = 0;
+
+static void
+t_symbolize_mode(mode_t mode, const char *expected)
+{
+ char str[11] = "";
+ symbolize_mode(mode, str);
+ if(strcmp(str, expected) == 0)
+ {
+ printf("ok %d - %7o -> %s\n", ++counter, mode, expected);
+ return;
+ }
+
+ err = 1;
+ printf("not ok %d - %7o -> %s\n", ++counter, mode, expected);
+ printf("# Got: %s\n", str);
+}
+
+int
+main(void)
+{
+ int plan = 6;
+ printf("1..%d\n", plan);
+
+ t_symbolize_mode(0040000, "d---------");
+ t_symbolize_mode(0040755, "drwxr-xr-x");
+ t_symbolize_mode(0020644, "crw-r--r--");
+
+ t_symbolize_mode(0024644, "crwSr--r--");
+ t_symbolize_mode(0022774, "crwxrwsr--");
+ t_symbolize_mode(0021744, "crwxr--r-T");
+
+ assert(counter == plan);
+ return err;
+}
diff --git a/test-libutils/t_truncation.c b/test-libutils/t_truncation.c
@@ -0,0 +1,80 @@
+// 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 "../libutils/truncation.h"
+
+#include <assert.h>
+#include <stdio.h> // printf
+
+const char *argv0 = "test-lib/truncation";
+
+int counter = 0;
+int err = 0;
+
+static void
+t_parse_size(const char *str, off_t size, enum operation_e op)
+{
+ int id = ++counter;
+
+ struct truncation tr;
+ int ret = parse_size(str, &tr);
+ if(ret == 0 && tr.size == size && tr.op == op)
+ {
+ printf("ok %d - parse_size(\"%s\", _) -> {.size = %ld, .op = %d}\n", id, str, tr.size, tr.op);
+ return;
+ }
+
+ err = 1;
+ printf("not ok %d - parse_size(\"%s\", _) -> {.size = %ld, .op = %d}\n", id, str, tr.size, tr.op);
+ if(tr.size != size || tr.op != op) printf("# Expected: {.size = %ld, .op = %d}\n", size, op);
+ if(ret != 0) printf("# Exit status: %d\n", ret);
+}
+
+static void
+set(void)
+{
+ t_parse_size("0", 0L, OP_SET);
+ t_parse_size("666", 666L, OP_SET);
+ t_parse_size("666M", 666 * 1024 * 1024L, OP_SET);
+ t_parse_size("666MB", 666 * 1000 * 1000L, OP_SET);
+ t_parse_size("666MiB", 666 * 1024 * 1024L, OP_SET);
+}
+
+static void
+inc(void)
+{
+ t_parse_size("+0", 0L, OP_INC);
+ t_parse_size("+666", 666L, OP_INC);
+ t_parse_size("+666M", 666 * 1024 * 1024L, OP_INC);
+ t_parse_size("+666MB", 666 * 1000 * 1000L, OP_INC);
+ t_parse_size("+666MiB", 666 * 1024 * 1024L, OP_INC);
+}
+
+static void
+dec(void)
+{
+ t_parse_size("-0", 0L, OP_DEC);
+ t_parse_size("-666", 666L, OP_DEC);
+ t_parse_size("-666M", 666 * 1024 * 1024L, OP_DEC);
+ t_parse_size("-666MB", 666 * 1000 * 1000L, OP_DEC);
+ t_parse_size("-666MiB", 666 * 1024 * 1024L, OP_DEC);
+}
+
+int
+main(void)
+{
+ int plan = 15;
+ printf("1..%d\n", plan);
+
+ // needs standard error capture
+ //t_parse_size("", 0, OP_SET);
+
+ set();
+ inc();
+ dec();
+
+ assert(counter == plan);
+ return err;
+}