From 8b24de3882ff5a3e33b82ab20bb4eadf134cf559 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 19 Nov 2022 11:00:15 -0500 Subject: tests: Avoid syslog() using a low fd on macOS --- tests/tests.sh | 21 ++++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 8d13aca..26c993f 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -22,11 +22,11 @@ umask 022 export LC_ALL=C export TZ=UTC0 -export ASAN_OPTIONS="abort_on_error=1" -export LSAN_OPTIONS="abort_on_error=1" -export MSAN_OPTIONS="abort_on_error=1" -export TSAN_OPTIONS="abort_on_error=1" -export UBSAN_OPTIONS="abort_on_error=1" +export ASAN_OPTIONS="abort_on_error=1:log_to_syslog=0" +export LSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" +export MSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" +export TSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" +export UBSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" export LS_COLORS="" unset BFS_COLORS @@ -53,6 +53,17 @@ fi UNAME=$(uname) +if [ "$UNAME" = Darwin ]; then + # ASan on macOS likes to report + # + # malloc: nano zone abandoned due to inability to preallocate reserved vm space. + # + # to syslog, which as a side effect opens a socket which might take the + # place of one of the standard streams if the process is launched with it + # closed. This environment variable avoids the message. + export MallocNanoZone=0 +fi + if command -v capsh &>/dev/null; then if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then if [ -n "${BFS_TRIED_DROP:-}" ]; then -- cgit v1.2.3 From 33a6a3027f0b1ec931824a4b2206bf6659df53a4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 30 Nov 2022 13:57:12 -0500 Subject: parse: Pass the right argument when warning about -O9 --- src/parse.c | 2 +- tests/bfs/warn_O9.out | 19 +++++++++++++++++++ tests/bfs/warn_O9.sh | 3 +++ 3 files changed, 23 insertions(+), 1 deletion(-) create mode 100644 tests/bfs/warn_O9.out create mode 100644 tests/bfs/warn_O9.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 16132f3..5ef71f8 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1016,7 +1016,7 @@ static struct bfs_expr *parse_optlevel(struct parser_state *state, int arg1, int } if (*optlevel > 4) { - parse_expr_warning(state, expr, "${cyn}-O${bld}%s${rs} is the same as ${cyn}-O${bld}4${rs}.\n\n", state->argv[0] + 2); + parse_expr_warning(state, expr, "${cyn}-O${bld}%s${rs} is the same as ${cyn}-O${bld}4${rs}.\n\n", expr->argv[0] + 2); } return expr; diff --git a/tests/bfs/warn_O9.out b/tests/bfs/warn_O9.out new file mode 100644 index 0000000..336a6e8 --- /dev/null +++ b/tests/bfs/warn_O9.out @@ -0,0 +1,19 @@ +. +./a +./b +./c +./c/d +./e +./e/f +./g +./g/h +./i +./j +./j/foo +./k +./k/foo +./k/foo/bar +./l +./l/foo +./l/foo/bar +./l/foo/bar/baz diff --git a/tests/bfs/warn_O9.sh b/tests/bfs/warn_O9.sh new file mode 100644 index 0000000..821789f --- /dev/null +++ b/tests/bfs/warn_O9.sh @@ -0,0 +1,3 @@ +# Regression test: don't crash when warning if -O9 is the last argument +cd basic +bfs_diff -warn -O9 -- cgit v1.2.3 From c55e85580df10c5afdc6fc0710e756a456aa8e93 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 30 Nov 2022 14:16:51 -0500 Subject: parse: Fix crash on -xdev -mount --- src/parse.c | 2 +- tests/bfs/warn_xdev_mount.out | 19 +++++++++++++++++++ tests/bfs/warn_xdev_mount.sh | 2 ++ 3 files changed, 22 insertions(+), 1 deletion(-) create mode 100644 tests/bfs/warn_xdev_mount.out create mode 100644 tests/bfs/warn_xdev_mount.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 5ef71f8..a858a4c 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1802,7 +1802,7 @@ static struct bfs_expr *parse_mount(struct parser_state *state, int arg1, int ar bfs_warning(state->ctx, "${blu}-xdev${rs}, due to http://austingroupbugs.net/view.php?id=1133.\n\n"); state->ctx->flags |= BFTW_PRUNE_MOUNTS; - state->mount_arg = state->argv; + state->mount_arg = expr->argv; return expr; } diff --git a/tests/bfs/warn_xdev_mount.out b/tests/bfs/warn_xdev_mount.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/warn_xdev_mount.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/warn_xdev_mount.sh b/tests/bfs/warn_xdev_mount.sh new file mode 100644 index 0000000..5d395f6 --- /dev/null +++ b/tests/bfs/warn_xdev_mount.sh @@ -0,0 +1,2 @@ +# Regression test: don't crash if -mount is the last option +bfs_diff basic -warn -xdev -mount -- cgit v1.2.3 From d6a1b97b0eece42bb18a171ccdfcec7082d5f2d0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 2 Dec 2022 10:27:46 -0500 Subject: tests: Allow wildcard patterns like "posix/*" --- tests/tests.sh | 79 +++++++++++++++++++++++----------------------------------- 1 file changed, 31 insertions(+), 48 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 26c993f..639822a 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -138,16 +138,11 @@ Usage: ${GRN}$0${RST} [${BLU}--bfs${RST}=${MAG}path/to/bfs${RST}] [${BLU}--posix This message ${BLD}TEST${RST} - Select individual test cases to run (e.g. ${BLD}posix/basic${RST}) + Select individual test cases to run (e.g. ${BLD}posix/basic${RST}, ${BLD}"*exec*"${RST}, ...) EOF } -DEFAULT=yes -POSIX= -COMMON= -BSD= -GNU= -ALL= +PATTERNS=() SUDO= STOP= CLEAN=yes @@ -156,9 +151,6 @@ VERBOSE_COMMANDS= VERBOSE_ERRORS= VERBOSE_SKIPPED= VERBOSE_TESTS= -EXPLICIT= - -enabled_tests=() for arg; do case "$arg" in @@ -166,28 +158,16 @@ for arg; do BFS="${arg#*=}" ;; --posix) - DEFAULT= - POSIX=yes + PATTERNS+=("posix/*") ;; --bsd) - DEFAULT= - POSIX=yes - COMMON=yes - BSD=yes + PATTERNS+=("posix/*" "common/*" "bsd/*") ;; --gnu) - DEFAULT= - POSIX=yes - COMMON=yes - GNU=yes + PATTERNS+=("posix/*" "common/*" "gnu/*") ;; --all) - DEFAULT= - POSIX=yes - COMMON=yes - BSD=yes - GNU=yes - ALL=yes + PATTERNS+=("*") ;; --sudo) SUDO=yes @@ -224,27 +204,17 @@ for arg; do usage exit 0 ;; - */*) - EXPLICIT=yes - SUDO=yes - enabled_tests+=("$arg") - ;; - *) + -*) printf "${RED}error:${RST} Unrecognized option '%s'.\n\n" "$arg" >&2 usage >&2 exit 1 ;; + *) + PATTERNS+=("$arg") + ;; esac done -if [ "$DEFAULT" ]; then - POSIX=yes - COMMON=yes - BSD=yes - GNU=yes - ALL=yes -fi - function _realpath() { ( cd "$(dirname -- "$1")" @@ -273,14 +243,27 @@ chown "$(id -u):$(id -g)" "$TMP" cd "$TESTS" -if [ ! "$EXPLICIT" ]; then - [ "$POSIX" ] && enabled_tests+=(posix/*.sh) - [ "$COMMON" ] && enabled_tests+=(common/*.sh) - [ "$BSD" ] && enabled_tests+=(bsd/*.sh) - [ "$GNU" ] && enabled_tests+=(gnu/*.sh) - [ "$ALL" ] && enabled_tests+=(bfs/*.sh) +if (( ${#PATTERNS[@]} == 0 )); then + PATTERNS=("*") +fi - enabled_tests=("${enabled_tests[@]%.sh}") +TEST_CASES=() +for TEST in {posix,common,bsd,gnu,bfs}/*.sh; do + TEST="${TEST%.sh}" + for PATTERN in "${PATTERNS[@]}"; do + if [[ $TEST == $PATTERN ]]; then + TEST_CASES+=("$TEST") + break + fi + done +done + +if (( ${#TEST_CASES[@]} == 0 )); then + printf "${RED}error:${RST} No tests matched" >&2 + printf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 + printf ".\n\n" >&2 + usage >&2 + exit 1 fi function clean_scratch() { @@ -671,7 +654,7 @@ passed=0 failed=0 skipped=0 -for TEST in "${enabled_tests[@]}"; do +for TEST in "${TEST_CASES[@]}"; do if [[ -t 1 || "$VERBOSE_TESTS" ]]; then printf "${BOL}${YLW}%s${RST}${EOL}" "$TEST" else -- cgit v1.2.3 From 2898d6b79d15c984f95977f7385dd4273087c61b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 7 Dec 2022 10:21:19 -0500 Subject: config: New BFS_COUNTOF macro --- src/config.h | 5 +++++ src/fsade.c | 3 +-- tests/trie.c | 3 ++- 3 files changed, 8 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/src/config.h b/src/config.h index 0822e3c..4a49b75 100644 --- a/src/config.h +++ b/src/config.h @@ -155,6 +155,11 @@ # define BFS_FALLTHROUGH ((void)0) #endif +/** + * Get the length of an array. + */ +#define BFS_COUNTOF(array) (sizeof(array) / sizeof(0[array])) + // Lower bound on BFS_FLEX_SIZEOF() #define BFS_FLEX_LB(type, member, length) (offsetof(type, member) + sizeof(((type *)NULL)->member[0]) * (length)) diff --git a/src/fsade.c b/src/fsade.c index a609b97..45969d1 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -206,7 +206,6 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { ACL_TYPE_NFS4, #endif }; - static const size_t n_acl_types = sizeof(acl_types)/sizeof(acl_types[0]); if (ftwbuf->type == BFS_LNK) { return 0; @@ -215,7 +214,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { const char *path = fake_at(ftwbuf); int ret = -1, error = 0; - for (size_t i = 0; i < n_acl_types && ret <= 0; ++i) { + for (size_t i = 0; i < BFS_COUNTOF(acl_types) && ret <= 0; ++i) { acl_type_t type = acl_types[i]; if (type == ACL_TYPE_DEFAULT && ftwbuf->type != BFS_DIR) { diff --git a/tests/trie.c b/tests/trie.c index 6bc7549..88e92da 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -17,6 +17,7 @@ #undef NDEBUG #include "../src/trie.h" +#include "../src/config.h" #include #include #include @@ -50,7 +51,7 @@ const char *keys[] = { ">>>", }; -const size_t nkeys = sizeof(keys) / sizeof(keys[0]); +const size_t nkeys = BFS_COUNTOF(keys); int main(void) { struct trie trie; -- cgit v1.2.3 From f3ba6a3e40d72e00d13e943d9338b60b4bee2e40 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 13 Dec 2022 15:44:17 -0500 Subject: tests: Add a test for readdir() errors --- tests/posix/readdir_error.sh | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) create mode 100644 tests/posix/readdir_error.sh (limited to 'tests') diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh new file mode 100644 index 0000000..cca71f0 --- /dev/null +++ b/tests/posix/readdir_error.sh @@ -0,0 +1,21 @@ +clean_scratch +mkfifo scratch/{pid,hup} + +( + # Create a zombie process + echo >/dev/null & + # Write the PID to scratch/pid + echo $! >scratch/pid + # Don't wait on the processes + exec cat scratch/hup >/dev/null +) & + +# Kill cat on exit +trap "echo >scratch/hup" EXIT + +# Read the zombie PID +read -r pid /dev/null -- cgit v1.2.3 From f21a9340cd0c615ead1954eb6b93845a4f8c0897 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 14 Dec 2022 12:19:31 -0500 Subject: tests/posix/readdir_error: Fix flakiness --- tests/posix/readdir_error.sh | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh index cca71f0..ce06723 100644 --- a/tests/posix/readdir_error.sh +++ b/tests/posix/readdir_error.sh @@ -1,21 +1,37 @@ +skip_unless test "$UNAME" = "Linux" + clean_scratch -mkfifo scratch/{pid,hup} +mkfifo scratch/{fever,pid,wait,running} ( # Create a zombie process - echo >/dev/null & + cat scratch/fever >/dev/null & # Write the PID to scratch/pid echo $! >scratch/pid - # Don't wait on the processes - exec cat scratch/hup >/dev/null + # Don't wait on the zombie process + exec cat scratch/wait scratch/fever >scratch/running ) & -# Kill cat on exit -trap "echo >scratch/hup" EXIT +# Kill the parent cat on exit +trap "kill -9 %1" EXIT -# Read the zombie PID +# Read the child PID read -r pid scratch/wait & +read -r _ /dev/null -- cgit v1.2.3 From 0074d5b0c064dbf6cfc7231fc5b74659d81b120e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 14 Dec 2022 12:20:18 -0500 Subject: tests: Fix crash when stderr is redirected bash uses fileno(stderr) to keep track of $COLUMNS. With stderr redirected, $COLUMNS will be unset, leading to $ ./tests/tests.sh 2> >(cat) ./tests/tests.sh: line 635: COLUMNS: unbound variable Fix it by using $(tput cols) if $COLUMNS is unset, which is almost POSIX. Link: https://www.austingroupbugs.net/view.php?id=1053 --- tests/tests.sh | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 639822a..3fdf49b 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -630,9 +630,15 @@ BOL='\n' EOL='\n' function update_eol() { + # Bash gets $COLUMNS from stderr, so if it's redirected use tput instead + local cols="${COLUMNS-}" + if [ -z "$cols" ]; then + cols=$(tput cols) + fi + # Put the cursor at the last column, then write a space so the next # character will wrap - EOL="\\033[${COLUMNS}G " + EOL="\\033[${cols}G " } if [ "$VERBOSE_TESTS" ]; then -- cgit v1.2.3 From e01042b84abdfa224d47e6d11eb9798ce4c7d2f8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 14:17:07 -0500 Subject: tests: Replace skip_unless test with test || skip --- tests/bfs/L_capable.sh | 6 +++--- tests/bfs/capable.sh | 6 +++--- tests/bfs/exec_flush_fprint_fail.sh | 2 +- tests/bfs/fprint_error_stderr.sh | 2 +- tests/bfs/fprint_error_stdout.sh | 2 +- tests/bfs/stderr_fails_loudly.sh | 2 +- tests/bfs/stderr_fails_silently.sh | 2 +- tests/bsd/L_acl.sh | 4 ++-- tests/bsd/L_xattr.sh | 4 ++-- tests/bsd/L_xattrname.sh | 4 ++-- tests/bsd/acl.sh | 4 ++-- tests/bsd/flags.sh | 4 ++-- tests/bsd/xattr.sh | 4 ++-- tests/bsd/xattrname.sh | 4 ++-- tests/common/L_ilname.sh | 2 +- tests/common/L_mount.sh | 4 ++-- tests/common/ilname.sh | 2 +- tests/common/iname.sh | 2 +- tests/common/inum_bind_mount.sh | 4 ++-- tests/common/inum_mount.sh | 4 ++-- tests/common/ipath.sh | 2 +- tests/common/mount.sh | 4 ++-- tests/gnu/exec_flush_fail.sh | 2 +- tests/gnu/exec_plus_flush_fail.sh | 2 +- tests/gnu/fprint_error.sh | 2 +- tests/gnu/fprint_noerror.sh | 2 +- tests/gnu/fstype.sh | 3 +-- tests/gnu/gid_plus.sh | 2 +- tests/gnu/gid_plus_plus.sh | 2 +- tests/gnu/inum_automount.sh | 6 +++--- tests/gnu/iwholename.sh | 2 +- tests/gnu/print_error.sh | 2 +- tests/gnu/regex_invalid_utf8.sh | 6 +++--- tests/gnu/regextype_emacs.sh | 2 +- tests/gnu/regextype_grep.sh | 2 +- tests/gnu/uid_plus.sh | 2 +- tests/gnu/uid_plus_plus.sh | 2 +- tests/gnu/xtype_bind_mount.sh | 4 ++-- tests/posix/L_xdev.sh | 4 ++-- tests/posix/name_bracket.sh | 2 +- tests/posix/readdir_error.sh | 4 ++-- tests/posix/type_bind_mount.sh | 4 ++-- tests/posix/xdev.sh | 4 ++-- tests/tests.sh | 10 ---------- 44 files changed, 67 insertions(+), 78 deletions(-) (limited to 'tests') diff --git a/tests/bfs/L_capable.sh b/tests/bfs/L_capable.sh index 533ac2f..a349677 100644 --- a/tests/bfs/L_capable.sh +++ b/tests/bfs/L_capable.sh @@ -1,9 +1,9 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$SUDO" || skip +test "$UNAME" = "Linux" || skip clean_scratch -skip_unless invoke_bfs scratch -quit -capable +invoke_bfs scratch -quit -capable || skip "$XTOUCH" scratch/{normal,capable} sudo setcap all+ep scratch/capable diff --git a/tests/bfs/capable.sh b/tests/bfs/capable.sh index 256b9bc..8b60ea6 100644 --- a/tests/bfs/capable.sh +++ b/tests/bfs/capable.sh @@ -1,9 +1,9 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$SUDO" || skip +test "$UNAME" = "Linux" || skip clean_scratch -skip_unless invoke_bfs scratch -quit -capable +invoke_bfs scratch -quit -capable || skip "$XTOUCH" scratch/{normal,capable} sudo setcap all+ep scratch/capable diff --git a/tests/bfs/exec_flush_fprint_fail.sh b/tests/bfs/exec_flush_fprint_fail.sh index 5da944a..b58624a 100644 --- a/tests/bfs/exec_flush_fprint_fail.sh +++ b/tests/bfs/exec_flush_fprint_fail.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic/a -fprint /dev/full -exec true \; diff --git a/tests/bfs/fprint_error_stderr.sh b/tests/bfs/fprint_error_stderr.sh index 427808f..e51d026 100644 --- a/tests/bfs/fprint_error_stderr.sh +++ b/tests/bfs/fprint_error_stderr.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full diff --git a/tests/bfs/fprint_error_stdout.sh b/tests/bfs/fprint_error_stdout.sh index fbdc1d0..6aa4b11 100644 --- a/tests/bfs/fprint_error_stdout.sh +++ b/tests/bfs/fprint_error_stdout.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full diff --git a/tests/bfs/stderr_fails_loudly.sh b/tests/bfs/stderr_fails_loudly.sh index d8b3861..c423d9e 100644 --- a/tests/bfs/stderr_fails_loudly.sh +++ b/tests/bfs/stderr_fails_loudly.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full diff --git a/tests/bfs/stderr_fails_silently.sh b/tests/bfs/stderr_fails_silently.sh index 731cb02..a37393d 100644 --- a/tests/bfs/stderr_fails_silently.sh +++ b/tests/bfs/stderr_fails_silently.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip bfs_diff -D all basic 2>/dev/full diff --git a/tests/bsd/L_acl.sh b/tests/bsd/L_acl.sh index cf573e4..db97013 100644 --- a/tests/bsd/L_acl.sh +++ b/tests/bsd/L_acl.sh @@ -1,9 +1,9 @@ clean_scratch -skip_unless invoke_bfs scratch -quit -acl +invoke_bfs scratch -quit -acl || skip "$XTOUCH" scratch/{normal,acl} -skip_unless set_acl scratch/acl +set_acl scratch/acl || skip ln -s acl scratch/link bfs_diff -L scratch -acl diff --git a/tests/bsd/L_xattr.sh b/tests/bsd/L_xattr.sh index 7c27e0d..1f61c78 100644 --- a/tests/bsd/L_xattr.sh +++ b/tests/bsd/L_xattr.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs +invoke_bfs scratch -quit -xattr || skip +make_xattrs || skip bfs_diff -L scratch -xattr diff --git a/tests/bsd/L_xattrname.sh b/tests/bsd/L_xattrname.sh index 39d6a77..3b2006b 100644 --- a/tests/bsd/L_xattrname.sh +++ b/tests/bsd/L_xattrname.sh @@ -1,5 +1,5 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs +invoke_bfs scratch -quit -xattr || skip +make_xattrs || skip case "$UNAME" in Darwin|FreeBSD) diff --git a/tests/bsd/acl.sh b/tests/bsd/acl.sh index 1665684..c044398 100644 --- a/tests/bsd/acl.sh +++ b/tests/bsd/acl.sh @@ -1,9 +1,9 @@ clean_scratch -skip_unless invoke_bfs scratch -quit -acl +invoke_bfs scratch -quit -acl || skip "$XTOUCH" scratch/{normal,acl} -skip_unless set_acl scratch/acl +set_acl scratch/acl || skip ln -s acl scratch/link bfs_diff scratch -acl diff --git a/tests/bsd/flags.sh b/tests/bsd/flags.sh index ffb1cc2..949a7d3 100644 --- a/tests/bsd/flags.sh +++ b/tests/bsd/flags.sh @@ -1,8 +1,8 @@ -skip_unless invoke_bfs scratch -quit -flags offline +invoke_bfs scratch -quit -flags offline || skip clean_scratch "$XTOUCH" scratch/{foo,bar} -skip_unless chflags offline scratch/bar +chflags offline scratch/bar || skip bfs_diff scratch -flags -offline,nohidden diff --git a/tests/bsd/xattr.sh b/tests/bsd/xattr.sh index 727c220..4a4658c 100644 --- a/tests/bsd/xattr.sh +++ b/tests/bsd/xattr.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs +invoke_bfs scratch -quit -xattr || skip +make_xattrs || skip bfs_diff scratch -xattr diff --git a/tests/bsd/xattrname.sh b/tests/bsd/xattrname.sh index 6a0fe7e..655bd74 100644 --- a/tests/bsd/xattrname.sh +++ b/tests/bsd/xattrname.sh @@ -1,5 +1,5 @@ -skip_unless invoke_bfs scratch -quit -xattr -skip_unless make_xattrs +invoke_bfs scratch -quit -xattr || skip +make_xattrs || skip case "$UNAME" in Darwin|FreeBSD) diff --git a/tests/common/L_ilname.sh b/tests/common/L_ilname.sh index cfb15a8..e0495ed 100644 --- a/tests/common/L_ilname.sh +++ b/tests/common/L_ilname.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -ilname PATTERN +invoke_bfs -quit -ilname PATTERN || skip bfs_diff -L links -ilname '[AQ]' diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh index dad7e00..5b56762 100644 --- a/tests/common/L_mount.sh +++ b/tests/common/L_mount.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$SUDO" || skip +test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} diff --git a/tests/common/ilname.sh b/tests/common/ilname.sh index 7ab0793..fc7e9e4 100644 --- a/tests/common/ilname.sh +++ b/tests/common/ilname.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -ilname PATTERN +invoke_bfs -quit -ilname PATTERN || skip bfs_diff links -ilname '[AQ]' diff --git a/tests/common/iname.sh b/tests/common/iname.sh index 8fcc443..c25a646 100644 --- a/tests/common/iname.sh +++ b/tests/common/iname.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -iname PATTERN +invoke_bfs -quit -iname PATTERN || skip bfs_diff basic -iname '*F*' diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh index e35ed4e..a9e01bf 100644 --- a/tests/common/inum_bind_mount.sh +++ b/tests/common/inum_bind_mount.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$SUDO" || skip +test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{foo,bar} diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh index f9f4e2b..7cc5e40 100644 --- a/tests/common/inum_mount.sh +++ b/tests/common/inum_mount.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$SUDO" || skip +test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} diff --git a/tests/common/ipath.sh b/tests/common/ipath.sh index 9e6f8c5..7d05f31 100644 --- a/tests/common/ipath.sh +++ b/tests/common/ipath.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -ipath PATTERN +invoke_bfs -quit -ipath PATTERN || skip bfs_diff basic -ipath 'basic/*F*' diff --git a/tests/common/mount.sh b/tests/common/mount.sh index 2732a68..f077ea2 100644 --- a/tests/common/mount.sh +++ b/tests/common/mount.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$SUDO" || skip +test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} diff --git a/tests/gnu/exec_flush_fail.sh b/tests/gnu/exec_flush_fail.sh index 4772a14..ce796b2 100644 --- a/tests/gnu/exec_flush_fail.sh +++ b/tests/gnu/exec_flush_fail.sh @@ -1,3 +1,3 @@ # Failure to flush streams before exec should be caught -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic -print0 -exec true \; >/dev/full diff --git a/tests/gnu/exec_plus_flush_fail.sh b/tests/gnu/exec_plus_flush_fail.sh index 5c74fd8..8beee09 100644 --- a/tests/gnu/exec_plus_flush_fail.sh +++ b/tests/gnu/exec_plus_flush_fail.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full diff --git a/tests/gnu/fprint_error.sh b/tests/gnu/fprint_error.sh index e7f2394..0e75b0e 100644 --- a/tests/gnu/fprint_error.sh +++ b/tests/gnu/fprint_error.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic -maxdepth 0 -fprint /dev/full diff --git a/tests/gnu/fprint_noerror.sh b/tests/gnu/fprint_noerror.sh index 142e935..f13a62b 100644 --- a/tests/gnu/fprint_noerror.sh +++ b/tests/gnu/fprint_noerror.sh @@ -1,3 +1,3 @@ # Regression test: /dev/full should not fail until actually written to -skip_unless test -e /dev/full +test -e /dev/full || skip invoke_bfs basic -false -fprint /dev/full diff --git a/tests/gnu/fstype.sh b/tests/gnu/fstype.sh index 939438e..05645c3 100644 --- a/tests/gnu/fstype.sh +++ b/tests/gnu/fstype.sh @@ -1,3 +1,2 @@ -fstype=$(invoke_bfs basic -maxdepth 0 -printf '%F\n') -skip_if test $? -ne 0 +fstype=$(invoke_bfs basic -maxdepth 0 -printf '%F\n') || skip bfs_diff basic -fstype "$fstype" diff --git a/tests/gnu/gid_plus.sh b/tests/gnu/gid_plus.sh index 8ad493b..ccba0e6 100644 --- a/tests/gnu/gid_plus.sh +++ b/tests/gnu/gid_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -g)" -eq 0 +test "$(id -g)" -eq 0 && skip bfs_diff basic -gid +0 diff --git a/tests/gnu/gid_plus_plus.sh b/tests/gnu/gid_plus_plus.sh index 7982633..ec7ae86 100644 --- a/tests/gnu/gid_plus_plus.sh +++ b/tests/gnu/gid_plus_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -g)" -eq 0 +test "$(id -g)" -eq 0 && skip bfs_diff basic -gid ++0 diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh index 648ea05..6bf2977 100644 --- a/tests/gnu/inum_automount.sh +++ b/tests/gnu/inum_automount.sh @@ -1,11 +1,11 @@ # bfs shouldn't trigger automounts unless it descends into them -skip_unless test "$SUDO" -skip_unless command -v systemd-mount &>/dev/null +test "$SUDO" || skip +command -v systemd-mount &>/dev/null || skip clean_scratch mkdir scratch/{foo,automnt} -skip_unless sudo systemd-mount -A -o bind basic scratch/automnt +sudo systemd-mount -A -o bind basic scratch/automnt || skip before=$(inum scratch/automnt) bfs_diff scratch -inum "$before" -prune diff --git a/tests/gnu/iwholename.sh b/tests/gnu/iwholename.sh index 67e9630..0b2d038 100644 --- a/tests/gnu/iwholename.sh +++ b/tests/gnu/iwholename.sh @@ -1,2 +1,2 @@ -skip_unless invoke_bfs -quit -iwholename PATTERN +invoke_bfs -quit -iwholename PATTERN || skip bfs_diff basic -iwholename 'basic/*F*' diff --git a/tests/gnu/print_error.sh b/tests/gnu/print_error.sh index 9fd5af5..62a32b4 100644 --- a/tests/gnu/print_error.sh +++ b/tests/gnu/print_error.sh @@ -1,2 +1,2 @@ -skip_unless test -e /dev/full +test -e /dev/full || skip fail invoke_bfs basic -maxdepth 0 >/dev/full diff --git a/tests/gnu/regex_invalid_utf8.sh b/tests/gnu/regex_invalid_utf8.sh index edb4b1e..603d688 100644 --- a/tests/gnu/regex_invalid_utf8.sh +++ b/tests/gnu/regex_invalid_utf8.sh @@ -1,8 +1,8 @@ clean_scratch # Incomplete UTF-8 sequences -skip_unless touch scratch/$'\xC3' -skip_unless touch scratch/$'\xE2\x84' -skip_unless touch scratch/$'\xF0\x9F\x92' +touch scratch/$'\xC3' || skip +touch scratch/$'\xE2\x84' || skip +touch scratch/$'\xF0\x9F\x92' || skip bfs_diff scratch -regex 'scratch/..' diff --git a/tests/gnu/regextype_emacs.sh b/tests/gnu/regextype_emacs.sh index d0f68cc..3cc388c 100644 --- a/tests/gnu/regextype_emacs.sh +++ b/tests/gnu/regextype_emacs.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs -regextype emacs -quit +invoke_bfs -regextype emacs -quit || skip bfs_diff basic -regextype emacs -regex '.*/\(f+o?o?\|bar\)' diff --git a/tests/gnu/regextype_grep.sh b/tests/gnu/regextype_grep.sh index 0136700..0830667 100644 --- a/tests/gnu/regextype_grep.sh +++ b/tests/gnu/regextype_grep.sh @@ -1,3 +1,3 @@ -skip_unless invoke_bfs -regextype grep -quit +invoke_bfs -regextype grep -quit || skip bfs_diff basic -regextype grep -regex '.*/f\+o\?o\?' diff --git a/tests/gnu/uid_plus.sh b/tests/gnu/uid_plus.sh index fc4bce3..22b2c8e 100644 --- a/tests/gnu/uid_plus.sh +++ b/tests/gnu/uid_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -u)" -eq 0 +test "$(id -u)" -eq 0 && skip bfs_diff basic -uid +0 diff --git a/tests/gnu/uid_plus_plus.sh b/tests/gnu/uid_plus_plus.sh index 5d5e086..e021888 100644 --- a/tests/gnu/uid_plus_plus.sh +++ b/tests/gnu/uid_plus_plus.sh @@ -1,2 +1,2 @@ -skip_if test "$(id -u)" -eq 0 +test "$(id -u)" -eq 0 && skip bfs_diff basic -uid ++0 diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh index 264b6f8..a6dbed3 100644 --- a/tests/gnu/xtype_bind_mount.sh +++ b/tests/gnu/xtype_bind_mount.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$SUDO" || skip +test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{file,null} diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh index ddbadd8..2fc99dd 100644 --- a/tests/posix/L_xdev.sh +++ b/tests/posix/L_xdev.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$SUDO" || skip +test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} diff --git a/tests/posix/name_bracket.sh b/tests/posix/name_bracket.sh index 84a417f..80ca186 100644 --- a/tests/posix/name_bracket.sh +++ b/tests/posix/name_bracket.sh @@ -1,5 +1,5 @@ # fnmatch() is broken on macOS -skip_if test "$UNAME" = "Darwin" +test "$UNAME" = "Darwin" && skip # An unclosed [ should be matched literally bfs_diff weirdnames -name '[' diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh index ce06723..483f543 100644 --- a/tests/posix/readdir_error.sh +++ b/tests/posix/readdir_error.sh @@ -1,4 +1,4 @@ -skip_unless test "$UNAME" = "Linux" +test "$UNAME" = "Linux" || skip clean_scratch mkfifo scratch/{fever,pid,wait,running} @@ -33,5 +33,5 @@ while [ "$state" != "Z" ]; do done # On Linux, open(/proc/$pid/net) will succeed but readdir() will fail -skip_unless test -r "/proc/$pid/net" +test -r "/proc/$pid/net" || skip fail invoke_bfs "/proc/$pid/net" >/dev/null diff --git a/tests/posix/type_bind_mount.sh b/tests/posix/type_bind_mount.sh index 445f6ef..fe32875 100644 --- a/tests/posix/type_bind_mount.sh +++ b/tests/posix/type_bind_mount.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_unless test "$UNAME" = "Linux" +test "$SUDO" || skip +test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{file,null} diff --git a/tests/posix/xdev.sh b/tests/posix/xdev.sh index 4591940..44e04dd 100644 --- a/tests/posix/xdev.sh +++ b/tests/posix/xdev.sh @@ -1,5 +1,5 @@ -skip_unless test "$SUDO" -skip_if test "$UNAME" = "Darwin" +test "$SUDO" || skip +test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} diff --git a/tests/tests.sh b/tests/tests.sh index 3fdf49b..3047bf4 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -543,16 +543,6 @@ function skip() { exit $EX_SKIP } -function skip_if() { - if "$@"; then - skip - fi -} - -function skip_unless() { - skip_if fail "$@" -} - function closefrom() { if [ -d /proc/self/fd ]; then local fds=/proc/self/fd -- cgit v1.2.3 From 7d87b96b421b76e387cee903b7b7c1bc16c54310 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 14:39:18 -0500 Subject: tests: Move crash detection into invoke_bfs, use ! instead of fail --- tests/bfs/D_incomplete.sh | 2 +- tests/bfs/and_incomplete.sh | 2 +- tests/bfs/comma_incomplete.sh | 2 +- tests/bfs/exclude_exclude.sh | 2 +- tests/bfs/exclude_print.sh | 2 +- tests/bfs/exec_flush_fprint_fail.sh | 2 +- tests/bfs/fprint_error_stderr.sh | 2 +- tests/bfs/fprint_error_stdout.sh | 2 +- tests/bfs/high_byte.sh | 2 +- tests/bfs/links_empty.sh | 2 +- tests/bfs/links_invalid.sh | 2 +- tests/bfs/links_negative.sh | 2 +- tests/bfs/links_noarg.sh | 2 +- tests/bfs/newerma_nonexistent.sh | 2 +- tests/bfs/newermq.sh | 2 +- tests/bfs/newermt_invalid.sh | 2 +- tests/bfs/newerqm.sh | 2 +- tests/bfs/or_incomplete.sh | 2 +- tests/bfs/perm_symbolic_double_comma.sh | 2 +- tests/bfs/perm_symbolic_missing_action.sh | 2 +- tests/bfs/perm_symbolic_trailing_comma.sh | 2 +- tests/bfs/printf_duplicate_flag.sh | 2 +- tests/bfs/printf_everything.sh | 4 ++-- tests/bfs/printf_incomplete_escape.sh | 2 +- tests/bfs/printf_incomplete_format.sh | 2 +- tests/bfs/printf_invalid_escape.sh | 2 +- tests/bfs/printf_invalid_format.sh | 2 +- tests/bfs/printf_must_be_numeric.sh | 2 +- tests/bfs/stderr_fails_loudly.sh | 2 +- tests/bfs/unexpected_operator.sh | 2 +- tests/bfs/xtype_depth.sh | 2 +- tests/bsd/f_incomplete.sh | 2 +- tests/bsd/mtime_bad_unit.sh | 2 +- tests/bsd/mtime_missing_unit.sh | 2 +- tests/common/maxdepth_incomplete.sh | 2 +- tests/common/mindepth_incomplete.sh | 2 +- tests/gnu/exec_flush_fail.sh | 2 +- tests/gnu/exec_nothing.sh | 2 +- tests/gnu/exec_plus_flush_fail.sh | 2 +- tests/gnu/files0_from_empty.sh | 2 +- tests/gnu/files0_from_error.sh | 2 +- tests/gnu/files0_from_none.sh | 2 +- tests/gnu/files0_from_nothing.sh | 2 +- tests/gnu/files0_from_nowhere.sh | 2 +- tests/gnu/files0_from_ok.sh | 2 +- tests/gnu/fls_nonexistent.sh | 2 +- tests/gnu/fprint0_nonexistent.sh | 2 +- tests/gnu/fprint_error.sh | 2 +- tests/gnu/fprint_noarg.sh | 2 +- tests/gnu/fprint_nonexistent.sh | 2 +- tests/gnu/fprintf_nofile.sh | 2 +- tests/gnu/fprintf_noformat.sh | 2 +- tests/gnu/fprintf_nonexistent.sh | 2 +- tests/gnu/ignore_readdir_race_root.sh | 2 +- tests/gnu/ok_nothing.sh | 2 +- tests/gnu/print_error.sh | 2 +- tests/gnu/regex_error.sh | 2 +- tests/posix/closed_stderr.sh | 2 +- tests/posix/closed_stdout.sh | 2 +- tests/posix/exec_plus_nothing.sh | 2 +- tests/posix/extra_paren.sh | 2 +- tests/posix/incomplete.sh | 2 +- tests/posix/missing_paren.sh | 2 +- tests/posix/newer_nonexistent.sh | 2 +- tests/posix/ok_plus_nothing.sh | 2 +- tests/posix/readdir_error.sh | 2 +- tests/tests.sh | 16 +++++----------- 67 files changed, 72 insertions(+), 78 deletions(-) (limited to 'tests') diff --git a/tests/bfs/D_incomplete.sh b/tests/bfs/D_incomplete.sh index 396d365..30c522a 100644 --- a/tests/bfs/D_incomplete.sh +++ b/tests/bfs/D_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -D +! invoke_bfs -D diff --git a/tests/bfs/and_incomplete.sh b/tests/bfs/and_incomplete.sh index f7bc2c3..05abc2d 100644 --- a/tests/bfs/and_incomplete.sh +++ b/tests/bfs/and_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -print -a +! invoke_bfs -print -a diff --git a/tests/bfs/comma_incomplete.sh b/tests/bfs/comma_incomplete.sh index 07cf505..bd60168 100644 --- a/tests/bfs/comma_incomplete.sh +++ b/tests/bfs/comma_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -print , +! invoke_bfs -print , diff --git a/tests/bfs/exclude_exclude.sh b/tests/bfs/exclude_exclude.sh index c687623..739342f 100644 --- a/tests/bfs/exclude_exclude.sh +++ b/tests/bfs/exclude_exclude.sh @@ -1 +1 @@ -fail invoke_bfs basic -exclude -exclude -name foo +! invoke_bfs basic -exclude -exclude -name foo diff --git a/tests/bfs/exclude_print.sh b/tests/bfs/exclude_print.sh index 52ff0fd..dc89e1d 100644 --- a/tests/bfs/exclude_print.sh +++ b/tests/bfs/exclude_print.sh @@ -1 +1 @@ -fail invoke_bfs basic -exclude -print +! invoke_bfs basic -exclude -print diff --git a/tests/bfs/exec_flush_fprint_fail.sh b/tests/bfs/exec_flush_fprint_fail.sh index b58624a..cd38e41 100644 --- a/tests/bfs/exec_flush_fprint_fail.sh +++ b/tests/bfs/exec_flush_fprint_fail.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs basic/a -fprint /dev/full -exec true \; +! invoke_bfs basic/a -fprint /dev/full -exec true \; diff --git a/tests/bfs/fprint_error_stderr.sh b/tests/bfs/fprint_error_stderr.sh index e51d026..2cc4037 100644 --- a/tests/bfs/fprint_error_stderr.sh +++ b/tests/bfs/fprint_error_stderr.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full +! invoke_bfs basic -maxdepth 0 -fprint /dev/full 2>/dev/full diff --git a/tests/bfs/fprint_error_stdout.sh b/tests/bfs/fprint_error_stdout.sh index 6aa4b11..42a7b36 100644 --- a/tests/bfs/fprint_error_stdout.sh +++ b/tests/bfs/fprint_error_stdout.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full +! invoke_bfs basic -maxdepth 0 -fprint /dev/full >/dev/full diff --git a/tests/bfs/high_byte.sh b/tests/bfs/high_byte.sh index 222f24b..c76199f 100644 --- a/tests/bfs/high_byte.sh +++ b/tests/bfs/high_byte.sh @@ -1 +1 @@ -fail invoke_bfs -$'\xFF' +! invoke_bfs -$'\xFF' diff --git a/tests/bfs/links_empty.sh b/tests/bfs/links_empty.sh index 34c7c25..42cf6e5 100644 --- a/tests/bfs/links_empty.sh +++ b/tests/bfs/links_empty.sh @@ -1 +1 @@ -fail invoke_bfs links -links '' +! invoke_bfs links -links '' diff --git a/tests/bfs/links_invalid.sh b/tests/bfs/links_invalid.sh index ff69fa6..4d139c9 100644 --- a/tests/bfs/links_invalid.sh +++ b/tests/bfs/links_invalid.sh @@ -1 +1 @@ -fail invoke_bfs links -links ASDF +! invoke_bfs links -links ASDF diff --git a/tests/bfs/links_negative.sh b/tests/bfs/links_negative.sh index b5d9c58..e664b99 100644 --- a/tests/bfs/links_negative.sh +++ b/tests/bfs/links_negative.sh @@ -1 +1 @@ -fail invoke_bfs links -links +-1 +! invoke_bfs links -links +-1 diff --git a/tests/bfs/links_noarg.sh b/tests/bfs/links_noarg.sh index 5dede5f..5c948dc 100644 --- a/tests/bfs/links_noarg.sh +++ b/tests/bfs/links_noarg.sh @@ -1 +1 @@ -fail invoke_bfs links -links +! invoke_bfs links -links diff --git a/tests/bfs/newerma_nonexistent.sh b/tests/bfs/newerma_nonexistent.sh index 7f3695f..cdedb4a 100644 --- a/tests/bfs/newerma_nonexistent.sh +++ b/tests/bfs/newerma_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs times -newerma basic/nonexistent +! invoke_bfs times -newerma basic/nonexistent diff --git a/tests/bfs/newermq.sh b/tests/bfs/newermq.sh index 2a22586..2f705dc 100644 --- a/tests/bfs/newermq.sh +++ b/tests/bfs/newermq.sh @@ -1 +1 @@ -fail invoke_bfs times -newermq times/a +! invoke_bfs times -newermq times/a diff --git a/tests/bfs/newermt_invalid.sh b/tests/bfs/newermt_invalid.sh index 61d2485..98efece 100644 --- a/tests/bfs/newermt_invalid.sh +++ b/tests/bfs/newermt_invalid.sh @@ -1 +1 @@ -fail invoke_bfs times -newermt not_a_date_time +! invoke_bfs times -newermt not_a_date_time diff --git a/tests/bfs/newerqm.sh b/tests/bfs/newerqm.sh index da84350..c0cff98 100644 --- a/tests/bfs/newerqm.sh +++ b/tests/bfs/newerqm.sh @@ -1 +1 @@ -fail invoke_bfs times -newerqm times/a +! invoke_bfs times -newerqm times/a diff --git a/tests/bfs/or_incomplete.sh b/tests/bfs/or_incomplete.sh index c941b95..4af31b6 100644 --- a/tests/bfs/or_incomplete.sh +++ b/tests/bfs/or_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs -print -o +! invoke_bfs -print -o diff --git a/tests/bfs/perm_symbolic_double_comma.sh b/tests/bfs/perm_symbolic_double_comma.sh index 66db0ac..48f9d4b 100644 --- a/tests/bfs/perm_symbolic_double_comma.sh +++ b/tests/bfs/perm_symbolic_double_comma.sh @@ -1 +1 @@ -fail invoke_bfs perms -perm a+r,,u+w +! invoke_bfs perms -perm a+r,,u+w diff --git a/tests/bfs/perm_symbolic_missing_action.sh b/tests/bfs/perm_symbolic_missing_action.sh index 3b18721..28446ab 100644 --- a/tests/bfs/perm_symbolic_missing_action.sh +++ b/tests/bfs/perm_symbolic_missing_action.sh @@ -1 +1 @@ -fail invoke_bfs perms -perm a +! invoke_bfs perms -perm a diff --git a/tests/bfs/perm_symbolic_trailing_comma.sh b/tests/bfs/perm_symbolic_trailing_comma.sh index c52ebe6..01bbc16 100644 --- a/tests/bfs/perm_symbolic_trailing_comma.sh +++ b/tests/bfs/perm_symbolic_trailing_comma.sh @@ -1 +1 @@ -fail invoke_bfs perms -perm a+r, +! invoke_bfs perms -perm a+r, diff --git a/tests/bfs/printf_duplicate_flag.sh b/tests/bfs/printf_duplicate_flag.sh index 77650d0..5ff29f1 100644 --- a/tests/bfs/printf_duplicate_flag.sh +++ b/tests/bfs/printf_duplicate_flag.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%--p' +! invoke_bfs basic -printf '%--p' diff --git a/tests/bfs/printf_everything.sh b/tests/bfs/printf_everything.sh index 5f20718..5e95830 100644 --- a/tests/bfs/printf_everything.sh +++ b/tests/bfs/printf_everything.sh @@ -1,14 +1,14 @@ everything=(%{a,b,c,d,D,f,g,G,h,H,i,k,l,m,M,n,p,P,s,S,t,u,U,y,Y}) # Check if we have fstypes -if ! fail invoke_bfs basic -printf '%F' -quit >/dev/null; then +if ! ! invoke_bfs basic -printf '%F' -quit >/dev/null; then everything+=(%F) fi everything+=(%{A,C,T}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z}) # Check if we have birth times -if ! fail invoke_bfs basic -printf '%w' -quit >/dev/null; then +if ! ! invoke_bfs basic -printf '%w' -quit >/dev/null; then everything+=(%w %{B,W}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z}) fi diff --git a/tests/bfs/printf_incomplete_escape.sh b/tests/bfs/printf_incomplete_escape.sh index 144add5..f560d28 100644 --- a/tests/bfs/printf_incomplete_escape.sh +++ b/tests/bfs/printf_incomplete_escape.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '\' +! invoke_bfs basic -printf '\' diff --git a/tests/bfs/printf_incomplete_format.sh b/tests/bfs/printf_incomplete_format.sh index 347a0f4..92c6afc 100644 --- a/tests/bfs/printf_incomplete_format.sh +++ b/tests/bfs/printf_incomplete_format.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%' +! invoke_bfs basic -printf '%' diff --git a/tests/bfs/printf_invalid_escape.sh b/tests/bfs/printf_invalid_escape.sh index ce12233..4338f9b 100644 --- a/tests/bfs/printf_invalid_escape.sh +++ b/tests/bfs/printf_invalid_escape.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '\!' +! invoke_bfs basic -printf '\!' diff --git a/tests/bfs/printf_invalid_format.sh b/tests/bfs/printf_invalid_format.sh index 1717615..59d63a7 100644 --- a/tests/bfs/printf_invalid_format.sh +++ b/tests/bfs/printf_invalid_format.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%!' +! invoke_bfs basic -printf '%!' diff --git a/tests/bfs/printf_must_be_numeric.sh b/tests/bfs/printf_must_be_numeric.sh index eabb3d6..7c7c3fa 100644 --- a/tests/bfs/printf_must_be_numeric.sh +++ b/tests/bfs/printf_must_be_numeric.sh @@ -1 +1 @@ -fail invoke_bfs basic -printf '%+p' +! invoke_bfs basic -printf '%+p' diff --git a/tests/bfs/stderr_fails_loudly.sh b/tests/bfs/stderr_fails_loudly.sh index c423d9e..8572d5a 100644 --- a/tests/bfs/stderr_fails_loudly.sh +++ b/tests/bfs/stderr_fails_loudly.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full +! invoke_bfs -D all basic -false -fprint /dev/full 2>/dev/full diff --git a/tests/bfs/unexpected_operator.sh b/tests/bfs/unexpected_operator.sh index b3658f6..2eb0e71 100644 --- a/tests/bfs/unexpected_operator.sh +++ b/tests/bfs/unexpected_operator.sh @@ -1 +1 @@ -fail invoke_bfs \! -o -print +! invoke_bfs \! -o -print diff --git a/tests/bfs/xtype_depth.sh b/tests/bfs/xtype_depth.sh index cd478af..02c8173 100644 --- a/tests/bfs/xtype_depth.sh +++ b/tests/bfs/xtype_depth.sh @@ -1,2 +1,2 @@ # Make sure -xtype is considered side-effecting for facts_when_impure -fail invoke_bfs loops -xtype l -depth 100 +! invoke_bfs loops -xtype l -depth 100 diff --git a/tests/bsd/f_incomplete.sh b/tests/bsd/f_incomplete.sh index acb63af..50afe42 100644 --- a/tests/bsd/f_incomplete.sh +++ b/tests/bsd/f_incomplete.sh @@ -1,2 +1,2 @@ -fail invoke_bfs -f +! invoke_bfs -f diff --git a/tests/bsd/mtime_bad_unit.sh b/tests/bsd/mtime_bad_unit.sh index 3921f80..6e2caf1 100644 --- a/tests/bsd/mtime_bad_unit.sh +++ b/tests/bsd/mtime_bad_unit.sh @@ -1 +1 @@ -fail invoke_bfs times -mtime +1q +! invoke_bfs times -mtime +1q diff --git a/tests/bsd/mtime_missing_unit.sh b/tests/bsd/mtime_missing_unit.sh index 3ac4c97..f6b1f93 100644 --- a/tests/bsd/mtime_missing_unit.sh +++ b/tests/bsd/mtime_missing_unit.sh @@ -1 +1 @@ -fail invoke_bfs times -mtime +1w2 +! invoke_bfs times -mtime +1w2 diff --git a/tests/common/maxdepth_incomplete.sh b/tests/common/maxdepth_incomplete.sh index 536dcf5..0bcb461 100644 --- a/tests/common/maxdepth_incomplete.sh +++ b/tests/common/maxdepth_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs basic -maxdepth +! invoke_bfs basic -maxdepth diff --git a/tests/common/mindepth_incomplete.sh b/tests/common/mindepth_incomplete.sh index 19a3b21..6f55a42 100644 --- a/tests/common/mindepth_incomplete.sh +++ b/tests/common/mindepth_incomplete.sh @@ -1 +1 @@ -fail invoke_bfs basic -mindepth +! invoke_bfs basic -mindepth diff --git a/tests/gnu/exec_flush_fail.sh b/tests/gnu/exec_flush_fail.sh index ce796b2..5505f7a 100644 --- a/tests/gnu/exec_flush_fail.sh +++ b/tests/gnu/exec_flush_fail.sh @@ -1,3 +1,3 @@ # Failure to flush streams before exec should be caught test -e /dev/full || skip -fail invoke_bfs basic -print0 -exec true \; >/dev/full +! invoke_bfs basic -print0 -exec true \; >/dev/full diff --git a/tests/gnu/exec_nothing.sh b/tests/gnu/exec_nothing.sh index 9d613e8..443aa0d 100644 --- a/tests/gnu/exec_nothing.sh +++ b/tests/gnu/exec_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't segfault on missing command -fail invoke_bfs basic -exec \; +! invoke_bfs basic -exec \; diff --git a/tests/gnu/exec_plus_flush_fail.sh b/tests/gnu/exec_plus_flush_fail.sh index 8beee09..53a50e5 100644 --- a/tests/gnu/exec_plus_flush_fail.sh +++ b/tests/gnu/exec_plus_flush_fail.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full +! invoke_bfs basic/a -print0 -exec echo found {} + >/dev/full diff --git a/tests/gnu/files0_from_empty.sh b/tests/gnu/files0_from_empty.sh index bd4fbf4..85eee8f 100644 --- a/tests/gnu/files0_from_empty.sh +++ b/tests/gnu/files0_from_empty.sh @@ -1 +1 @@ -printf "\0" | fail invoke_bfs -files0-from - +! printf "\0" | invoke_bfs -files0-from - diff --git a/tests/gnu/files0_from_error.sh b/tests/gnu/files0_from_error.sh index ab27ea2..1515d0b 100644 --- a/tests/gnu/files0_from_error.sh +++ b/tests/gnu/files0_from_error.sh @@ -1 +1 @@ -fail invoke_bfs -files0-from basic +! invoke_bfs -files0-from basic diff --git a/tests/gnu/files0_from_none.sh b/tests/gnu/files0_from_none.sh index c6e5b97..090fce0 100644 --- a/tests/gnu/files0_from_none.sh +++ b/tests/gnu/files0_from_none.sh @@ -1 +1 @@ -printf "" | fail invoke_bfs -files0-from - +! printf "" | invoke_bfs -files0-from - diff --git a/tests/gnu/files0_from_nothing.sh b/tests/gnu/files0_from_nothing.sh index 5fdae60..fee50a8 100644 --- a/tests/gnu/files0_from_nothing.sh +++ b/tests/gnu/files0_from_nothing.sh @@ -1 +1 @@ -fail invoke_bfs -files0-from basic/nonexistent +! invoke_bfs -files0-from basic/nonexistent diff --git a/tests/gnu/files0_from_nowhere.sh b/tests/gnu/files0_from_nowhere.sh index 2337613..68eea4b 100644 --- a/tests/gnu/files0_from_nowhere.sh +++ b/tests/gnu/files0_from_nowhere.sh @@ -1 +1 @@ -fail invoke_bfs -files0-from +! invoke_bfs -files0-from diff --git a/tests/gnu/files0_from_ok.sh b/tests/gnu/files0_from_ok.sh index 5387e5c..8e145ce 100644 --- a/tests/gnu/files0_from_ok.sh +++ b/tests/gnu/files0_from_ok.sh @@ -1 +1 @@ -printf "basic\0" | fail invoke_bfs -files0-from - -ok echo {} \; +! printf "basic\0" | invoke_bfs -files0-from - -ok echo {} \; diff --git a/tests/gnu/fls_nonexistent.sh b/tests/gnu/fls_nonexistent.sh index 4756834..ff86763 100644 --- a/tests/gnu/fls_nonexistent.sh +++ b/tests/gnu/fls_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs rainbow -fls scratch/nonexistent/path +! invoke_bfs rainbow -fls scratch/nonexistent/path diff --git a/tests/gnu/fprint0_nonexistent.sh b/tests/gnu/fprint0_nonexistent.sh index d8e0f30..ec14c2d 100644 --- a/tests/gnu/fprint0_nonexistent.sh +++ b/tests/gnu/fprint0_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprint0 scratch/nonexistent/path +! invoke_bfs basic -fprint0 scratch/nonexistent/path diff --git a/tests/gnu/fprint_error.sh b/tests/gnu/fprint_error.sh index 0e75b0e..7617034 100644 --- a/tests/gnu/fprint_error.sh +++ b/tests/gnu/fprint_error.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs basic -maxdepth 0 -fprint /dev/full +! invoke_bfs basic -maxdepth 0 -fprint /dev/full diff --git a/tests/gnu/fprint_noarg.sh b/tests/gnu/fprint_noarg.sh index bf772f3..8511649 100644 --- a/tests/gnu/fprint_noarg.sh +++ b/tests/gnu/fprint_noarg.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprint +! invoke_bfs basic -fprint diff --git a/tests/gnu/fprint_nonexistent.sh b/tests/gnu/fprint_nonexistent.sh index b6dac8a..4409162 100644 --- a/tests/gnu/fprint_nonexistent.sh +++ b/tests/gnu/fprint_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprint scratch/nonexistent/path +! invoke_bfs basic -fprint scratch/nonexistent/path diff --git a/tests/gnu/fprintf_nofile.sh b/tests/gnu/fprintf_nofile.sh index c2c48a5..4e79002 100644 --- a/tests/gnu/fprintf_nofile.sh +++ b/tests/gnu/fprintf_nofile.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprintf +! invoke_bfs basic -fprintf diff --git a/tests/gnu/fprintf_noformat.sh b/tests/gnu/fprintf_noformat.sh index 0d285c3..fd97f4c 100644 --- a/tests/gnu/fprintf_noformat.sh +++ b/tests/gnu/fprintf_noformat.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprintf /dev/null +! invoke_bfs basic -fprintf /dev/null diff --git a/tests/gnu/fprintf_nonexistent.sh b/tests/gnu/fprintf_nonexistent.sh index 6ed141a..160e739 100644 --- a/tests/gnu/fprintf_nonexistent.sh +++ b/tests/gnu/fprintf_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs basic -fprintf scratch/nonexistent/path '%p\n' +! invoke_bfs basic -fprintf scratch/nonexistent/path '%p\n' diff --git a/tests/gnu/ignore_readdir_race_root.sh b/tests/gnu/ignore_readdir_race_root.sh index bad7391..dc41e7f 100644 --- a/tests/gnu/ignore_readdir_race_root.sh +++ b/tests/gnu/ignore_readdir_race_root.sh @@ -1,2 +1,2 @@ # Make sure -ignore_readdir_race doesn't suppress ENOENT at the root -fail invoke_bfs basic/nonexistent -ignore_readdir_race +! invoke_bfs basic/nonexistent -ignore_readdir_race diff --git a/tests/gnu/ok_nothing.sh b/tests/gnu/ok_nothing.sh index 439687b..52c3547 100644 --- a/tests/gnu/ok_nothing.sh +++ b/tests/gnu/ok_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't segfault on missing command -fail invoke_bfs basic -ok \; +! invoke_bfs basic -ok \; diff --git a/tests/gnu/print_error.sh b/tests/gnu/print_error.sh index 62a32b4..bc79637 100644 --- a/tests/gnu/print_error.sh +++ b/tests/gnu/print_error.sh @@ -1,2 +1,2 @@ test -e /dev/full || skip -fail invoke_bfs basic -maxdepth 0 >/dev/full +! invoke_bfs basic -maxdepth 0 >/dev/full diff --git a/tests/gnu/regex_error.sh b/tests/gnu/regex_error.sh index 9bd4c8d..4af933f 100644 --- a/tests/gnu/regex_error.sh +++ b/tests/gnu/regex_error.sh @@ -1 +1 @@ -fail invoke_bfs basic -regex '[' +! invoke_bfs basic -regex '[' diff --git a/tests/posix/closed_stderr.sh b/tests/posix/closed_stderr.sh index cc746ef..570d5bb 100644 --- a/tests/posix/closed_stderr.sh +++ b/tests/posix/closed_stderr.sh @@ -1 +1 @@ -fail invoke_bfs basic >&- 2>&- +! invoke_bfs basic >&- 2>&- diff --git a/tests/posix/closed_stdout.sh b/tests/posix/closed_stdout.sh index 446bf56..25c060d 100644 --- a/tests/posix/closed_stdout.sh +++ b/tests/posix/closed_stdout.sh @@ -1 +1 @@ -fail invoke_bfs basic >&- +! invoke_bfs basic >&- diff --git a/tests/posix/exec_plus_nothing.sh b/tests/posix/exec_plus_nothing.sh index ef01968..347722d 100644 --- a/tests/posix/exec_plus_nothing.sh +++ b/tests/posix/exec_plus_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't look OOB for {} + -fail invoke_bfs basic -exec + +! invoke_bfs basic -exec + diff --git a/tests/posix/extra_paren.sh b/tests/posix/extra_paren.sh index cd8e8f8..d15022f 100644 --- a/tests/posix/extra_paren.sh +++ b/tests/posix/extra_paren.sh @@ -1 +1 @@ -fail invoke_bfs basic -print \) +! invoke_bfs basic -print \) diff --git a/tests/posix/incomplete.sh b/tests/posix/incomplete.sh index 07b1b61..bca5a13 100644 --- a/tests/posix/incomplete.sh +++ b/tests/posix/incomplete.sh @@ -1 +1 @@ -fail invoke_bfs basic \( +! invoke_bfs basic \( diff --git a/tests/posix/missing_paren.sh b/tests/posix/missing_paren.sh index ac8dd60..d906fbe 100644 --- a/tests/posix/missing_paren.sh +++ b/tests/posix/missing_paren.sh @@ -1 +1 @@ -fail invoke_bfs basic \( -print +! invoke_bfs basic \( -print diff --git a/tests/posix/newer_nonexistent.sh b/tests/posix/newer_nonexistent.sh index 789cadf..5f2da4b 100644 --- a/tests/posix/newer_nonexistent.sh +++ b/tests/posix/newer_nonexistent.sh @@ -1 +1 @@ -fail invoke_bfs times -newer times/nonexistent +! invoke_bfs times -newer times/nonexistent diff --git a/tests/posix/ok_plus_nothing.sh b/tests/posix/ok_plus_nothing.sh index 7cb7de5..77c7644 100644 --- a/tests/posix/ok_plus_nothing.sh +++ b/tests/posix/ok_plus_nothing.sh @@ -1,2 +1,2 @@ # Regression test: don't look OOB for {} + -fail invoke_bfs basic -ok + +! invoke_bfs basic -ok + diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh index 483f543..e45ec5c 100644 --- a/tests/posix/readdir_error.sh +++ b/tests/posix/readdir_error.sh @@ -34,4 +34,4 @@ done # On Linux, open(/proc/$pid/net) will succeed but readdir() will fail test -r "/proc/$pid/net" || skip -fail invoke_bfs "/proc/$pid/net" >/dev/null +! invoke_bfs "/proc/$pid/net" >/dev/null diff --git a/tests/tests.sh b/tests/tests.sh index 3047bf4..844de5c 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -476,19 +476,13 @@ function bfs_verbose() { function invoke_bfs() { bfs_verbose "$@" "${BFS[@]}" "$@" -} - -# Expect a command to fail, but not crash -function fail() { - "$@" - local STATUS="$?" + local status=$? - if ((STATUS > 125)); then - exit "$STATUS" - elif ((STATUS > 0)); then - return 0 + # Allow bfs to fail, but not crash + if ((status > 125)); then + exit $status else - return 1 + return $status fi } -- cgit v1.2.3 From e50c19f284dad6b4b7b79f91cc8576a97626be8a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 15:32:04 -0500 Subject: tests: Turn on set -e --- tests/bfs/D_unknown.sh | 2 +- tests/bfs/O9.sh | 2 +- tests/bfs/execdir_plus_nonexistent.sh | 7 +++---- tests/bfs/help.sh | 10 ++++------ tests/bsd/X.sh | 3 +-- tests/bsd/exit.sh | 10 ++-------- tests/common/L_mount.sh | 7 +++---- tests/common/execdir_nonexistent.sh | 7 +++---- tests/common/inum_bind_mount.sh | 6 ++---- tests/common/inum_mount.sh | 6 ++---- tests/common/mount.sh | 7 +++---- tests/gnu/L_delete.sh | 2 +- tests/gnu/L_loops_continue.sh | 3 +-- tests/gnu/inum_automount.sh | 8 +++----- tests/gnu/printf_Y_error.sh | 10 +++------- tests/gnu/xtype_bind_mount.sh | 8 +++----- tests/posix/L_loops.sh | 2 +- tests/posix/L_xdev.sh | 7 +++---- tests/posix/depth_error.sh | 10 +++------- tests/posix/exec_nonexistent.sh | 7 +++---- tests/posix/exec_plus_nonexistent.sh | 7 +++---- tests/posix/exec_plus_status.sh | 3 +-- tests/posix/readdir_error.sh | 2 +- tests/posix/type_bind_mount.sh | 6 ++---- tests/posix/xdev.sh | 7 +++---- tests/tests.sh | 18 +++++++++++++----- 26 files changed, 69 insertions(+), 98 deletions(-) (limited to 'tests') diff --git a/tests/bfs/D_unknown.sh b/tests/bfs/D_unknown.sh index e3614ba..cac9bd9 100644 --- a/tests/bfs/D_unknown.sh +++ b/tests/bfs/D_unknown.sh @@ -1,4 +1,4 @@ stderr=$(invoke_bfs -warn -D unknown basic 2>&1 >"$OUT") -[ -n "$stderr" ] || return 1 +[ -n "$stderr" ] sort_output diff_output diff --git a/tests/bfs/O9.sh b/tests/bfs/O9.sh index 12f6c2d..c12a7a3 100644 --- a/tests/bfs/O9.sh +++ b/tests/bfs/O9.sh @@ -1,4 +1,4 @@ stderr=$(invoke_bfs -warn -O9 basic 2>&1 >"$OUT") -[ -n "$stderr" ] || return 1 +[ -n "$stderr" ] sort_output diff_output diff --git a/tests/bfs/execdir_plus_nonexistent.sh b/tests/bfs/execdir_plus_nonexistent.sh index 8436953..88f3e90 100644 --- a/tests/bfs/execdir_plus_nonexistent.sh +++ b/tests/bfs/execdir_plus_nonexistent.sh @@ -1,5 +1,4 @@ -stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 +! stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null) +[ -n "$stderr" ] -bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print -(($? == EX_BFS)) +check_exit $EX_BFS bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print diff --git a/tests/bfs/help.sh b/tests/bfs/help.sh index 5e5c684..2c0b28a 100644 --- a/tests/bfs/help.sh +++ b/tests/bfs/help.sh @@ -1,6 +1,4 @@ -invoke_bfs -help | grep -E '\{...?\}' && return 1 -invoke_bfs -D help | grep -E '\{...?\}' && return 1 -invoke_bfs -S help | grep -E '\{...?\}' && return 1 -invoke_bfs -regextype help | grep -E '\{...?\}' && return 1 - -return 0 +! invoke_bfs -help | grep -E '\{...?\}' +! invoke_bfs -D help | grep -E '\{...?\}' +! invoke_bfs -S help | grep -E '\{...?\}' +! invoke_bfs -regextype help | grep -E '\{...?\}' diff --git a/tests/bsd/X.sh b/tests/bsd/X.sh index 03d9eee..df9a261 100644 --- a/tests/bsd/X.sh +++ b/tests/bsd/X.sh @@ -1,2 +1 @@ -bfs_diff -X weirdnames -[ $? -eq $EX_BFS ] +check_exit $EX_BFS bfs_diff -X weirdnames diff --git a/tests/bsd/exit.sh b/tests/bsd/exit.sh index 524a75f..248349c 100644 --- a/tests/bsd/exit.sh +++ b/tests/bsd/exit.sh @@ -1,11 +1,5 @@ -invoke_bfs basic -name foo -exit 42 -if [ $? -ne 42 ]; then - return 1 -fi +check_exit 42 invoke_bfs basic -name foo -exit 42 -invoke_bfs basic -name qux -exit 42 -if [ $? -ne 0 ]; then - return 1 -fi +check_exit 0 invoke_bfs basic -name qux -exit 42 bfs_diff basic/g -print -name g -exit diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh index 5b56762..9ab785e 100644 --- a/tests/common/L_mount.sh +++ b/tests/common/L_mount.sh @@ -3,13 +3,12 @@ test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} + sudo mount -t tmpfs tmpfs scratch/mnt +trap "sudo umount scratch/mnt" EXIT + ln -s ../mnt scratch/foo/bar "$XTOUCH" scratch/mnt/baz ln -s ../mnt/baz scratch/foo/qux bfs_diff -L scratch -mount -ret=$? - -sudo umount scratch/mnt -return $ret diff --git a/tests/common/execdir_nonexistent.sh b/tests/common/execdir_nonexistent.sh index 5d116e5..af17fe5 100644 --- a/tests/common/execdir_nonexistent.sh +++ b/tests/common/execdir_nonexistent.sh @@ -1,5 +1,4 @@ -stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 +! stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) +[ -n "$stderr" ] -bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print -(($? == EX_BFS)) +check_exit $EX_BFS bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh index a9e01bf..625ee3d 100644 --- a/tests/common/inum_bind_mount.sh +++ b/tests/common/inum_bind_mount.sh @@ -3,10 +3,8 @@ test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{foo,bar} + sudo mount --bind scratch/{foo,bar} +trap "sudo umount scratch/bar" EXIT bfs_diff scratch -inum "$(inum scratch/bar)" -ret=$? - -sudo umount scratch/bar -return $ret diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh index 7cc5e40..91d06e2 100644 --- a/tests/common/inum_mount.sh +++ b/tests/common/inum_mount.sh @@ -3,10 +3,8 @@ test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} + sudo mount -t tmpfs tmpfs scratch/mnt +trap "sudo umount scratch/mnt" EXIT bfs_diff scratch -inum "$(inum scratch/mnt)" -ret=$? - -sudo umount scratch/mnt -return $ret diff --git a/tests/common/mount.sh b/tests/common/mount.sh index f077ea2..3f3ed38 100644 --- a/tests/common/mount.sh +++ b/tests/common/mount.sh @@ -3,11 +3,10 @@ test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} + sudo mount -t tmpfs tmpfs scratch/mnt +trap "sudo umount scratch/mnt" EXIT + "$XTOUCH" scratch/foo/bar scratch/mnt/baz bfs_diff scratch -mount -ret=$? - -sudo umount scratch/mnt -return $ret diff --git a/tests/gnu/L_delete.sh b/tests/gnu/L_delete.sh index 6ec167c..8fdb12a 100644 --- a/tests/gnu/L_delete.sh +++ b/tests/gnu/L_delete.sh @@ -4,6 +4,6 @@ mkdir scratch/bar ln -s ../foo scratch/bar/baz # Don't try to rmdir() a symlink -invoke_bfs -L scratch/bar -delete || return 1 +invoke_bfs -L scratch/bar -delete bfs_diff scratch diff --git a/tests/gnu/L_loops_continue.sh b/tests/gnu/L_loops_continue.sh index 0244137..d4c95f1 100644 --- a/tests/gnu/L_loops_continue.sh +++ b/tests/gnu/L_loops_continue.sh @@ -1,2 +1 @@ -bfs_diff -L loops -[ $? -eq $EX_BFS ] +check_exit $EX_BFS bfs_diff -L loops diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh index 6bf2977..da2e3b0 100644 --- a/tests/gnu/inum_automount.sh +++ b/tests/gnu/inum_automount.sh @@ -5,13 +5,11 @@ command -v systemd-mount &>/dev/null || skip clean_scratch mkdir scratch/{foo,automnt} + sudo systemd-mount -A -o bind basic scratch/automnt || skip +trap "sudo systemd-umount scratch/automnt" EXIT before=$(inum scratch/automnt) bfs_diff scratch -inum "$before" -prune -ret=$? after=$(inum scratch/automnt) - -sudo systemd-umount scratch/automnt - -((ret == 0 && before == after)) +((before == after)) diff --git a/tests/gnu/printf_Y_error.sh b/tests/gnu/printf_Y_error.sh index 6487711..e9a2083 100644 --- a/tests/gnu/printf_Y_error.sh +++ b/tests/gnu/printf_Y_error.sh @@ -1,12 +1,8 @@ clean_scratch mkdir scratch/foo -chmod -x scratch/foo ln -s foo/bar scratch/bar -bfs_diff scratch -printf '(%p) (%l) %y %Y\n' -ret=$? - -chmod +x scratch/foo -clean_scratch +chmod -x scratch/foo +trap "chmod +x scratch/foo" EXIT -[ $ret -eq $EX_BFS ] +check_exit $EX_BFS bfs_diff scratch -printf '(%p) (%l) %y %Y\n' diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh index a6dbed3..9babd9d 100644 --- a/tests/gnu/xtype_bind_mount.sh +++ b/tests/gnu/xtype_bind_mount.sh @@ -3,11 +3,9 @@ test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{file,null} -sudo mount --bind /dev/null scratch/null ln -s /dev/null scratch/link -bfs_diff -L scratch -type c -ret=$? +sudo mount --bind /dev/null scratch/null +trap "sudo umount scratch/null" EXIT -sudo umount scratch/null -return $ret +bfs_diff -L scratch -type c diff --git a/tests/posix/L_loops.sh b/tests/posix/L_loops.sh index f737cea..1314401 100644 --- a/tests/posix/L_loops.sh +++ b/tests/posix/L_loops.sh @@ -1,4 +1,4 @@ # POSIX says it's okay to either stop or keep going on seeing a filesystem # loop, as long as a diagnostic is printed -errors=$(invoke_bfs -L loops 2>&1 >/dev/null) +! errors=$(invoke_bfs -L loops 2>&1 >/dev/null) [ -n "$errors" ] diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh index 2fc99dd..587c8bb 100644 --- a/tests/posix/L_xdev.sh +++ b/tests/posix/L_xdev.sh @@ -3,13 +3,12 @@ test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} + sudo mount -t tmpfs tmpfs scratch/mnt +trap "sudo umount scratch/mnt" EXIT + ln -s ../mnt scratch/foo/bar "$XTOUCH" scratch/mnt/baz ln -s ../mnt/baz scratch/foo/qux bfs_diff -L scratch -xdev -ret=$? - -sudo umount scratch/mnt -return $ret diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh index f770210..15cc82d 100644 --- a/tests/posix/depth_error.sh +++ b/tests/posix/depth_error.sh @@ -1,11 +1,7 @@ clean_scratch "$XTOUCH" -p scratch/foo/bar -chmod a-r scratch/foo - -bfs_diff scratch -depth -ret=$? -chmod +r scratch/foo -clean_scratch +chmod a-r scratch/foo +trap "chmod +r scratch/foo" EXIT -[ $ret -eq $EX_BFS ] +check_exit $EX_BFS bfs_diff scratch -depth diff --git a/tests/posix/exec_nonexistent.sh b/tests/posix/exec_nonexistent.sh index b4e08e0..d4ad92a 100644 --- a/tests/posix/exec_nonexistent.sh +++ b/tests/posix/exec_nonexistent.sh @@ -1,8 +1,7 @@ # Failure to execute the command should lead to an error message and # non-zero exit status. See https://unix.stackexchange.com/q/704522/56202 -stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 +! stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) +[ -n "$stderr" ] -bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print -(($? == EX_BFS)) +check_exit $EX_BFS bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print diff --git a/tests/posix/exec_plus_nonexistent.sh b/tests/posix/exec_plus_nonexistent.sh index f96099e..6c9cb8c 100644 --- a/tests/posix/exec_plus_nonexistent.sh +++ b/tests/posix/exec_plus_nonexistent.sh @@ -1,5 +1,4 @@ -stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null) -[ -n "$stderr" ] || return 1 +! stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null) +[ -n "$stderr" ] -bfs_diff basic -exec "$TESTS/nonexistent" {} + -print -(($? == EX_BFS)) +check_exit $EX_BFS bfs_diff basic -exec "$TESTS/nonexistent" {} + -print diff --git a/tests/posix/exec_plus_status.sh b/tests/posix/exec_plus_status.sh index ea9e5ef..f44062e 100644 --- a/tests/posix/exec_plus_status.sh +++ b/tests/posix/exec_plus_status.sh @@ -1,4 +1,3 @@ # -exec ... {} + should always return true, but if the command fails, bfs # should exit with a non-zero status -bfs_diff basic -exec false {} + -print -(($? == EX_BFS)) +check_exit $EX_BFS bfs_diff basic -exec false {} + -print diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh index e45ec5c..9a002a1 100644 --- a/tests/posix/readdir_error.sh +++ b/tests/posix/readdir_error.sh @@ -29,7 +29,7 @@ kill -9 "$pid" # Wait until it's really a zombie state=R while [ "$state" != "Z" ]; do - read -r _ _ state _ <"/proc/$pid/stat" || exit 1 + read -r _ _ state _ <"/proc/$pid/stat" done # On Linux, open(/proc/$pid/net) will succeed but readdir() will fail diff --git a/tests/posix/type_bind_mount.sh b/tests/posix/type_bind_mount.sh index fe32875..2d913db 100644 --- a/tests/posix/type_bind_mount.sh +++ b/tests/posix/type_bind_mount.sh @@ -3,10 +3,8 @@ test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{file,null} + sudo mount --bind /dev/null scratch/null +trap "sudo umount scratch/null" EXIT bfs_diff scratch -type c -ret=$? - -sudo umount scratch/null -return $ret diff --git a/tests/posix/xdev.sh b/tests/posix/xdev.sh index 44e04dd..9d21b14 100644 --- a/tests/posix/xdev.sh +++ b/tests/posix/xdev.sh @@ -3,11 +3,10 @@ test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} + sudo mount -t tmpfs tmpfs scratch/mnt +trap "sudo umount scratch/mnt" EXIT + "$XTOUCH" scratch/foo/bar scratch/mnt/baz bfs_diff scratch -xdev -ret=$? - -sudo umount scratch/mnt -return $ret diff --git a/tests/tests.sh b/tests/tests.sh index 844de5c..7ccd9b5 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -476,16 +476,24 @@ function bfs_verbose() { function invoke_bfs() { bfs_verbose "$@" "${BFS[@]}" "$@" - local status=$? + local status="$?" # Allow bfs to fail, but not crash if ((status > 125)); then - exit $status + exit "$status" else - return $status + return "$status" fi } +function check_exit() { + local expected="$1" + local actual="0" + shift + "$@" || actual="$?" + ((actual == expected)) +} + # Detect colored diff support if [ -t 2 ] && diff --color=always /dev/null /dev/null 2>/dev/null; then DIFF="diff --color=always" @@ -655,9 +663,9 @@ for TEST in "${TEST_CASES[@]}"; do mkdir -p "${OUT%/*}" if [ "$VERBOSE_ERRORS" ]; then - (. "$TESTS/$TEST.sh") + (set -e; . "$TESTS/$TEST.sh") else - (. "$TESTS/$TEST.sh") 2>"$TMP/stderr" + (set -e; . "$TESTS/$TEST.sh") 2>"$TMP/stderr" fi status=$? -- cgit v1.2.3 From e82a7f51d4b4e90df868e5410a0445b009b54ff2 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 15:51:59 -0500 Subject: tests: Print the skip reason for --verbose=skipped --- tests/bfs/execdir_plus.sh | 5 +---- tests/tests.sh | 13 +++++++++---- 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/tests/bfs/execdir_plus.sh b/tests/bfs/execdir_plus.sh index 9ae7764..f66b898 100644 --- a/tests/bfs/execdir_plus.sh +++ b/tests/bfs/execdir_plus.sh @@ -1,7 +1,4 @@ tree=$(invoke_bfs -D tree 2>&1 -quit) - -if [[ "$tree" == *"-S dfs"* ]]; then - skip -fi +[[ "$tree" == *"-S dfs"* ]] && skip bfs_diff basic -execdir "$TESTS/sort-args.sh" {} + diff --git a/tests/tests.sh b/tests/tests.sh index 7ccd9b5..4a95fca 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -191,7 +191,6 @@ for arg; do VERBOSE_SKIPPED=yes ;; --verbose=tests) - VERBOSE_SKIPPED=yes VERBOSE_TESTS=yes ;; --verbose) @@ -542,6 +541,15 @@ function bfs_diff() ( ) function skip() { + if [ "$VERBOSE_SKIPPED" ]; then + caller | { + read -r line file + printf "${BOL}${CYN}%s skipped!${RST} (%s)\n" "$TEST" "$(awk "NR == $line" "$file")" + } + elif [ "$VERBOSE_TESTS" ]; then + printf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" + fi + exit $EX_SKIP } @@ -673,9 +681,6 @@ for TEST in "${TEST_CASES[@]}"; do ((++passed)) elif ((status == EX_SKIP)); then ((++skipped)) - if [ "$VERBOSE_SKIPPED" ]; then - printf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" - fi else ((++failed)) [ "$VERBOSE_ERRORS" ] || cat "$TMP/stderr" >&2 -- cgit v1.2.3 From e2b540c9e2a52500b17fa1005b26b2dd5a652c09 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 16:43:25 -0500 Subject: tests: Fail early in bfs_diff if the diff fails Otherwise, propagate the exit code from bfs --- tests/bfs/execdir_plus_nonexistent.sh | 2 +- tests/bsd/X.sh | 2 +- tests/common/execdir_nonexistent.sh | 2 +- tests/gnu/L_loops_continue.sh | 2 +- tests/gnu/printf_Y_error.sh | 2 +- tests/posix/depth_error.sh | 2 +- tests/posix/exec_nonexistent.sh | 2 +- tests/posix/exec_plus_nonexistent.sh | 2 +- tests/posix/exec_plus_status.sh | 2 +- tests/tests.sh | 13 +++---------- 10 files changed, 12 insertions(+), 19 deletions(-) (limited to 'tests') diff --git a/tests/bfs/execdir_plus_nonexistent.sh b/tests/bfs/execdir_plus_nonexistent.sh index 88f3e90..e3b4d2d 100644 --- a/tests/bfs/execdir_plus_nonexistent.sh +++ b/tests/bfs/execdir_plus_nonexistent.sh @@ -1,4 +1,4 @@ ! stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null) [ -n "$stderr" ] -check_exit $EX_BFS bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print +! bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print diff --git a/tests/bsd/X.sh b/tests/bsd/X.sh index df9a261..54000cf 100644 --- a/tests/bsd/X.sh +++ b/tests/bsd/X.sh @@ -1 +1 @@ -check_exit $EX_BFS bfs_diff -X weirdnames +! bfs_diff -X weirdnames diff --git a/tests/common/execdir_nonexistent.sh b/tests/common/execdir_nonexistent.sh index af17fe5..4bb4fdb 100644 --- a/tests/common/execdir_nonexistent.sh +++ b/tests/common/execdir_nonexistent.sh @@ -1,4 +1,4 @@ ! stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) [ -n "$stderr" ] -check_exit $EX_BFS bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print +! bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print diff --git a/tests/gnu/L_loops_continue.sh b/tests/gnu/L_loops_continue.sh index d4c95f1..55aeb33 100644 --- a/tests/gnu/L_loops_continue.sh +++ b/tests/gnu/L_loops_continue.sh @@ -1 +1 @@ -check_exit $EX_BFS bfs_diff -L loops +! bfs_diff -L loops diff --git a/tests/gnu/printf_Y_error.sh b/tests/gnu/printf_Y_error.sh index e9a2083..3aa816e 100644 --- a/tests/gnu/printf_Y_error.sh +++ b/tests/gnu/printf_Y_error.sh @@ -5,4 +5,4 @@ ln -s foo/bar scratch/bar chmod -x scratch/foo trap "chmod +x scratch/foo" EXIT -check_exit $EX_BFS bfs_diff scratch -printf '(%p) (%l) %y %Y\n' +! bfs_diff scratch -printf '(%p) (%l) %y %Y\n' diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh index 15cc82d..e91fbf6 100644 --- a/tests/posix/depth_error.sh +++ b/tests/posix/depth_error.sh @@ -4,4 +4,4 @@ clean_scratch chmod a-r scratch/foo trap "chmod +r scratch/foo" EXIT -check_exit $EX_BFS bfs_diff scratch -depth +! bfs_diff scratch -depth diff --git a/tests/posix/exec_nonexistent.sh b/tests/posix/exec_nonexistent.sh index d4ad92a..901be86 100644 --- a/tests/posix/exec_nonexistent.sh +++ b/tests/posix/exec_nonexistent.sh @@ -4,4 +4,4 @@ ! stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) [ -n "$stderr" ] -check_exit $EX_BFS bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print +! bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print diff --git a/tests/posix/exec_plus_nonexistent.sh b/tests/posix/exec_plus_nonexistent.sh index 6c9cb8c..6bddc67 100644 --- a/tests/posix/exec_plus_nonexistent.sh +++ b/tests/posix/exec_plus_nonexistent.sh @@ -1,4 +1,4 @@ ! stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null) [ -n "$stderr" ] -check_exit $EX_BFS bfs_diff basic -exec "$TESTS/nonexistent" {} + -print +! bfs_diff basic -exec "$TESTS/nonexistent" {} + -print diff --git a/tests/posix/exec_plus_status.sh b/tests/posix/exec_plus_status.sh index f44062e..a814c4e 100644 --- a/tests/posix/exec_plus_status.sh +++ b/tests/posix/exec_plus_status.sh @@ -1,3 +1,3 @@ # -exec ... {} + should always return true, but if the command fails, bfs # should exit with a non-zero status -check_exit $EX_BFS bfs_diff basic -exec false {} + -print +! bfs_diff basic -exec false {} + -print diff --git a/tests/tests.sh b/tests/tests.sh index 4a95fca..9bf9b8b 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -500,8 +500,6 @@ else DIFF="diff" fi -# Return value when bfs fails -EX_BFS=10 # Return value when a difference is detected EX_DIFF=20 # Return value when a test is skipped @@ -529,15 +527,10 @@ function bfs_diff() ( exec 3>&- "${BFS[@]}" "$@" | sort >"$OUT" - local STATUS="${PIPESTATUS[0]}" + local status="${PIPESTATUS[0]}" - diff_output || return $EX_DIFF - - if [ "$STATUS" -eq 0 ]; then - return 0 - else - return $EX_BFS - fi + diff_output || exit $EX_DIFF + return "$status" ) function skip() { -- cgit v1.2.3 From d056ec6d2d4728b695cc146c5119b251e8234393 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 16:46:06 -0500 Subject: tests: Save test stderr to separate files --- tests/tests.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 9bf9b8b..962a2cf 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -666,7 +666,7 @@ for TEST in "${TEST_CASES[@]}"; do if [ "$VERBOSE_ERRORS" ]; then (set -e; . "$TESTS/$TEST.sh") else - (set -e; . "$TESTS/$TEST.sh") 2>"$TMP/stderr" + (set -e; . "$TESTS/$TEST.sh") 2>"$TMP/$TEST.err" fi status=$? @@ -676,7 +676,7 @@ for TEST in "${TEST_CASES[@]}"; do ((++skipped)) else ((++failed)) - [ "$VERBOSE_ERRORS" ] || cat "$TMP/stderr" >&2 + [ "$VERBOSE_ERRORS" ] || cat "$TMP/$TEST.err" >&2 printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" [ "$STOP" ] && break fi -- cgit v1.2.3 From f92fa445c278ffa601b13b4ba98f35c8af16b761 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 17:44:29 -0500 Subject: tests: New test for -fstype on a stacked mount point --- tests/gnu/fstype_stacked.out | 1 + tests/gnu/fstype_stacked.sh | 13 +++++++++++++ 2 files changed, 14 insertions(+) create mode 100644 tests/gnu/fstype_stacked.out create mode 100644 tests/gnu/fstype_stacked.sh (limited to 'tests') diff --git a/tests/gnu/fstype_stacked.out b/tests/gnu/fstype_stacked.out new file mode 100644 index 0000000..99c7511 --- /dev/null +++ b/tests/gnu/fstype_stacked.out @@ -0,0 +1 @@ +scratch/mnt diff --git a/tests/gnu/fstype_stacked.sh b/tests/gnu/fstype_stacked.sh new file mode 100644 index 0000000..7110402 --- /dev/null +++ b/tests/gnu/fstype_stacked.sh @@ -0,0 +1,13 @@ +test "$SUDO" || skip +test "$UNAME" = "Linux" || skip + +clean_scratch +mkdir scratch/mnt + +sudo mount -t tmpfs tmpfs scratch/mnt +trap "sudo umount scratch/mnt" EXIT + +sudo mount -t ramfs ramfs scratch/mnt +trap "sudo umount scratch/mnt; sudo umount scratch/mnt" EXIT + +bfs_diff scratch/mnt -fstype ramfs -print -o -printf '%p: %F\n' -- cgit v1.2.3 From 0040a91a6b3a192acfeec0ae1e24516b54ba872a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 16 Dec 2022 18:03:44 -0500 Subject: tests/bfs/printf_everything: Simplify double negation --- tests/bfs/printf_everything.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/bfs/printf_everything.sh b/tests/bfs/printf_everything.sh index 5e95830..07d574a 100644 --- a/tests/bfs/printf_everything.sh +++ b/tests/bfs/printf_everything.sh @@ -1,14 +1,14 @@ everything=(%{a,b,c,d,D,f,g,G,h,H,i,k,l,m,M,n,p,P,s,S,t,u,U,y,Y}) # Check if we have fstypes -if ! ! invoke_bfs basic -printf '%F' -quit >/dev/null; then +if invoke_bfs basic -printf '%F' -quit >/dev/null; then everything+=(%F) fi everything+=(%{A,C,T}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z}) # Check if we have birth times -if ! ! invoke_bfs basic -printf '%w' -quit >/dev/null; then +if invoke_bfs basic -printf '%w' -quit >/dev/null; then everything+=(%w %{B,W}{%,+,@,a,A,b,B,c,C,d,D,e,F,g,G,h,H,I,j,k,l,m,M,n,p,r,R,s,S,t,T,u,U,V,w,W,x,X,y,Y,z,Z}) fi -- cgit v1.2.3 From b6859d7a6f7e0b3a3cb70fa75e7e46998e8f0f03 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 29 Dec 2022 13:05:06 -0500 Subject: tests: Use bfs_sudo wrapper instead of testing $SUDO --- tests/bfs/L_capable.sh | 3 +-- tests/bfs/capable.sh | 3 +-- tests/common/L_mount.sh | 5 ++--- tests/common/inum_bind_mount.sh | 5 ++--- tests/common/inum_mount.sh | 5 ++--- tests/common/mount.sh | 5 ++--- tests/gnu/fstype_stacked.sh | 9 ++++----- tests/gnu/inum_automount.sh | 5 ++--- tests/gnu/xtype_bind_mount.sh | 5 ++--- tests/posix/L_xdev.sh | 5 ++--- tests/posix/type_bind_mount.sh | 5 ++--- tests/posix/xdev.sh | 5 ++--- tests/tests.sh | 40 +++++++++++++++++++++++++--------------- 13 files changed, 49 insertions(+), 51 deletions(-) (limited to 'tests') diff --git a/tests/bfs/L_capable.sh b/tests/bfs/L_capable.sh index a349677..232d6ac 100644 --- a/tests/bfs/L_capable.sh +++ b/tests/bfs/L_capable.sh @@ -1,4 +1,3 @@ -test "$SUDO" || skip test "$UNAME" = "Linux" || skip clean_scratch @@ -6,7 +5,7 @@ clean_scratch invoke_bfs scratch -quit -capable || skip "$XTOUCH" scratch/{normal,capable} -sudo setcap all+ep scratch/capable +bfs_sudo setcap all+ep scratch/capable || skip ln -s capable scratch/link bfs_diff -L scratch -capable diff --git a/tests/bfs/capable.sh b/tests/bfs/capable.sh index 8b60ea6..e5cad63 100644 --- a/tests/bfs/capable.sh +++ b/tests/bfs/capable.sh @@ -1,4 +1,3 @@ -test "$SUDO" || skip test "$UNAME" = "Linux" || skip clean_scratch @@ -6,7 +5,7 @@ clean_scratch invoke_bfs scratch -quit -capable || skip "$XTOUCH" scratch/{normal,capable} -sudo setcap all+ep scratch/capable +bfs_sudo setcap all+ep scratch/capable || skip ln -s capable scratch/link bfs_diff scratch -capable diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh index 9ab785e..b04acd0 100644 --- a/tests/common/L_mount.sh +++ b/tests/common/L_mount.sh @@ -1,11 +1,10 @@ -test "$SUDO" || skip test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -trap "sudo umount scratch/mnt" EXIT +bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt" EXIT ln -s ../mnt scratch/foo/bar "$XTOUCH" scratch/mnt/baz diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh index 625ee3d..ecb4ec3 100644 --- a/tests/common/inum_bind_mount.sh +++ b/tests/common/inum_bind_mount.sh @@ -1,10 +1,9 @@ -test "$SUDO" || skip test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{foo,bar} -sudo mount --bind scratch/{foo,bar} -trap "sudo umount scratch/bar" EXIT +bfs_sudo mount --bind scratch/{foo,bar} || skip +trap "bfs_sudo umount scratch/bar" EXIT bfs_diff scratch -inum "$(inum scratch/bar)" diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh index 91d06e2..a4832e4 100644 --- a/tests/common/inum_mount.sh +++ b/tests/common/inum_mount.sh @@ -1,10 +1,9 @@ -test "$SUDO" || skip test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -trap "sudo umount scratch/mnt" EXIT +bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt" EXIT bfs_diff scratch -inum "$(inum scratch/mnt)" diff --git a/tests/common/mount.sh b/tests/common/mount.sh index 3f3ed38..b13b43c 100644 --- a/tests/common/mount.sh +++ b/tests/common/mount.sh @@ -1,11 +1,10 @@ -test "$SUDO" || skip test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -trap "sudo umount scratch/mnt" EXIT +bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt" EXIT "$XTOUCH" scratch/foo/bar scratch/mnt/baz diff --git a/tests/gnu/fstype_stacked.sh b/tests/gnu/fstype_stacked.sh index 7110402..16f428f 100644 --- a/tests/gnu/fstype_stacked.sh +++ b/tests/gnu/fstype_stacked.sh @@ -1,13 +1,12 @@ -test "$SUDO" || skip test "$UNAME" = "Linux" || skip clean_scratch mkdir scratch/mnt -sudo mount -t tmpfs tmpfs scratch/mnt -trap "sudo umount scratch/mnt" EXIT +bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt" EXIT -sudo mount -t ramfs ramfs scratch/mnt -trap "sudo umount scratch/mnt; sudo umount scratch/mnt" EXIT +bfs_sudo mount -t ramfs ramfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt; bfs_sudo umount scratch/mnt" EXIT bfs_diff scratch/mnt -fstype ramfs -print -o -printf '%p: %F\n' diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh index da2e3b0..c4450ef 100644 --- a/tests/gnu/inum_automount.sh +++ b/tests/gnu/inum_automount.sh @@ -1,13 +1,12 @@ # bfs shouldn't trigger automounts unless it descends into them -test "$SUDO" || skip command -v systemd-mount &>/dev/null || skip clean_scratch mkdir scratch/{foo,automnt} -sudo systemd-mount -A -o bind basic scratch/automnt || skip -trap "sudo systemd-umount scratch/automnt" EXIT +bfs_sudo systemd-mount -A -o bind basic scratch/automnt || skip +trap "bfs_sudo systemd-umount scratch/automnt" EXIT before=$(inum scratch/automnt) bfs_diff scratch -inum "$before" -prune diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh index 9babd9d..99a11ab 100644 --- a/tests/gnu/xtype_bind_mount.sh +++ b/tests/gnu/xtype_bind_mount.sh @@ -1,11 +1,10 @@ -test "$SUDO" || skip test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{file,null} ln -s /dev/null scratch/link -sudo mount --bind /dev/null scratch/null -trap "sudo umount scratch/null" EXIT +bfs_sudo mount --bind /dev/null scratch/null || skip +trap "bfs_sudo umount scratch/null" EXIT bfs_diff -L scratch -type c diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh index 587c8bb..172ea23 100644 --- a/tests/posix/L_xdev.sh +++ b/tests/posix/L_xdev.sh @@ -1,11 +1,10 @@ -test "$SUDO" || skip test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -trap "sudo umount scratch/mnt" EXIT +bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt" EXIT ln -s ../mnt scratch/foo/bar "$XTOUCH" scratch/mnt/baz diff --git a/tests/posix/type_bind_mount.sh b/tests/posix/type_bind_mount.sh index 2d913db..c9a161d 100644 --- a/tests/posix/type_bind_mount.sh +++ b/tests/posix/type_bind_mount.sh @@ -1,10 +1,9 @@ -test "$SUDO" || skip test "$UNAME" = "Linux" || skip clean_scratch "$XTOUCH" scratch/{file,null} -sudo mount --bind /dev/null scratch/null -trap "sudo umount scratch/null" EXIT +bfs_sudo mount --bind /dev/null scratch/null || skip +trap "bfs_sudo umount scratch/null" EXIT bfs_diff scratch -type c diff --git a/tests/posix/xdev.sh b/tests/posix/xdev.sh index 9d21b14..33412bf 100644 --- a/tests/posix/xdev.sh +++ b/tests/posix/xdev.sh @@ -1,11 +1,10 @@ -test "$SUDO" || skip test "$UNAME" = "Darwin" && skip clean_scratch mkdir scratch/{foo,mnt} -sudo mount -t tmpfs tmpfs scratch/mnt -trap "sudo umount scratch/mnt" EXIT +bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip +trap "bfs_sudo umount scratch/mnt" EXIT "$XTOUCH" scratch/foo/bar scratch/mnt/baz diff --git a/tests/tests.sh b/tests/tests.sh index 962a2cf..e57db4e 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -101,18 +101,15 @@ fi function usage() { local pad=$(printf "%*s" ${#0} "") cat </dev/null; then + if ((${#SUDO[@]})) && command -v mountpoint &>/dev/null; then for path in "$TMP"/scratch/*; do if mountpoint -q "$path"; then sudo umount "$path" @@ -608,10 +619,9 @@ function make_xattrs() { *) # Linux tmpfs doesn't support the user.* namespace, so we use the security.* # namespace, which is writable by root and readable by others - [ "$SUDO" ] \ - && sudo setfattr -n security.bfs_test scratch/xattr \ - && sudo setfattr -n security.bfs_test_2 scratch/xattr_2 \ - && sudo setfattr -h -n security.bfs_test scratch/xattr_link + bfs_sudo setfattr -n security.bfs_test scratch/xattr \ + && bfs_sudo setfattr -n security.bfs_test_2 scratch/xattr_2 \ + && bfs_sudo setfattr -h -n security.bfs_test scratch/xattr_link ;; esac } -- cgit v1.2.3 From 683552c4c9a3dfee4ce603157bb2cf18d64fbcfc Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 30 Dec 2022 14:49:46 -0500 Subject: bfstd: New wrappers for dirname()/basename() --- Makefile | 13 +++++++++---- src/bfstd.c | 45 ++++++++++++++++++++++++++++++++++++++++----- src/bfstd.h | 28 +++++++++++++++++++++++++--- src/bftw.c | 2 +- src/color.c | 2 +- src/diag.c | 11 ++++++++--- src/eval.c | 10 +--------- src/exec.c | 2 +- src/mtab.c | 5 +++-- src/parse.c | 6 ++++-- tests/bfstd.c | 57 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/mksock.c | 22 ++++++++-------------- tests/xtouch.c | 1 - 13 files changed, 158 insertions(+), 46 deletions(-) create mode 100644 tests/bfstd.c (limited to 'tests') diff --git a/Makefile b/Makefile index 49a3a87..b39a88a 100644 --- a/Makefile +++ b/Makefile @@ -197,6 +197,7 @@ $(FLAG_GOALS): $(FLAG_PREREQS) all: \ $(BIN)/bfs \ + $(BIN)/tests/bfstd \ $(BIN)/tests/mksock \ $(BIN)/tests/trie \ $(BIN)/tests/xtimegm \ @@ -250,22 +251,26 @@ LIBBFS := \ # The main executable $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) +# Standalone binary tests +STANDALONE_CHECKS := check-bfstd check-trie check-xtimegm + # The different search strategies that we test STRATEGIES := bfs dfs ids eds STRATEGY_CHECKS := $(STRATEGIES:%=check-%) # All the different checks we run -CHECKS := $(STRATEGY_CHECKS) check-trie check-xtimegm +CHECKS := $(STANDALONE_CHECKS) $(STRATEGY_CHECKS) check: $(CHECKS) .PHONY: check $(CHECKS) +$(STANDALONE_CHECKS): check-%: $(BIN)/tests/% + $< + $(STRATEGY_CHECKS): check-%: $(BIN)/bfs $(BIN)/tests/mksock $(BIN)/tests/xtouch ./tests/tests.sh --bfs="$(BIN)/bfs -S $*" $(TEST_FLAGS) -check-trie check-xtimegm: check-%: $(BIN)/tests/% - $< - +$(BIN)/tests/bfstd: $(OBJ)/tests/bfstd.o $(LIBBFS) $(BIN)/tests/mksock: $(OBJ)/tests/mksock.o $(LIBBFS) $(BIN)/tests/trie: $(OBJ)/tests/trie.o $(LIBBFS) $(BIN)/tests/xtimegm: $(OBJ)/tests/xtimegm.o $(LIBBFS) diff --git a/src/bfstd.c b/src/bfstd.c index 1561796..3a37250 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -45,17 +45,52 @@ bool is_nonexistence_error(int error) { return error == ENOENT || errno == ENOTDIR; } -const char *xbasename(const char *path) { - const char *i; +char *xdirname(const char *path) { + size_t i = xbaseoff(path); // Skip trailing slashes - for (i = path + strlen(path); i > path && i[-1] == '/'; --i); + while (i > 0 && path[i - 1] == '/') { + --i; + } + + if (i > 0) { + return strndup(path, i); + } else if (path[i] == '/') { + return strdup("/"); + } else { + return strdup("."); + } +} + +char *xbasename(const char *path) { + size_t i = xbaseoff(path); + size_t len = strcspn(path + i, "/"); + if (len > 0) { + return strndup(path + i, len); + } else if (path[i] == '/') { + return strdup("/"); + } else { + return strdup("."); + } +} + +size_t xbaseoff(const char *path) { + size_t i = strlen(path); + + // Skip trailing slashes + while (i > 0 && path[i - 1] == '/') { + --i; + } // Find the beginning of the name - for (; i > path && i[-1] != '/'; --i); + while (i > 0 && path[i - 1] != '/') { + --i; + } // Skip leading slashes - for (; i[0] == '/' && i[1]; ++i); + while (path[i] == '/' && path[i + 1]) { + ++i; + } return i; } diff --git a/src/bfstd.h b/src/bfstd.h index 6bf6ec8..d46fa02 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -22,6 +22,7 @@ #define BFS_BFSTD_H #include +#include // #include @@ -45,13 +46,34 @@ bool is_nonexistence_error(int error); // #include /** - * basename() variant that doesn't modify the input. + * Re-entrant dirname() variant that always allocates a copy. * * @param path * The path in question. - * @return A pointer into path at the base name offset. + * @return + * The parent directory of the path. + */ +char *xdirname(const char *path); + +/** + * Re-entrant basename() variant that always allocates a copy. + * + * @param path + * The path in question. + * @return + * The final component of the path. + */ +char *xbasename(const char *path); + +/** + * Find the offset of the final component of a path. + * + * @param path + * The path in question. + * @return + * The offset of the basename. */ -const char *xbasename(const char *path); +size_t xbaseoff(const char *path); #include diff --git a/src/bftw.c b/src/bftw.c index 8c88101..5f3ebde 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -793,7 +793,7 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) { if (ftwbuf->depth == 0) { // Compute the name offset for root paths like "foo/bar" - ftwbuf->nameoff = xbasename(ftwbuf->path) - ftwbuf->path; + ftwbuf->nameoff = xbaseoff(ftwbuf->path); } if (ftwbuf->error != 0) { diff --git a/src/color.c b/src/color.c index cc37e96..7c16ec5 100644 --- a/src/color.c +++ b/src/color.c @@ -809,7 +809,7 @@ static int print_path_colored(CFILE *cfile, const char *path, const struct BFTW if (path == ftwbuf->path) { nameoff = ftwbuf->nameoff; } else { - nameoff = xbasename(path) - path; + nameoff = xbaseoff(path); } if (print_dirs_colored(cfile, path, ftwbuf, flags, nameoff) != 0) { diff --git a/src/diag.c b/src/diag.c index a0c11f2..b02473a 100644 --- a/src/diag.c +++ b/src/diag.c @@ -84,13 +84,18 @@ bool bfs_vdebug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *fo } } +/** Get the command name without any leading directories. */ +static const char *bfs_cmd(const struct bfs_ctx *ctx) { + return ctx->argv[0] + xbaseoff(ctx->argv[0]); +} + void bfs_error_prefix(const struct bfs_ctx *ctx) { - cfprintf(ctx->cerr, "${bld}%s:${rs} ${err}error:${rs} ", xbasename(ctx->argv[0])); + cfprintf(ctx->cerr, "${bld}%s:${rs} ${err}error:${rs} ", bfs_cmd(ctx)); } bool bfs_warning_prefix(const struct bfs_ctx *ctx) { if (ctx->warn) { - cfprintf(ctx->cerr, "${bld}%s:${rs} ${wrn}warning:${rs} ", xbasename(ctx->argv[0])); + cfprintf(ctx->cerr, "${bld}%s:${rs} ${wrn}warning:${rs} ", bfs_cmd(ctx)); return true; } else { return false; @@ -99,7 +104,7 @@ bool bfs_warning_prefix(const struct bfs_ctx *ctx) { bool bfs_debug_prefix(const struct bfs_ctx *ctx, enum debug_flags flag) { if (ctx->debug & flag) { - cfprintf(ctx->cerr, "${bld}%s:${rs} ${cyn}-D %s${rs}: ", xbasename(ctx->argv[0]), debug_flag_name(flag)); + cfprintf(ctx->cerr, "${bld}%s:${rs} ${cyn}-D %s${rs}: ", bfs_cmd(ctx), debug_flag_name(flag)); return true; } else { return false; diff --git a/src/eval.c b/src/eval.c index 89591b2..32b2e0e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -590,15 +590,7 @@ bool eval_name(const struct bfs_expr *expr, struct bfs_eval *state) { if (ftwbuf->depth == 0) { // Any trailing slashes are not part of the name. This can only // happen for the root path. - const char *slash = strchr(name, '/'); - if (slash && slash > name) { - copy = strndup(name, slash - name); - if (!copy) { - eval_report_error(state); - return false; - } - name = copy; - } + name = copy = xbasename(name); } bool ret = fnmatch(expr->argv[1], name, expr->num) == 0; diff --git a/src/exec.c b/src/exec.c index a1cbde1..8630469 100644 --- a/src/exec.c +++ b/src/exec.c @@ -295,7 +295,7 @@ static int bfs_exec_openwd(struct bfs_exec *execbuf, const struct BFTW *ftwbuf) if (ftwbuf->at_fd != AT_FDCWD) { // Rely on at_fd being the immediate parent - assert(ftwbuf->at_path == xbasename(ftwbuf->at_path)); + assert(xbaseoff(ftwbuf->at_path) == 0); execbuf->wd_fd = ftwbuf->at_fd; if (!(execbuf->flags & BFS_EXEC_MULTI)) { diff --git a/src/mtab.c b/src/mtab.c index 316ec6e..39676e5 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -86,7 +86,8 @@ static int bfs_mtab_add(struct bfs_mtab *mtab, const char *path, const char *typ goto fail_entry; } - if (!trie_insert_str(&mtab->names, xbasename(path))) { + const char *name = path + xbaseoff(path); + if (!trie_insert_str(&mtab->names, name)) { goto fail; } @@ -223,7 +224,7 @@ const char *bfs_fstype(const struct bfs_mtab *mtab, const struct bfs_stat *statb } bool bfs_might_be_mount(const struct bfs_mtab *mtab, const char *path) { - const char *name = xbasename(path); + const char *name = path + xbaseoff(path); return trie_find_str(&mtab->names, name); } diff --git a/src/parse.c b/src/parse.c index ad32714..51a9d0a 100644 --- a/src/parse.c +++ b/src/parse.c @@ -2888,7 +2888,8 @@ static CFILE *launch_pager(pid_t *pid, CFILE *cout) { NULL, }; - if (strcmp(xbasename(exe), "less") == 0) { + const char *cmd = exe + xbaseoff(exe); + if (strcmp(cmd, "less") == 0) { // We know less supports colors, other pagers may not ret->colors = cout->colors; argv[1] = "-FKRX"; @@ -3931,7 +3932,8 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .now = ctx->now, }; - if (strcmp(xbasename(state.command), "find") == 0) { + const char *cmd = state.command + xbaseoff(state.command); + if (strcmp(cmd, "find") == 0) { // Operate depth-first when invoked as "find" ctx->strategy = BFTW_DFS; } diff --git a/tests/bfstd.c b/tests/bfstd.c new file mode 100644 index 0000000..4a8181b --- /dev/null +++ b/tests/bfstd.c @@ -0,0 +1,57 @@ +/**************************************************************************** + * bfs * + * Copyright (C) 2022 Tavian Barnes * + * * + * Permission to use, copy, modify, and/or distribute this software for any * + * purpose with or without fee is hereby granted. * + * * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * + ****************************************************************************/ + +#include "../src/bfstd.h" +#include +#include +#include + +/** Check the result of xdirname()/xbasename(). */ +static void check_base_dir(const char *path, const char *dir, const char *base) { + char *xdir = xdirname(path); + if (!xdir) { + perror("xdirname()"); + abort(); + } else if (strcmp(xdir, dir) != 0) { + fprintf(stderr, "xdirname(\"%s\") == \"%s\" (!= \"%s\")\n", path, xdir, dir); + abort(); + } + free(xdir); + + char *xbase = xbasename(path); + if (!xbase) { + perror("xbasename()"); + abort(); + } else if (strcmp(xbase, base) != 0) { + fprintf(stderr, "xbasename(\"%s\") == \"%s\" (!= \"%s\")\n", path, xbase, base); + abort(); + } + free(xbase); +} + +int main(void) { + // From man 3p basename + check_base_dir("usr", ".", "usr"); + check_base_dir("usr/", ".", "usr"); + check_base_dir("", ".", "."); + check_base_dir("/", "/", "/"); + // check_base_dir("//", "/" or "//", "/" or "//"); + check_base_dir("///", "/", "/"); + check_base_dir("/usr/", "/", "usr"); + check_base_dir("/usr/lib", "/usr", "lib"); + check_base_dir("//usr//lib//", "//usr", "lib"); + check_base_dir("/home//dwc//test", "/home//dwc", "test"); +} diff --git a/tests/mksock.c b/tests/mksock.c index d1776b3..5068bc8 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -19,8 +19,8 @@ * program does the job. */ +#include "../src/bfstd.h" #include -#include #include #include #include @@ -41,18 +41,13 @@ static void errmsg(const char *cmd, const char *path) { * file name is not. */ static int chdir_parent(const char *path) { - char *copy = strdup(path); - if (!copy) { + char *dir = xdirname(path); + if (!dir) { return -1; } - const char *dir = dirname(copy); int ret = chdir(dir); - - int error = errno; - free(copy); - errno = error; - + free(dir); return ret; } @@ -66,22 +61,21 @@ static int init_sun(struct sockaddr_un *sock, const char *path) { return -1; } - char *copy = strdup(path); - if (!copy) { + char *base = xbasename(path); + if (!base) { return -1; } - const char *base = basename(copy); len = strlen(base); if (len >= sizeof(sock->sun_path)) { - free(copy); + free(base); errno = ENAMETOOLONG; return -1; } sock->sun_family = AF_UNIX; memcpy(sock->sun_path, base, len + 1); - free(copy); + free(base); return 0; } diff --git a/tests/xtouch.c b/tests/xtouch.c index 9a91ec7..262fc33 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -14,7 +14,6 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * ****************************************************************************/ -#include "../src/bfstd.h" #include "../src/xtime.h" #include #include -- cgit v1.2.3 From ca00b621eba1bf8b2a591b943090ac8af605922d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Jan 2023 14:49:32 -0500 Subject: tests/xtouch: Fix macOS build --- tests/xtouch.c | 1 + 1 file changed, 1 insertion(+) (limited to 'tests') diff --git a/tests/xtouch.c b/tests/xtouch.c index 262fc33..9a91ec7 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -14,6 +14,7 @@ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * ****************************************************************************/ +#include "../src/bfstd.h" #include "../src/xtime.h" #include #include -- cgit v1.2.3 From 869e4010433c8610ba59f9a6a310df8be228d718 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 22 Jan 2023 14:21:36 -0500 Subject: mtab: Mitigate the race between bfs_mtab_parse() and bfs_mtab_fill_types() Fixes #97. --- src/mtab.c | 37 +++++++++++++++++++++++++++++++++++-- tests/gnu/fstype_umount.out | 0 tests/gnu/fstype_umount.sh | 12 ++++++++++++ 3 files changed, 47 insertions(+), 2 deletions(-) create mode 100644 tests/gnu/fstype_umount.out create mode 100644 tests/gnu/fstype_umount.sh (limited to 'tests') diff --git a/src/mtab.c b/src/mtab.c index 39676e5..35ae51d 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -193,18 +193,51 @@ fail: } static void bfs_mtab_fill_types(struct bfs_mtab *mtab) { + const enum bfs_stat_flags flags = BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC; + for (size_t i = 0; i < darray_length(mtab->entries); ++i) { struct bfs_mtab_entry *entry = &mtab->entries[i]; + // It's possible that /path/to/mount was unmounted between bfs_mtab_parse() and bfs_mtab_fill_types(). + // In that case, the dev_t of /path/to/mount will be the same as /path/to, which should not get its + // fstype from the old mount record of /path/to/mount. + int fd = -1; + const char *path = entry->path; + char *dir = xdirname(path); + if (dir) { + fd = open(dir, O_SEARCH | O_CLOEXEC | O_DIRECTORY); + } + if (fd >= 0) { + path += xbaseoff(path); + } else { + fd = AT_FDCWD; + } + struct bfs_stat sb; - if (bfs_stat(AT_FDCWD, entry->path, BFS_STAT_NOFOLLOW | BFS_STAT_NOSYNC, &sb) != 0) { - continue; + if (bfs_stat(fd, path, flags, &sb) != 0) { + goto next; + } + + if (fd >= 0) { + struct bfs_stat parent; + if (bfs_stat(fd, NULL, flags, &parent) == 0) { + if (parent.dev == sb.dev) { + // Not a mount point any more (or a bind mount, but with the same fstype) + goto next; + } + } } struct trie_leaf *leaf = trie_insert_mem(&mtab->types, &sb.dev, sizeof(sb.dev)); if (leaf) { leaf->value = entry->type; } + + next: + free(dir); + if (fd >= 0) { + xclose(fd); + } } mtab->types_filled = true; diff --git a/tests/gnu/fstype_umount.out b/tests/gnu/fstype_umount.out new file mode 100644 index 0000000..e69de29 diff --git a/tests/gnu/fstype_umount.sh b/tests/gnu/fstype_umount.sh new file mode 100644 index 0000000..e817831 --- /dev/null +++ b/tests/gnu/fstype_umount.sh @@ -0,0 +1,12 @@ +test "$UNAME" = "Linux" || skip + +clean_scratch + +mkdir scratch/tmp +bfs_sudo mount -t tmpfs tmpfs scratch/tmp || skip +trap "bfs_sudo umount -R scratch/tmp" EXIT + +mkdir scratch/tmp/ram +bfs_sudo mount -t ramfs ramfs scratch/tmp/ram || skip + +bfs_diff scratch/tmp -path scratch/tmp -exec "${SUDO[@]}" umount scratch/tmp/ram \; , -fstype ramfs -print -- cgit v1.2.3 From 9463fdd30d392c98de7b5712d30dfbaeada40e99 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 25 Jan 2023 16:14:11 -0500 Subject: Replace license boilerplate with SPDX tags And while I'm at it, remove years from copyright declarations. Link: https://spdx.dev/about/ Link: https://daniel.haxx.se/blog/2023/01/08/copyright-without-years/ --- LICENSE | 33 +++++++++++++++++++++------------ Makefile | 17 ++--------------- completions/bfs.bash | 21 ++++----------------- src/bar.c | 17 ++--------------- src/bar.h | 17 ++--------------- src/bfstd.c | 17 ++--------------- src/bfstd.h | 17 ++--------------- src/bftw.c | 17 ++--------------- src/bftw.h | 17 ++--------------- src/color.c | 17 ++--------------- src/color.h | 17 ++--------------- src/config.h | 17 ++--------------- src/ctx.c | 17 ++--------------- src/ctx.h | 17 ++--------------- src/darray.c | 17 ++--------------- src/darray.h | 17 ++--------------- src/diag.c | 17 ++--------------- src/diag.h | 17 ++--------------- src/dir.c | 17 ++--------------- src/dir.h | 17 ++--------------- src/dstring.c | 17 ++--------------- src/dstring.h | 17 ++--------------- src/eval.c | 17 ++--------------- src/eval.h | 17 ++--------------- src/exec.c | 17 ++--------------- src/exec.h | 17 ++--------------- src/expr.h | 17 ++--------------- src/fsade.c | 17 ++--------------- src/fsade.h | 17 ++--------------- src/main.c | 17 ++--------------- src/mtab.c | 17 ++--------------- src/mtab.h | 17 ++--------------- src/opt.c | 17 ++--------------- src/opt.h | 17 ++--------------- src/parse.c | 17 ++--------------- src/parse.h | 17 ++--------------- src/printf.c | 17 ++--------------- src/printf.h | 17 ++--------------- src/pwcache.c | 17 ++--------------- src/pwcache.h | 17 ++--------------- src/stat.c | 17 ++--------------- src/stat.h | 17 ++--------------- src/trie.c | 17 ++--------------- src/trie.h | 17 ++--------------- src/typo.c | 17 ++--------------- src/typo.h | 17 ++--------------- src/xregex.c | 17 ++--------------- src/xregex.h | 18 ++---------------- src/xspawn.c | 17 ++--------------- src/xspawn.h | 17 ++--------------- src/xtime.c | 17 ++--------------- src/xtime.h | 17 ++--------------- tests/bfstd.c | 17 ++--------------- tests/find-color.sh | 17 ++--------------- tests/ls-color.sh | 17 ++--------------- tests/mksock.c | 17 ++--------------- tests/tests.sh | 17 ++--------------- tests/trie.c | 17 ++--------------- tests/xtimegm.c | 17 ++--------------- tests/xtouch.c | 17 ++--------------- 60 files changed, 141 insertions(+), 900 deletions(-) (limited to 'tests') diff --git a/LICENSE b/LICENSE index 069b145..290e3d3 100644 --- a/LICENSE +++ b/LICENSE @@ -1,12 +1,21 @@ -Copyright (C) 2015-2021 Tavian Barnes - -Permission to use, copy, modify, and/or distribute this software for any -purpose with or without fee is hereby granted. - -THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES -WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF -MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR -ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES -WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN -ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF -OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. +Copyright © 2015-2023 Tavian Barnes and the bfs contributors + +Permission to use, copy, modify, and/or distribute this software for any purpose with or +without fee is hereby granted. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO +THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT +SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION +OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE +USE OR PERFORMANCE OF THIS SOFTWARE. + +--- + +bfs is licensed under the Zero Clause BSD License. Individual files contain the following +tag instead of the full license text: + + SPDX-License-Identifier: 0BSD + +This enables machine processing of license information based on the SPDX License +Identifiers that are available here: https://spdx.org/licenses/ diff --git a/Makefile b/Makefile index b39a88a..0247b47 100644 --- a/Makefile +++ b/Makefile @@ -1,18 +1,5 @@ -############################################################################ -# bfs # -# Copyright (C) 2015-2023 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD ifneq ($(wildcard .git),) VERSION := $(shell git describe --always 2>/dev/null) diff --git a/completions/bfs.bash b/completions/bfs.bash index f734ab1..2f52e8d 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -1,21 +1,8 @@ -# bash completion script for bfs +# Copyright © Benjamin Mundt +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD -############################################################################ -# bfs # -# Copyright (C) 2020 Benjamin Mundt # -# Copyright (C) 2021 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# bash completion script for bfs _bfs() { local cur prev words cword diff --git a/src/bar.c b/src/bar.c index 37d33c8..bd5d381 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "bar.h" #include "bfstd.h" diff --git a/src/bar.h b/src/bar.h index 3e509d6..20d92a9 100644 --- a/src/bar.h +++ b/src/bar.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A terminal status bar. diff --git a/src/bfstd.c b/src/bfstd.c index 3a37250..437e9c9 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "bfstd.h" #include "config.h" diff --git a/src/bfstd.h b/src/bfstd.h index 79307cc..0e11b66 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Standard library wrappers and polyfills. diff --git a/src/bftw.c b/src/bftw.c index 5f3ebde..9feca79 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The bftw() implementation consists of the following components: diff --git a/src/bftw.h b/src/bftw.h index c458e1b..77697ed 100644 --- a/src/bftw.h +++ b/src/bftw.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A file-walking API based on nftw(). diff --git a/src/color.c b/src/color.c index 7c16ec5..589e631 100644 --- a/src/color.c +++ b/src/color.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "color.h" #include "bfstd.h" diff --git a/src/color.h b/src/color.h index 5b350cc..737c34b 100644 --- a/src/color.h +++ b/src/color.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Utilities for colored output on ANSI terminals. diff --git a/src/config.h b/src/config.h index 810f913..5ae9c82 100644 --- a/src/config.h +++ b/src/config.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Configuration and feature/platform detection. diff --git a/src/ctx.c b/src/ctx.c index 0403299..ff4a2a7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "ctx.h" #include "color.h" diff --git a/src/ctx.h b/src/ctx.h index 6755d02..4c748b7 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * bfs execution context. diff --git a/src/darray.c b/src/darray.c index 6585d30..42b8397 100644 --- a/src/darray.c +++ b/src/darray.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "darray.h" #include diff --git a/src/darray.h b/src/darray.h index 4464381..cc6cc42 100644 --- a/src/darray.h +++ b/src/darray.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A dynamic array library. diff --git a/src/diag.c b/src/diag.c index b02473a..53db98e 100644 --- a/src/diag.c +++ b/src/diag.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "diag.h" #include "bfstd.h" diff --git a/src/diag.h b/src/diag.h index 8d0b19f..2952e30 100644 --- a/src/diag.h +++ b/src/diag.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Formatters for diagnostic messages. diff --git a/src/dir.c b/src/dir.c index 2081cc5..3d18eb2 100644 --- a/src/dir.c +++ b/src/dir.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "dir.h" #include "bfstd.h" diff --git a/src/dir.h b/src/dir.h index 69344c6..01eaaba 100644 --- a/src/dir.h +++ b/src/dir.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Directories and their contents. diff --git a/src/dstring.c b/src/dstring.c index f344d09..9112e54 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "dstring.h" #include diff --git a/src/dstring.h b/src/dstring.h index df20a04..ee3b345 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A dynamic string library. diff --git a/src/eval.c b/src/eval.c index d297af1..53ce605 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of all the primary expressions. diff --git a/src/eval.h b/src/eval.h index a50dc4e..3d70319 100644 --- a/src/eval.h +++ b/src/eval.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The evaluation functions that implement primary expressions like -name, diff --git a/src/exec.c b/src/exec.c index 8630469..6bde1c1 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "exec.h" #include "bfstd.h" diff --git a/src/exec.h b/src/exec.h index a3e3c71..9d4192d 100644 --- a/src/exec.h +++ b/src/exec.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of -exec/-execdir/-ok/-okdir. diff --git a/src/expr.h b/src/expr.h index a52007a..1628cac 100644 --- a/src/expr.h +++ b/src/expr.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The expression tree representation. diff --git a/src/fsade.c b/src/fsade.c index 45969d1..a61a6b8 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2021 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "fsade.h" #include "config.h" diff --git a/src/fsade.h b/src/fsade.h index f45c6fd..9bef892 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over (file)system features that are (un)implemented differently diff --git a/src/main.c b/src/main.c index fbddbe5..4f99580 100644 --- a/src/main.c +++ b/src/main.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * - main(): the entry point for bfs(1), a breadth-first version of find(1) diff --git a/src/mtab.c b/src/mtab.c index 07d7a53..ae6dfd2 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "mtab.h" #include "bfstd.h" diff --git a/src/mtab.h b/src/mtab.h index 807539d..5dfdf6c 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over platform-specific APIs for enumerating mounted filesystems. diff --git a/src/opt.c b/src/opt.c index e76e216..5505b7b 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The expression optimizer. Different optimization levels are supported: diff --git a/src/opt.h b/src/opt.h index 5f8180d..28cadb9 100644 --- a/src/opt.h +++ b/src/opt.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Optimization. diff --git a/src/parse.c b/src/parse.c index f2582e0..30bf56d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2015-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * The command line parser. Expressions are parsed by recursive descent, with a diff --git a/src/parse.h b/src/parse.h index 7e29a03..6895c9f 100644 --- a/src/parse.h +++ b/src/parse.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * bfs command line parsing. diff --git a/src/printf.c b/src/printf.c index 7c0c8db..1b4f2d4 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "printf.h" #include "bfstd.h" diff --git a/src/printf.h b/src/printf.h index a8c5f2a..2bff087 100644 --- a/src/printf.h +++ b/src/printf.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2017-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Implementation of -printf/-fprintf. diff --git a/src/pwcache.c b/src/pwcache.c index 868ec8f..5026dee 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "pwcache.h" #include "darray.h" diff --git a/src/pwcache.h b/src/pwcache.h index f1ca0bf..b6c0b67 100644 --- a/src/pwcache.h +++ b/src/pwcache.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A caching wrapper for /etc/{passwd,group}. diff --git a/src/stat.c b/src/stat.c index 94dedef..aaa5eac 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "stat.h" #include "bfstd.h" diff --git a/src/stat.h b/src/stat.h index 44cbfad..7a70146 100644 --- a/src/stat.h +++ b/src/stat.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2019 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A facade over the stat() API that unifies some details that diverge between diff --git a/src/trie.c b/src/trie.c index 08a99b5..77c43cc 100644 --- a/src/trie.c +++ b/src/trie.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * This is an implementation of a "qp trie," as documented at diff --git a/src/trie.h b/src/trie.h index 2ede6ea..03ee64d 100644 --- a/src/trie.h +++ b/src/trie.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #ifndef BFS_TRIE_H #define BFS_TRIE_H diff --git a/src/typo.c b/src/typo.c index c16cab4..305711d 100644 --- a/src/typo.c +++ b/src/typo.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "typo.h" #include diff --git a/src/typo.h b/src/typo.h index 0347aae..13eaa67 100644 --- a/src/typo.h +++ b/src/typo.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2016 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #ifndef BFS_TYPO_H #define BFS_TYPO_H diff --git a/src/xregex.c b/src/xregex.c index 6f0e5a1..5f5480f 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xregex.h" #include "config.h" diff --git a/src/xregex.h b/src/xregex.h index b2f56a5..998a2b0 100644 --- a/src/xregex.h +++ b/src/xregex.h @@ -1,19 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes and bfs * - * contributors * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes and the bfs contributors +// SPDX-License-Identifier: 0BSD #ifndef BFS_XREGEX_H #define BFS_XREGEX_H diff --git a/src/xspawn.c b/src/xspawn.c index f76267b..a30c264 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xspawn.h" #include "bfstd.h" diff --git a/src/xspawn.h b/src/xspawn.h index cd6a42e..3dbf5d2 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2018-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * A process-spawning library inspired by posix_spawn(). diff --git a/src/xtime.c b/src/xtime.c index 079d42a..82690d0 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "xtime.h" #include diff --git a/src/xtime.h b/src/xtime.h index b49cd04..75d1f4e 100644 --- a/src/xtime.h +++ b/src/xtime.h @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * Date/time handling. diff --git a/tests/bfstd.c b/tests/bfstd.c index 4a8181b..8c61072 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" #include diff --git a/tests/find-color.sh b/tests/find-color.sh index ecdd5af..47de2a2 100755 --- a/tests/find-color.sh +++ b/tests/find-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD set -e diff --git a/tests/ls-color.sh b/tests/ls-color.sh index c82a58d..6d33f53 100755 --- a/tests/ls-color.sh +++ b/tests/ls-color.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2019 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD # Prints the "ground truth" coloring of a path using ls diff --git a/tests/mksock.c b/tests/mksock.c index 5068bc8..05edbeb 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2019 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD /** * There's no standard Unix utility that creates a socket file, so this small diff --git a/tests/tests.sh b/tests/tests.sh index e57db4e..98d332c 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -1,20 +1,7 @@ #!/usr/bin/env bash -############################################################################ -# bfs # -# Copyright (C) 2015-2022 Tavian Barnes # -# # -# Permission to use, copy, modify, and/or distribute this software for any # -# purpose with or without fee is hereby granted. # -# # -# THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES # -# WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF # -# MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR # -# ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES # -# WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN # -# ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF # -# OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. # -############################################################################ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD set -euP umask 022 diff --git a/tests/trie.c b/tests/trie.c index 88e92da..c2af18a 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020-2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #undef NDEBUG diff --git a/tests/xtimegm.c b/tests/xtimegm.c index d774b9e..bab64ba 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2020 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/xtime.h" #include diff --git a/tests/xtouch.c b/tests/xtouch.c index 9a91ec7..506c73d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,18 +1,5 @@ -/**************************************************************************** - * bfs * - * Copyright (C) 2022 Tavian Barnes * - * * - * Permission to use, copy, modify, and/or distribute this software for any * - * purpose with or without fee is hereby granted. * - * * - * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES * - * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF * - * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR * - * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES * - * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN * - * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF * - * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. * - ****************************************************************************/ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" #include "../src/xtime.h" -- cgit v1.2.3 From 88525617e977b2b24315e8a7bc565c2975cdf345 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 31 Jan 2023 12:16:41 -0500 Subject: tests: Use close() wrappers --- tests/mksock.c | 2 +- tests/xtouch.c | 6 ++---- 2 files changed, 3 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/mksock.c b/tests/mksock.c index 05edbeb..42ef322 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -100,7 +100,7 @@ int main(int argc, char *argv[]) { ret = EXIT_FAILURE; } - if (close(fd) != 0) { + if (xclose(fd) != 0) { errmsg(cmd, path); ret = EXIT_FAILURE; } diff --git a/tests/xtouch.c b/tests/xtouch.c index 506c73d..7e29547 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -104,13 +104,11 @@ static int xtouch(const struct args *args, const char *path) { } if (futimens(fd, args->times) != 0) { - int error = errno; - close(fd); - errno = error; + close_quietly(fd); return -1; } - return close(fd); + return xclose(fd); } } -- cgit v1.2.3 From ba2d4ac206ff1321ea953d3305d8bda048922983 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 28 Mar 2023 15:34:43 -0400 Subject: trie: Use list.h for the list of leaves --- src/trie.c | 28 ++++------------------------ src/trie.h | 31 ++++++++++--------------------- tests/trie.c | 2 -- 3 files changed, 14 insertions(+), 47 deletions(-) (limited to 'tests') diff --git a/src/trie.c b/src/trie.c index 77c43cc..7b00f4b 100644 --- a/src/trie.c +++ b/src/trie.c @@ -163,8 +163,7 @@ static uintptr_t trie_encode_node(const struct trie_node *node) { void trie_init(struct trie *trie) { trie->root = 0; - trie->head = NULL; - trie->tail = NULL; + list_init(&trie->leaves); } /** Check if a number is a power of two. */ @@ -341,16 +340,8 @@ static struct trie_leaf *trie_leaf_alloc(struct trie *trie, const void *key, siz return NULL; } - leaf->prev = trie->tail; - leaf->next = NULL; - - if (leaf->prev) { - leaf->prev->next = leaf; - } else { - trie->head = leaf; - } - - trie->tail = leaf; + link_init(&leaf->link); + list_append(&trie->leaves, &leaf->link); leaf->value = NULL; leaf->length = length; @@ -361,18 +352,7 @@ static struct trie_leaf *trie_leaf_alloc(struct trie *trie, const void *key, siz /** Free a leaf. */ static void trie_leaf_free(struct trie *trie, struct trie_leaf *leaf) { - if (leaf->prev) { - leaf->prev->next = leaf->next; - } else { - trie->head = leaf->next; - } - - if (leaf->next) { - leaf->next->prev = leaf->prev; - } else { - trie->tail = leaf->prev; - } - + list_remove(&trie->leaves, &leaf->link); free(leaf); } diff --git a/src/trie.h b/src/trie.h index 03ee64d..6e1e875 100644 --- a/src/trie.h +++ b/src/trie.h @@ -4,6 +4,8 @@ #ifndef BFS_TRIE_H #define BFS_TRIE_H +#include "config.h" +#include "list.h" #include #include #include @@ -12,24 +14,13 @@ * A leaf of a trie. */ struct trie_leaf { - /** - * Linked list of leaves, in insertion order. - */ - struct trie_leaf *prev, *next; - - /** - * An arbitrary value associated with this leaf. - */ + /** Linked list of leaves, in insertion order. */ + struct link link; + /** An arbitrary value associated with this leaf. */ void *value; - - /** - * The length of the key in bytes. - */ + /** The length of the key in bytes. */ size_t length; - - /** - * The key itself, stored inline. - */ + /** The key itself, stored inline. */ char key[]; }; @@ -40,7 +31,7 @@ struct trie { /** Pointer to the root node/leaf. */ uintptr_t root; /** Linked list of leaves. */ - struct trie_leaf *head, *tail; + struct list leaves; }; /** @@ -142,9 +133,7 @@ void trie_destroy(struct trie *trie); /** * Iterate over the leaves of a trie. */ -#define TRIE_FOR_EACH(trie, leaf) \ - for (struct trie_leaf *leaf = (trie)->head, *_next; \ - leaf && (_next = leaf->next, true); \ - leaf = _next) +#define TRIE_FOR_EACH(trie, leaf) \ + LIST_FOR_EACH(&(trie)->leaves, struct trie_leaf, leaf) #endif // BFS_TRIE_H diff --git a/tests/trie.c b/tests/trie.c index c2af18a..65660a9 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -74,8 +74,6 @@ int main(void) { size_t i = 0; TRIE_FOR_EACH(&trie, leaf) { assert(leaf == trie_find_str(&trie, keys[i])); - assert(!leaf->prev || leaf->prev->next == leaf); - assert(!leaf->next || leaf->next->prev == leaf); ++i; } assert(i == nkeys); -- cgit v1.2.3 From 86f4b4f7180bca73a734249e67dada708f8275ff Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 31 Mar 2023 16:19:45 -0400 Subject: list: Use macros instead of type-erased lists --- Makefile | 1 - src/bftw.c | 136 ++++++++++++------ src/config.h | 11 -- src/list.c | 169 ---------------------- src/list.h | 447 ++++++++++++++++++++++++++++++++++++++--------------------- src/trie.c | 8 +- src/trie.h | 9 +- src/xspawn.c | 15 +- src/xspawn.h | 4 +- tests/trie.c | 2 + 10 files changed, 403 insertions(+), 399 deletions(-) delete mode 100644 src/list.c (limited to 'tests') diff --git a/Makefile b/Makefile index 428a0f7..b509065 100644 --- a/Makefile +++ b/Makefile @@ -223,7 +223,6 @@ LIBBFS := \ $(OBJ)/src/eval.o \ $(OBJ)/src/exec.o \ $(OBJ)/src/fsade.o \ - $(OBJ)/src/list.o \ $(OBJ)/src/mtab.o \ $(OBJ)/src/opt.o \ $(OBJ)/src/parse.o \ diff --git a/src/bftw.c b/src/bftw.c index aa36b87..7bc724a 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -7,6 +7,8 @@ * - struct bftw_file: A file that has been encountered during the traversal. * They have reference-counted links to their parents in the directory tree. * + * - struct bftw_list: A linked list of bftw_file's. + * * - struct bftw_cache: An LRU list of bftw_file's with open file descriptors, * used for openat() to minimize the amount of path re-traversals. * @@ -39,11 +41,14 @@ struct bftw_file { struct bftw_file *parent; /** The root under which this file was found. */ struct bftw_file *root; + /** The next file in the queue, if any. */ + struct bftw_file *next; - /** Queue link. */ - struct slink link; - /** LRU link. */ - struct link lru; + /** LRU list links. */ + struct { + struct bftw_file *prev; + struct bftw_file *next; + } lru; /** This file's depth in the walk. */ size_t depth; @@ -68,36 +73,42 @@ struct bftw_file { char name[]; }; -/** Move from a list entry to a bftw_file. */ -#define BFTW_FILE(...) \ - LIST_ITEM(struct bftw_file, __VA_ARGS__) +/** + * A linked list of bftw_file's. + */ +struct bftw_list { + struct bftw_file *head; + struct bftw_file **tail; +}; /** * A cache of open directories. */ struct bftw_cache { - /** The LRU list. */ - struct list list; + /** The head of the LRU list. */ + struct bftw_file *head; + /** The tail of the LRU list. */ + struct bftw_file *tail; /** The insertion target for the LRU list. */ - struct link *target; + struct bftw_file *target; /** The remaining capacity of the LRU list. */ size_t capacity; }; /** Initialize a cache. */ static void bftw_cache_init(struct bftw_cache *cache, size_t capacity) { - list_init(&cache->list); + LIST_INIT(cache); cache->target = NULL; cache->capacity = capacity; } /** Remove a bftw_file from the cache. */ static void bftw_cache_remove(struct bftw_cache *cache, struct bftw_file *file) { - if (cache->target == &file->lru) { - cache->target = cache->target->prev; + if (cache->target == file) { + cache->target = file->lru.prev; } - list_remove(&cache->list, &file->lru); + LIST_REMOVE(cache, file, lru); ++cache->capacity; } @@ -106,7 +117,7 @@ static void bftw_cache_remove(struct bftw_cache *cache, struct bftw_file *file) static void bftw_file_close(struct bftw_cache *cache, struct bftw_file *file) { assert(file->fd >= 0); - if (list_attached(&cache->list, &file->lru)) { + if (LIST_ATTACHED(cache, file, lru)) { bftw_cache_remove(cache, file); } @@ -116,11 +127,11 @@ static void bftw_file_close(struct bftw_cache *cache, struct bftw_file *file) { /** Pop the least recently used directory from the cache. */ static int bftw_cache_pop(struct bftw_cache *cache) { - if (list_is_empty(&cache->list)) { + struct bftw_file *file = cache->tail; + if (!file) { return -1; } - struct bftw_file *file = BFTW_FILE(cache->list.tail, lru); bftw_file_close(cache, file); return 0; } @@ -138,11 +149,11 @@ static int bftw_cache_add(struct bftw_cache *cache, struct bftw_file *file) { assert(cache->capacity > 0); --cache->capacity; - list_insert_after(&cache->list, cache->target, &file->lru); + LIST_INSERT(cache, cache->target, file, lru); // Prefer to keep the root paths open by keeping them at the head of the list if (file->depth == 0) { - cache->target = &file->lru; + cache->target = file; } return 0; @@ -159,8 +170,9 @@ static size_t bftw_child_nameoff(const struct bftw_file *parent) { /** Destroy a cache. */ static void bftw_cache_destroy(struct bftw_cache *cache) { + assert(!cache->head); + assert(!cache->tail); assert(!cache->target); - assert(list_is_empty(&cache->list)); } /** Create a new bftw_file. */ @@ -186,8 +198,8 @@ static struct bftw_file *bftw_file_new(struct bftw_file *parent, const char *nam file->nameoff = 0; } - slink_init(&file->link); - link_init(&file->lru); + file->next = NULL; + file->lru.prev = file->lru.next = NULL; file->refcount = 1; file->fd = -1; @@ -280,16 +292,19 @@ static int bftw_file_open(struct bftw_cache *cache, struct bftw_file *file, cons } // Handle ENAMETOOLONG by manually traversing the path component-by-component - struct slist parents; - slist_init(&parents); - for (struct bftw_file *cur = file; cur != base; cur = cur->parent) { - slist_prepend(&parents, &cur->link); + struct bftw_list parents; + SLIST_INIT(&parents); + + struct bftw_file *cur; + for (cur = file; cur != base; cur = cur->parent) { + SLIST_PREPEND(&parents, cur); } - LIST_DRAIN(&parents, struct bftw_file, cur) { + while ((cur = parents.head)) { if (!cur->parent || cur->parent->fd >= 0) { bftw_file_openat(cache, cur, cur->parent, cur->name); } + SLIST_POP(&parents); } return file->fd; @@ -348,9 +363,9 @@ struct bftw_state { /** The cache of open directories. */ struct bftw_cache cache; /** The queue of files left to explore. */ - struct slist queue; + struct bftw_list queue; /** A batch of files to enqueue. */ - struct slist batch; + struct bftw_list batch; /** The current path. */ char *path; @@ -396,8 +411,8 @@ static int bftw_state_init(struct bftw_state *state, const struct bftw_args *arg bftw_cache_init(&state->cache, args->nopenfd); - slist_init(&state->queue); - slist_init(&state->batch); + SLIST_INIT(&state->queue); + SLIST_INIT(&state->batch); state->file = NULL; state->previous = NULL; @@ -757,7 +772,7 @@ static int bftw_push(struct bftw_state *state, const char *name, bool fill_id) { bftw_fill_id(file, &state->ftwbuf); } - slist_append(&state->batch, &file->link); + SLIST_APPEND(&state->batch, file); return 0; } @@ -800,11 +815,12 @@ static int bftw_build_path(struct bftw_state *state) { * Pop the next file from the queue. */ static int bftw_pop(struct bftw_state *state) { - if (!state->queue.head) { + state->file = state->queue.head; + if (!state->file) { return 0; } - state->file = BFTW_FILE(slist_pop(&state->queue)); + SLIST_POP(&state->queue); if (bftw_build_path(state) != 0) { return -1; @@ -940,8 +956,10 @@ static enum bftw_action bftw_gc_file(struct bftw_state *state, enum bftw_gc_flag * Drain all the files from the queue. */ static void bftw_drain_queue(struct bftw_state *state) { - LIST_DRAIN(&state->queue, struct bftw_file, file) { - state->file = file; + while (state->queue.head) { + state->file = state->queue.head; + SLIST_POP(&state->queue); + bftw_gc_file(state, BFTW_VISIT_NONE); } } @@ -966,21 +984,55 @@ static int bftw_state_destroy(struct bftw_state *state) { return state->error ? -1 : 0; } -/** Comparison function for BFTW_SORT. */ -static bool bftw_file_cmp(struct slink *left, struct slink *right, const void *ptr) { - return strcoll(BFTW_FILE(left)->name, BFTW_FILE(right)->name) <= 0; +/** Sort a bftw_list by filename. */ +static void bftw_list_sort(struct bftw_list *queue) { + if (!queue->head || !queue->head->next) { + return; + } + + struct bftw_list left, right; + SLIST_INIT(&left); + SLIST_INIT(&right); + + // Split + for (struct bftw_file *hare = queue->head; hare && (hare = hare->next); hare = hare->next) { + struct bftw_file *tortoise = queue->head; + SLIST_POP(queue); + SLIST_APPEND(&left, tortoise); + } + SLIST_EXTEND(&right, queue); + + // Recurse + bftw_list_sort(&left); + bftw_list_sort(&right); + + // Merge + while (left.head && right.head) { + struct bftw_file *lf = left.head; + struct bftw_file *rf = right.head; + + if (strcoll(lf->name, rf->name) <= 0) { + SLIST_POP(&left); + SLIST_APPEND(queue, lf); + } else { + SLIST_POP(&right); + SLIST_APPEND(queue, rf); + } + } + SLIST_EXTEND(queue, &left); + SLIST_EXTEND(queue, &right); } /** Finish adding a batch of files. */ static void bftw_batch_finish(struct bftw_state *state) { if (state->flags & BFTW_SORT) { - slist_sort(&state->batch, bftw_file_cmp, NULL); + bftw_list_sort(&state->batch); } if (state->strategy == BFTW_DFS) { - slist_extend(&state->batch, &state->queue); + SLIST_EXTEND(&state->batch, &state->queue); } - slist_extend(&state->queue, &state->batch); + SLIST_EXTEND(&state->queue, &state->batch); } /** diff --git a/src/config.h b/src/config.h index cee8511..34ae11d 100644 --- a/src/config.h +++ b/src/config.h @@ -147,17 +147,6 @@ */ #define BFS_COUNTOF(array) (sizeof(array) / sizeof(0[array])) -// BFS_CONTAINER_OF() helper -static inline char *bfs_container_offset(char *ptr, ptrdiff_t offset, size_t unused) { - return ptr ? ptr - offset : NULL; -} - -/** - * Move a pointer from a field to its outer struct. - */ -#define BFS_CONTAINER_OF(ptr, type, member) \ - ((type *)bfs_container_offset((char *)(ptr), offsetof(type, member), sizeof((ptr) - &((type *)NULL)->member))) - // Lower bound on BFS_FLEX_SIZEOF() #define BFS_FLEX_LB(type, member, length) (offsetof(type, member) + sizeof(((type *)NULL)->member[0]) * (length)) diff --git a/src/list.c b/src/list.c deleted file mode 100644 index dffee19..0000000 --- a/src/list.c +++ /dev/null @@ -1,169 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include "list.h" -#include -#include - -void slink_init(struct slink *link) { - link->next = NULL; -} - -void slist_init(struct slist *list) { - list->head = NULL; - list->tail = &list->head; -} - -bool slist_is_empty(const struct slist *list) { - return !list->head; -} - -void slist_append(struct slist *list, struct slink *link) { - assert(!link->next); - *list->tail = link; - list->tail = &link->next; -} - -void slist_prepend(struct slist *list, struct slink *link) { - assert(!link->next); - if (!list->head) { - list->tail = &link->next; - } - link->next = list->head; - list->head = link; -} - -void slist_extend(struct slist *dest, struct slist *src) { - if (src->head) { - *dest->tail = src->head; - dest->tail = src->tail; - slist_init(src); - } -} - -struct slink *slist_pop(struct slist *list) { - struct slink *head = list->head; - if (!head) { - return NULL; - } - - list->head = head->next; - if (!list->head) { - list->tail = &list->head; - } - - head->next = NULL; - return head; -} - -void slist_sort(struct slist *list, slist_cmp_fn *cmp_fn, const void *ptr) { - if (!list->head || !list->head->next) { - return; - } - - struct slist left, right; - slist_init(&left); - slist_init(&right); - - // Split - for (struct slink *hare = list->head; hare && (hare = hare->next); hare = hare->next) { - slist_append(&left, slist_pop(list)); - } - slist_extend(&right, list); - - // Recurse - slist_sort(&left, cmp_fn, ptr); - slist_sort(&right, cmp_fn, ptr); - - // Merge - while (left.head && right.head) { - if (cmp_fn(left.head, right.head, ptr)) { - slist_append(list, slist_pop(&left)); - } else { - slist_append(list, slist_pop(&right)); - } - } - slist_extend(list, &left); - slist_extend(list, &right); -} - -void link_init(struct link *link) { - link->prev = NULL; - link->next = NULL; -} - -void list_init(struct list *list) { - list->head = NULL; - list->tail = NULL; -} - -bool list_is_empty(const struct list *list) { - return !list->head; -} - -void list_append(struct list *list, struct link *link) { - list_insert_after(list, list->tail, link); -} - -void list_prepend(struct list *list, struct link *link) { - list_insert_after(list, NULL, link); -} - -void list_insert_after(struct list *list, struct link *target, struct link *link) { - assert(!list_attached(list, link)); - - if (target) { - link->prev = target; - link->next = target->next; - } else { - link->next = list->head; - } - - if (link->prev) { - link->prev->next = link; - } else { - list->head = link; - } - - if (link->next) { - link->next->prev = link; - } else { - list->tail = link; - } -} - -void list_remove(struct list *list, struct link *link) { - if (link->prev) { - assert(list->head != link); - link->prev->next = link->next; - } else { - assert(list->head == link); - list->head = link->next; - } - - if (link->next) { - assert(list->tail != link); - link->next->prev = link->prev; - } else { - assert(list->tail == link); - list->tail = link->prev; - } - - link->prev = NULL; - link->next = NULL; -} - -struct link *list_pop(struct list *list) { - struct link *head = list->head; - if (!head) { - return NULL; - } - - list_remove(list, head); - return head; -} - -bool list_attached(const struct list *list, const struct link *link) { - return link->prev || list->head == link - || link->next || list->tail == link; -} diff --git a/src/list.h b/src/list.h index b47bed7..c43be68 100644 --- a/src/list.h +++ b/src/list.h @@ -3,225 +3,354 @@ /** * Intrusive linked lists. + * + * Singly-linked lists are declared like this: + * + * struct item { + * struct item *next; + * }; + * + * struct list { + * struct item *head; + * struct item **tail; + * }; + * + * The SLIST_*() macros manipulate singly-linked lists. + * + * struct list list; + * SLIST_INIT(&list); + * + * struct item item; + * SLIST_APPEND(&list, &item); + * + * Doubly linked lists are similar: + * + * struct item { + * struct item *next; + * struct item *prev; + * }; + * + * struct list { + * struct item *head; + * struct item *tail; + * }; + * + * struct list list; + * LIST_INIT(&list); + * + * struct item item; + * LIST_APPEND(&list, &item); + * + * Items can be on multiple lists at once: + * + * struct item { + * struct { + * struct item *next; + * } chain; + * + * struct { + * struct item *next; + * struct item *prev; + * } lru; + * }; + * + * struct items { + * struct { + * struct item *head; + * struct item **tail; + * } queue; + * + * struct { + * struct item *head; + * struct item *tail; + * } cache; + * }; + * + * struct items items; + * SLIST_INIT(&items.queue); + * LIST_INIT(&items.cache); + * + * struct item item; + * SLIST_APPEND(&items.queue, &item, chain); + * LIST_APPEND(&items.cache, &item, lru); */ #ifndef BFS_LIST_H #define BFS_LIST_H -#include "config.h" -#include +#include /** - * A singly-linked list entry. + * Initialize a singly-linked list. + * + * @param list + * The list to initialize. + * + * --- + * + * Like many macros in this file, this macro delegates the bulk of its work to + * some helper macros. We explicitly parenthesize (list) here so the helpers + * don't have to. */ -struct slink { - struct slink *next; -}; +#define SLIST_INIT(list) \ + LIST_BLOCK_(SLIST_INIT_((list))) -/** Initialize a list entry. */ -void slink_init(struct slink *link); +#define SLIST_INIT_(list) \ + (list)->head = NULL; \ + (list)->tail = &(list)->head; /** - * A singly-linked list. + * Wraps a group of statements in a block. */ -struct slist { - struct slink *head; - struct slink **tail; -}; - -/** Initialize an empty list. */ -void slist_init(struct slist *list); - -/** Check if a list is empty. */ -bool slist_is_empty(const struct slist *list); - -/** Add an entry at the tail of the list. */ -void slist_append(struct slist *list, struct slink *link); - -/** Add an entry at the head of the list. */ -void slist_prepend(struct slist *list, struct slink *link); - -/** Add an entire list at the tail of the list. */ -void slist_extend(struct slist *dest, struct slist *src); - -/** Remove the head of the list. */ -struct slink *slist_pop(struct slist *list); +#define LIST_BLOCK_(block) do { block } while (0) /** - * Comparison function type for slist_sort(). + * Add an item to the tail of a singly-linked list. * - * @param left - * The left-hand side of the comparison. - * @param right - * The right-hand side of the comparison. - * @param ptr - * An arbitrary pointer passed to slist_sort(). - * @return - * Whether left <= right. + * @param list + * The list to modify. + * @param item + * The item to append. + * @param link (optional) + * If specified, use item->link.next rather than item->next. + * + * --- + * + * We play some tricks with variadic macros to handle the optional parameter: + * + * SLIST_APPEND(list, item) => { + * *list->tail = item; + * list->tail = &item->next; + * } + * + * SLIST_APPEND(list, item, link) => { + * *list->tail = item; + * list->tail = &item->link.next; + * } + * + * The first trick is that + * + * #define SLIST_APPEND(list, item, ...) + * + * won't work because both commas are required (until C23; see N3033). As a + * workaround, we dispatch to another macro and add a trailing comma. + * + * SLIST_APPEND(list, item) => SLIST_APPEND_(list, item, ) + * SLIST_APPEND(list, item, link) => SLIST_APPEND_(list, item, link, ) */ -typedef bool slist_cmp_fn(struct slink *left, struct slink *right, const void *ptr); +#define SLIST_APPEND(list, ...) SLIST_APPEND_(list, __VA_ARGS__, ) -/** Sort a list. */ -void slist_sort(struct slist *list, slist_cmp_fn *cmp_fn, const void *ptr); +/** + * Now we need a way to generate either ->next or ->link.next depending on + * whether the link parameter was passed. The approach is based on + * + * #define FOO(...) BAR(__VA_ARGS__, 1, 2, ) + * #define BAR(x, y, z, ...) z + * + * FOO(a) => 2 + * FOO(a, b) => 1 + * + * The LIST_NEXT_() macro uses this technique: + * + * LIST_NEXT_() => LIST_LINK_(next, ) + * LIST_NEXT_(link, ) => LIST_LINK_(next, link, ) + */ +#define LIST_NEXT_(...) LIST_LINK_(next, __VA_ARGS__) /** - * A doubly-linked list entry. + * LIST_LINK_() dispatches to yet another macro: + * + * LIST_LINK_(next, ) => LIST_LINK__(next, , , . , , ) + * LIST_LINK_(next, link, ) => LIST_LINK__(next, link, , , . , , ) */ -struct link { - struct link *prev; - struct link *next; -}; +#define LIST_LINK_(dir, ...) LIST_LINK__(dir, __VA_ARGS__, , . , , ) -/** Initialize a list entry. */ -void link_init(struct link *link); +/** + * And finally, LIST_LINK__() adds the link and the dot if necessary. + * + * dir link blank ignored dot + * v v v v v + * LIST_LINK__(next, , , . , , ) => next + * LIST_LINK__(next, link, , , . , , ) => link . next + * ^ ^ ^ ^ ^ + * dir link blank ignored dot + */ +#define LIST_LINK__(dir, link, blank, ignored, dot, ...) link dot dir /** - * A doubly-linked list. + * SLIST_APPEND_() uses LIST_NEXT_() to generate the right name for the list + * link, and finally delegates to the actual implementation. + * + * SLIST_APPEND_(list, item, ) => SLIST_APPEND__((list), (item), next) + * SLIST_APPEND_(list, item, link, ) => SLIST_APPEND__((list), (item), link.next) */ -struct list { - struct link *head; - struct link *tail; -}; +#define SLIST_APPEND_(list, item, ...) \ + LIST_BLOCK_(SLIST_APPEND__((list), (item), LIST_NEXT_(__VA_ARGS__))) -/** Initialize an empty list. */ -void list_init(struct list *list); +#define SLIST_APPEND__(list, item, next) \ + *list->tail = item; \ + list->tail = &item->next; -/** Check if a list is empty. */ -bool list_is_empty(const struct list *list); +/** + * Add an item to the head of a singly-linked list. + * + * @param list + * The list to modify. + * @param item + * The item to prepend. + * @param link (optional) + * If specified, use item->link.next rather than item->next. + */ +#define SLIST_PREPEND(list, ...) SLIST_PREPEND_(list, __VA_ARGS__, ) -/** Add an entry at the tail of the list. */ -void list_append(struct list *list, struct link *link); +#define SLIST_PREPEND_(list, item, ...) \ + LIST_BLOCK_(SLIST_PREPEND__((list), (item), LIST_NEXT_(__VA_ARGS__))) -/** Add an entry at the head of the list. */ -void list_prepend(struct list *list, struct link *link); +#define SLIST_PREPEND__(list, item, next) \ + list->tail = list->head ? list->tail : &item->next; \ + item->next = list->head; \ + list->head = item; -/** Insert an entry after the target entry. */ -void list_insert_after(struct list *list, struct link *target, struct link *link); +/** + * Add an entire singly-linked list to the tail of another. + * + * @param dest + * The destination list. + * @param src + * The source list. + */ +#define SLIST_EXTEND(dest, src) \ + LIST_BLOCK_(SLIST_EXTEND_((dest), (src))) -/** Remove an entry from a list. */ -void list_remove(struct list *list, struct link *link); +#define SLIST_EXTEND_(dest, src) \ + if (src->head) { \ + *dest->tail = src->head; \ + dest->tail = src->tail; \ + SLIST_INIT(src); \ + } -/** Remove the head of the list. */ -struct link *list_pop(struct list *list); +/** + * Pop the head off a singly-linked list. + * + * @param list + * The list to pop from. + * @param link (optional) + * If specified, use head->link.next rather than head->next. + */ +#define SLIST_POP(...) SLIST_POP_(__VA_ARGS__, ) -/** Check if a link is attached to a list. */ -bool list_attached(const struct list *list, const struct link *link); +#define SLIST_POP_(list, ...) \ + LIST_BLOCK_(SLIST_POP__((list), LIST_NEXT_(__VA_ARGS__))) -// LIST_ITEM() helper -#define LIST_ITEM_IMPL(type, entry, member, ...) \ - BFS_CONTAINER_OF(entry, type, member) +#define SLIST_POP__(list, next) \ + void *_next = (void *)list->head->next; \ + list->head->next = NULL; \ + list->head = _next; \ + list->tail = list->head ? list->tail : &list->head; /** - * Convert a list entry to its container. + * Initialize a doubly-linked list. * - * @param type - * The type of the list entries. - * @param entry - * The list entry to convert. - * @param member - * The name of the list link field (default: link). - * @return - * The item that contains the given entry. + * @param list + * The list to initialize. */ -#define LIST_ITEM(...) \ - LIST_ITEM_IMPL(__VA_ARGS__, link,) +#define LIST_INIT(list) \ + LIST_BLOCK_(LIST_INIT_((list))) -// LIST_NEXT() helper -#define LIST_NEXT_IMPL(type, entry, member, ...) \ - LIST_ITEM(type, (entry)->member.next, member) +#define LIST_INIT_(list) \ + list->head = list->tail = NULL; /** - * Get the next item in a list. - * - * @param type - * The type of the list entries. - * @param entry - * The current entry. - * @param member - * The name of the list link field (default: link). - * @return - * The next item in the list. + * LIST_PREV_() => prev + * LIST_PREV_(link, ) => link.prev */ -#define LIST_NEXT(...) \ - LIST_NEXT_IMPL(__VA_ARGS__, link,) - -// LIST_PREV() helper -#define LIST_PREV_IMPL(type, entry, member, ...) \ - LIST_ITEM(type, (entry)->member.prev, member) +#define LIST_PREV_(...) LIST_LINK_(prev, __VA_ARGS__) /** - * Get the previous entry in a list. + * Add an item to the tail of a doubly-linked list. * - * @param type - * The type of the list entries. - * @param entry - * The current entry. - * @param member - * The name of the list link field (default: link). - * @return - * The previous item in the list. + * @param list + * The list to modify. + * @param item + * The item to append. + * @param link (optional) + * If specified, use item->link.{prev,next} rather than item->{prev,next}. */ -#define LIST_PREV(...) \ - LIST_PREV_IMPL(__VA_ARGS__, link,) +#define LIST_APPEND(list, ...) LIST_INSERT(list, (list)->tail, __VA_ARGS__) -// Helper for LIST_FOR_EACH_*() -#define LIST_FOR_EACH_IMPL(entry, type, i, member, ...) \ - for (type *_next, *i = LIST_ITEM(type, entry, member); \ - i && (_next = LIST_NEXT(type, i, member), true); \ - i = _next) +/** + * Add an item to the head of a doubly-linked list. + * + * @param list + * The list to modify. + * @param item + * The item to prepend. + * @param link (optional) + * If specified, use item->link.{prev,next} rather than item->{prev,next}. + */ +#define LIST_PREPEND(list, ...) LIST_INSERT(list, NULL, __VA_ARGS__) /** - * Iterate over a list from the given entry. + * Insert into a doubly-linked list after the given cursor. * - * @param entry - * The entry to start from. - * @param type - * The type of the list entries. - * @param i - * The name of the loop variable, declared as type *i. - * @param member - * The name of the list link field (default: link). + * @param list + * The list to initialize. + * @param cursor + * Insert after this element. + * @param item + * The item to insert. + * @param link (optional) + * If specified, use item->link.{prev,next} rather than item->{prev,next}. */ -#define LIST_FOR_EACH_FROM(...) \ - LIST_FOR_EACH_IMPL(__VA_ARGS__, link,) +#define LIST_INSERT(list, cursor, ...) LIST_INSERT_(list, cursor, __VA_ARGS__, ) + +#define LIST_INSERT_(list, cursor, item, ...) \ + LIST_BLOCK_(LIST_INSERT__((list), (cursor), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))) + +#define LIST_INSERT__(list, cursor, item, prev, next) \ + item->prev = cursor; \ + item->next = cursor ? cursor->next : list->head; \ + *(item->prev ? &item->prev->next : &list->head) = item; \ + *(item->next ? &item->next->prev : &list->tail) = item; /** - * Iterate over a list. + * Remove an item from a doubly-linked list. * * @param list - * The list to iterate over. - * @param type - * The type of the list entries. - * @param i - * The name of the loop variable, declared as type *i. - * @param member - * The name of the list link field (default: link). + * The list to modify. + * @param item + * The item to remove. + * @param link (optional) + * If specified, use item->link.{prev,next} rather than item->{prev,next}. */ -#define LIST_FOR_EACH(list, ...) \ - LIST_FOR_EACH_FROM((list)->head, __VA_ARGS__) +#define LIST_REMOVE(list, ...) LIST_REMOVE_(list, __VA_ARGS__, ) -// Pop from a list or slist -#define LIST_POP(l) _Generic((l), \ - struct list *: list_pop((struct list *)l), \ - struct slist *: slist_pop((struct slist *)l)) +#define LIST_REMOVE_(list, item, ...) \ + LIST_BLOCK_(LIST_REMOVE__((list), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__))) -// Helper for LIST_DRAIN() -#define LIST_DRAIN_IMPL(list, type, i, member, ...) \ - for (type *i; (i = LIST_ITEM(type, LIST_POP(list), member));) +#define LIST_REMOVE__(list, item, prev, next) \ + *(item->prev ? &item->prev->next : &list->head) = item->next; \ + *(item->next ? &item->next->prev : &list->tail) = item->prev; \ + item->prev = item->next = NULL; /** - * Drain the entries from a list. + * Check if an item is attached to a doubly-linked list. * * @param list - * The list to drain. - * @param type - * The type of the list entries. - * @param i - * The name of the loop variable, declared as type *i. - * @param member - * The name of the list link field (default: link). - */ -#define LIST_DRAIN(...) \ - LIST_DRAIN_IMPL(__VA_ARGS__, link,) + * The list to check. + * @param item + * The item to check. + * @param link (optional) + * If specified, use item->link.{prev,next} rather than item->{prev,next}. + * @return + * Whether the item is attached to the list. + */ +#define LIST_ATTACHED(list, ...) LIST_ATTACHED_(list, __VA_ARGS__, ) + +#define LIST_ATTACHED_(list, item, ...) \ + LIST_ATTACHED__((list), (item), LIST_PREV_(__VA_ARGS__), LIST_NEXT_(__VA_ARGS__)) + +#define LIST_ATTACHED__(list, item, prev, next) \ + (item->prev || item->next || list->head == item || list->tail == item) #endif // BFS_LIST_H diff --git a/src/trie.c b/src/trie.c index 7b00f4b..43df9dc 100644 --- a/src/trie.c +++ b/src/trie.c @@ -83,6 +83,7 @@ #include "trie.h" #include "config.h" +#include "list.h" #include #include #include @@ -163,7 +164,7 @@ static uintptr_t trie_encode_node(const struct trie_node *node) { void trie_init(struct trie *trie) { trie->root = 0; - list_init(&trie->leaves); + LIST_INIT(trie); } /** Check if a number is a power of two. */ @@ -340,8 +341,7 @@ static struct trie_leaf *trie_leaf_alloc(struct trie *trie, const void *key, siz return NULL; } - link_init(&leaf->link); - list_append(&trie->leaves, &leaf->link); + LIST_APPEND(trie, leaf); leaf->value = NULL; leaf->length = length; @@ -352,7 +352,7 @@ static struct trie_leaf *trie_leaf_alloc(struct trie *trie, const void *key, siz /** Free a leaf. */ static void trie_leaf_free(struct trie *trie, struct trie_leaf *leaf) { - list_remove(&trie->leaves, &leaf->link); + LIST_REMOVE(trie, leaf); free(leaf); } diff --git a/src/trie.h b/src/trie.h index 6e1e875..58974aa 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "config.h" -#include "list.h" #include #include #include @@ -15,7 +14,7 @@ */ struct trie_leaf { /** Linked list of leaves, in insertion order. */ - struct link link; + struct trie_leaf *prev, *next; /** An arbitrary value associated with this leaf. */ void *value; /** The length of the key in bytes. */ @@ -31,7 +30,7 @@ struct trie { /** Pointer to the root node/leaf. */ uintptr_t root; /** Linked list of leaves. */ - struct list leaves; + struct trie_leaf *head, *tail; }; /** @@ -134,6 +133,8 @@ void trie_destroy(struct trie *trie); * Iterate over the leaves of a trie. */ #define TRIE_FOR_EACH(trie, leaf) \ - LIST_FOR_EACH(&(trie)->leaves, struct trie_leaf, leaf) + for (struct trie_leaf *leaf = (trie)->head, *_next; \ + leaf && (_next = leaf->next, true); \ + leaf = _next) #endif // BFS_TRIE_H diff --git a/src/xspawn.c b/src/xspawn.c index e6ce0de..a185200 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -33,7 +33,7 @@ enum bfs_spawn_op { * A spawn action. */ struct bfs_spawn_action { - struct slink link; + struct bfs_spawn_action *next; enum bfs_spawn_op op; int in_fd; @@ -44,12 +44,14 @@ struct bfs_spawn_action { int bfs_spawn_init(struct bfs_spawn *ctx) { ctx->flags = 0; - slist_init(&ctx->actions); + SLIST_INIT(ctx); return 0; } int bfs_spawn_destroy(struct bfs_spawn *ctx) { - LIST_DRAIN(&ctx->actions, struct bfs_spawn_action, action) { + while (ctx->head) { + struct bfs_spawn_action *action = ctx->head; + SLIST_POP(ctx); free(action); } @@ -68,12 +70,12 @@ static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_sp return NULL; } - slink_init(&action->link); + action->next = NULL; action->op = op; action->in_fd = -1; action->out_fd = -1; - slist_append(&ctx->actions, &action->link); + SLIST_APPEND(ctx, action); return action; } @@ -138,8 +140,7 @@ int bfs_spawn_addsetrlimit(struct bfs_spawn *ctx, int resource, const struct rli static void bfs_spawn_exec(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp, int pipefd[2]) { xclose(pipefd[0]); - const struct slink *head = ctx ? ctx->actions.head : NULL; - LIST_FOR_EACH_FROM(head, struct bfs_spawn_action, action) { + for (const struct bfs_spawn_action *action = ctx ? ctx->head : NULL; action; action = action->next) { // Move the error-reporting pipe out of the way if necessary... if (action->out_fd == pipefd[1]) { int fd = dup_cloexec(pipefd[1]); diff --git a/src/xspawn.h b/src/xspawn.h index 7e673f1..d9b4a2e 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -8,7 +8,6 @@ #ifndef BFS_XSPAWN_H #define BFS_XSPAWN_H -#include "list.h" #include #include @@ -25,7 +24,8 @@ enum bfs_spawn_flags { */ struct bfs_spawn { enum bfs_spawn_flags flags; - struct slist actions; + struct bfs_spawn_action *head; + struct bfs_spawn_action **tail; }; /** diff --git a/tests/trie.c b/tests/trie.c index 65660a9..c2af18a 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -74,6 +74,8 @@ int main(void) { size_t i = 0; TRIE_FOR_EACH(&trie, leaf) { assert(leaf == trie_find_str(&trie, keys[i])); + assert(!leaf->prev || leaf->prev->next == leaf); + assert(!leaf->next || leaf->next->prev == leaf); ++i; } assert(i == nkeys); -- cgit v1.2.3 From a3bed764555f76003bb03c023123846fdff76f1b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 6 Apr 2023 14:52:19 -0400 Subject: build: Fix tsan --- Makefile | 72 +++++++++++++++++++++++++++++++--------------------------- tests/tests.sh | 11 +++++---- 2 files changed, 45 insertions(+), 38 deletions(-) (limited to 'tests') diff --git a/Makefile b/Makefile index b509065..57ff238 100644 --- a/Makefile +++ b/Makefile @@ -65,7 +65,42 @@ MSAN := $(filter msan,$(MAKECMDGOALS)) TSAN := $(filter tsan,$(MAKECMDGOALS)) UBSAN := $(filter ubsan,$(MAKECMDGOALS)) -ifndef MSAN +ifdef ASAN +LOCAL_CFLAGS += -fsanitize=address +SANITIZE := y +endif + +ifdef LSAN +LOCAL_CFLAGS += -fsanitize=leak +SANITIZE := y +endif + +ifdef MSAN +# msan needs all code instrumented +NOLIBS := y +LOCAL_CFLAGS += -fsanitize=memory -fsanitize-memory-track-origins +SANITIZE := y +endif + +ifdef TSAN +# tsan needs all code instrumented +NOLIBS := y +# https://github.com/google/sanitizers/issues/342 +LOCAL_CPPFLAGS += -DBFS_TARGET_CLONES=false +LOCAL_CFLAGS += -fsanitize=thread +SANITIZE := y +endif + +ifdef UBSAN +LOCAL_CFLAGS += -fsanitize=undefined +SANITIZE := y +endif + +ifdef SANITIZE +LOCAL_CFLAGS += -fno-sanitize-recover=all +endif + +ifndef NOLIBS WITH_ONIGURUMA := y endif @@ -82,10 +117,10 @@ endif LOCAL_CFLAGS += $(ONIG_CFLAGS) LOCAL_LDLIBS += $(ONIG_LDLIBS) -endif +endif # WITH_ONIGURUMA ifeq ($(OS),Linux) -ifndef MSAN # These libraries are not built with msan +ifndef NOLIBS WITH_ACL := y WITH_ATTR := y WITH_LIBCAP := y @@ -111,41 +146,12 @@ endif LOCAL_LDFLAGS += -Wl,--as-needed LOCAL_LDLIBS += -lrt -endif +endif # Linux ifeq ($(OS),NetBSD) LOCAL_LDLIBS += -lutil endif -ifdef ASAN -LOCAL_CFLAGS += -fsanitize=address -SANITIZE := y -endif - -ifdef LSAN -LOCAL_CFLAGS += -fsanitize=leak -SANITIZE := y -endif - -ifdef MSAN -LOCAL_CFLAGS += -fsanitize=memory -fsanitize-memory-track-origins -SANITIZE := y -endif - -ifdef TSAN -LOCAL_CFLAGS += -fsanitize=thread -SANITIZE := y -endif - -ifdef UBSAN -LOCAL_CFLAGS += -fsanitize=undefined -SANITIZE := y -endif - -ifdef SANITIZE -LOCAL_CFLAGS += -fno-sanitize-recover=all -endif - ifneq ($(filter gcov,$(MAKECMDGOALS)),) LOCAL_CFLAGS += --coverage # gcov only intercepts fork()/exec() with -std=gnu* diff --git a/tests/tests.sh b/tests/tests.sh index 98d332c..9265481 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -9,11 +9,12 @@ umask 022 export LC_ALL=C export TZ=UTC0 -export ASAN_OPTIONS="abort_on_error=1:log_to_syslog=0" -export LSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" -export MSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" -export TSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" -export UBSAN_OPTIONS="abort_on_error=1:log_to_syslog=0" +SAN_OPTIONS="halt_on_error=1:log_to_syslog=0" +export ASAN_OPTIONS="$SAN_OPTIONS" +export LSAN_OPTIONS="$SAN_OPTIONS" +export MSAN_OPTIONS="$SAN_OPTIONS" +export TSAN_OPTIONS="$SAN_OPTIONS" +export UBSAN_OPTIONS="$SAN_OPTIONS" export LS_COLORS="" unset BFS_COLORS -- cgit v1.2.3 From 8368e139c176dcde3b125f4a180ff868729b1862 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 10 May 2023 15:42:54 -0400 Subject: config: s/BFS_COUNTOF/countof/ --- src/config.h | 2 +- src/fsade.c | 2 +- src/opt.c | 16 ++++++++-------- tests/trie.c | 2 +- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/src/config.h b/src/config.h index e17f90b..60bc930 100644 --- a/src/config.h +++ b/src/config.h @@ -134,7 +134,7 @@ /** * Get the length of an array. */ -#define BFS_COUNTOF(array) (sizeof(array) / sizeof(0[array])) +#define countof(array) (sizeof(array) / sizeof(0[array])) // Lower bound on BFS_FLEX_SIZEOF() #define BFS_FLEX_LB(type, member, length) (offsetof(type, member) + sizeof(((type *)NULL)->member[0]) * (length)) diff --git a/src/fsade.c b/src/fsade.c index aefbb75..4d67940 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -201,7 +201,7 @@ int bfs_check_acl(const struct BFTW *ftwbuf) { const char *path = fake_at(ftwbuf); int ret = -1, error = 0; - for (size_t i = 0; i < BFS_COUNTOF(acl_types) && ret <= 0; ++i) { + for (size_t i = 0; i < countof(acl_types) && ret <= 0; ++i) { acl_type_t type = acl_types[i]; if (type == ACL_TYPE_DEFAULT && ftwbuf->type != BFS_DIR) { diff --git a/src/opt.c b/src/opt.c index 5505b7b..731dd10 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1154,21 +1154,21 @@ static const struct { * Look up the appropriate optimizer for an expression and call it. */ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs_expr *expr) { - for (size_t i = 0; i < BFS_COUNTOF(opt_pure); ++i) { + for (size_t i = 0; i < countof(opt_pure); ++i) { if (opt_pure[i] == expr->eval_fn) { expr->pure = true; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_always_true); ++i) { + for (size_t i = 0; i < countof(opt_always_true); ++i) { if (opt_always_true[i] == expr->eval_fn) { expr->always_true = true; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_always_false); ++i) { + for (size_t i = 0; i < countof(opt_always_false); ++i) { if (opt_always_false[i] == expr->eval_fn) { expr->always_false = true; break; @@ -1176,35 +1176,35 @@ static struct bfs_expr *optimize_expr_lookup(struct opt_state *state, struct bfs } expr->cost = FAST_COST; - for (size_t i = 0; i < BFS_COUNTOF(opt_costs); ++i) { + for (size_t i = 0; i < countof(opt_costs); ++i) { if (opt_costs[i].eval_fn == expr->eval_fn) { expr->cost = opt_costs[i].cost; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_probs); ++i) { + for (size_t i = 0; i < countof(opt_probs); ++i) { if (opt_probs[i].eval_fn == expr->eval_fn) { expr->probability = opt_probs[i].probability; break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_preds); ++i) { + for (size_t i = 0; i < countof(opt_preds); ++i) { if (opt_preds[i].eval_fn == expr->eval_fn) { infer_pred_facts(state, opt_preds[i].pred); break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_ranges); ++i) { + for (size_t i = 0; i < countof(opt_ranges); ++i) { if (opt_ranges[i].eval_fn == expr->eval_fn) { infer_icmp_facts(state, expr, opt_ranges[i].range); break; } } - for (size_t i = 0; i < BFS_COUNTOF(opt_fns); ++i) { + for (size_t i = 0; i < countof(opt_fns); ++i) { if (opt_fns[i].eval_fn == expr->eval_fn) { return opt_fns[i].opt_fn(state, expr); } diff --git a/tests/trie.c b/tests/trie.c index c2af18a..ced14d4 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -38,7 +38,7 @@ const char *keys[] = { ">>>", }; -const size_t nkeys = BFS_COUNTOF(keys); +const size_t nkeys = countof(keys); int main(void) { struct trie trie; -- cgit v1.2.3 From 59f87eed2b930af2f31fd1d1fb2589f80f426ee0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 11 May 2023 10:12:35 -0400 Subject: config: Provide and In anticipation of C23, since those headers won't be necessary any more. --- src/bfstd.c | 1 - src/bfstd.h | 2 +- src/bftw.c | 1 - src/color.c | 1 - src/color.h | 1 - src/config.h | 6 +++++- src/ctx.h | 2 +- src/diag.h | 1 - src/dir.c | 2 -- src/eval.h | 2 +- src/exec.c | 1 - src/expr.h | 2 +- src/fsade.h | 1 - src/main.c | 2 +- src/mtab.c | 1 - src/mtab.h | 2 +- src/opt.c | 1 - src/parse.c | 1 - src/printf.c | 1 - src/pwcache.c | 2 +- src/stat.c | 1 - src/trie.c | 1 - src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 2 +- tests/xtimegm.c | 2 +- tests/xtouch.c | 2 +- 27 files changed, 15 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index 1dc322b..932f2c4 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -9,7 +9,6 @@ #include #include #include -#include #include #include #include diff --git a/src/bfstd.h b/src/bfstd.h index 028e4e6..e4fd1f1 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -8,7 +8,7 @@ #ifndef BFS_BFSTD_H #define BFS_BFSTD_H -#include +#include "config.h" #include // #include diff --git a/src/bftw.c b/src/bftw.c index 56701a7..14805de 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -28,7 +28,6 @@ #include #include #include -#include #include #include #include diff --git a/src/color.c b/src/color.c index 589e631..eeadf98 100644 --- a/src/color.c +++ b/src/color.c @@ -15,7 +15,6 @@ #include #include #include -#include #include #include #include diff --git a/src/color.h b/src/color.h index 737c34b..3db2b07 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include -#include #include /** diff --git a/src/config.h b/src/config.h index 229bafb..4408feb 100644 --- a/src/config.h +++ b/src/config.h @@ -8,9 +8,13 @@ #ifndef BFS_CONFIG_H #define BFS_CONFIG_H -#include #include +#if __STDC_VERSION__ < 202311L +# include +# include +#endif + // bfs packaging configuration #ifndef BFS_COMMAND diff --git a/src/ctx.h b/src/ctx.h index 4c748b7..0dc9f08 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -9,8 +9,8 @@ #define BFS_CTX_H #include "bftw.h" +#include "config.h" #include "trie.h" -#include #include #include #include diff --git a/src/diag.h b/src/diag.h index 2952e30..987d4b4 100644 --- a/src/diag.h +++ b/src/diag.h @@ -11,7 +11,6 @@ #include "ctx.h" #include "config.h" #include -#include struct bfs_expr; diff --git a/src/dir.c b/src/dir.c index eb6e3e0..30db5df 100644 --- a/src/dir.c +++ b/src/dir.c @@ -8,8 +8,6 @@ #include #include #include -#include -#include #include #include #include diff --git a/src/eval.h b/src/eval.h index 3d70319..bdb9440 100644 --- a/src/eval.h +++ b/src/eval.h @@ -9,7 +9,7 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H -#include +#include "config.h" struct bfs_ctx; struct bfs_expr; diff --git a/src/exec.c b/src/exec.c index 6bde1c1..7f22d36 100644 --- a/src/exec.c +++ b/src/exec.c @@ -14,7 +14,6 @@ #include #include #include -#include #include #include #include diff --git a/src/expr.h b/src/expr.h index 1628cac..356689d 100644 --- a/src/expr.h +++ b/src/expr.h @@ -9,9 +9,9 @@ #define BFS_EXPR_H #include "color.h" +#include "config.h" #include "eval.h" #include "stat.h" -#include #include #include #include diff --git a/src/fsade.h b/src/fsade.h index 557da26..0d9ecaf 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -10,7 +10,6 @@ #define BFS_FSADE_H #include "config.h" -#include #define BFS_CAN_CHECK_ACL BFS_USE_SYS_ACL_H diff --git a/src/main.c b/src/main.c index 4f99580..24a5035 100644 --- a/src/main.c +++ b/src/main.c @@ -40,13 +40,13 @@ */ #include "bfstd.h" +#include "config.h" #include "ctx.h" #include "eval.h" #include "parse.h" #include #include #include -#include #include #include #include diff --git a/src/mtab.c b/src/mtab.c index 27f1743..1d1ad94 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -9,7 +9,6 @@ #include "trie.h" #include #include -#include #include #include #include diff --git a/src/mtab.h b/src/mtab.h index 5dfdf6c..ca4372c 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -8,7 +8,7 @@ #ifndef BFS_MTAB_H #define BFS_MTAB_H -#include +#include "config.h" struct bfs_stat; diff --git a/src/opt.c b/src/opt.c index 731dd10..4ce9425 100644 --- a/src/opt.c +++ b/src/opt.c @@ -39,7 +39,6 @@ #include #include #include -#include #include #include #include diff --git a/src/parse.c b/src/parse.c index 15f38a4..55f1e74 100644 --- a/src/parse.c +++ b/src/parse.c @@ -37,7 +37,6 @@ #include #include #include -#include #include #include #include diff --git a/src/printf.c b/src/printf.c index 454fbee..9ccc216 100644 --- a/src/printf.c +++ b/src/printf.c @@ -20,7 +20,6 @@ #include #include #include -#include #include #include #include diff --git a/src/pwcache.c b/src/pwcache.c index 5026dee..f52e4e1 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -2,12 +2,12 @@ // SPDX-License-Identifier: 0BSD #include "pwcache.h" +#include "config.h" #include "darray.h" #include "trie.h" #include #include #include -#include #include #include #include diff --git a/src/stat.c b/src/stat.c index f3d9046..7973d71 100644 --- a/src/stat.c +++ b/src/stat.c @@ -7,7 +7,6 @@ #include #include #include -#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bfe97e6..e0e00ff 100644 --- a/src/trie.c +++ b/src/trie.c @@ -86,7 +86,6 @@ #include "list.h" #include #include -#include #include #include #include diff --git a/src/trie.h b/src/trie.h index 58974aa..6bd211e 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "config.h" -#include #include #include diff --git a/src/xspawn.c b/src/xspawn.c index a6d18a3..00fb76e 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -7,7 +7,6 @@ #include "list.h" #include #include -#include #include #include #include diff --git a/src/xtime.c b/src/xtime.c index 82690d0..406d694 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -2,9 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "xtime.h" +#include "config.h" #include #include -#include #include #include #include diff --git a/tests/xtimegm.c b/tests/xtimegm.c index bab64ba..b2479b7 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -2,7 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "../src/xtime.h" -#include +#include "../src/config.h" #include #include #include diff --git a/tests/xtouch.c b/tests/xtouch.c index 7e29547..50416ba 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -2,10 +2,10 @@ // SPDX-License-Identifier: 0BSD #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/xtime.h" #include #include -#include #include #include #include -- cgit v1.2.3 From 7e26443627926bb3bcc88bd790190d5e4e0eda98 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 11 May 2023 10:38:23 -0400 Subject: config: Properly align flex_sizeof() --- src/config.h | 31 +++++++++++++++++++++---------- tests/bfstd.c | 13 +++++++++++++ 2 files changed, 34 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/src/config.h b/src/config.h index 4408feb..c6dcc1e 100644 --- a/src/config.h +++ b/src/config.h @@ -140,11 +140,12 @@ */ #define countof(array) (sizeof(array) / sizeof(0[array])) -// Lower bound on flex_sizeof() -#define BFS_FLEX_LB(type, member, length) (offsetof(type, member) + sizeof(((type *)NULL)->member[0]) * (length)) - -// Maximum macro for BFS_FLEX_SIZE() -#define BFS_FLEX_MAX(a, b) ((a) > (b) ? (a) : (b)) +/** + * Round up to a multiple of an alignment. + */ +static inline size_t align_ceil(size_t align, size_t size) { + return (size + align - 1) & ~(align - 1); +} /** * Computes the size of a struct containing a flexible array member of the given @@ -154,13 +155,23 @@ * The type of the struct containing the flexible array. * @param member * The name of the flexible array member. - * @param length + * @param count * The length of the flexible array. */ -#define flex_sizeof(type, member, length) \ - (sizeof(type) <= BFS_FLEX_LB(type, member, 0) \ - ? BFS_FLEX_LB(type, member, length) \ - : BFS_FLEX_MAX(sizeof(type), BFS_FLEX_LB(type, member, length))) +#define flex_sizeof(type, member, count) \ + flex_sizeof_impl(alignof(type), sizeof(type), offsetof(type, member), sizeof(((type *)NULL)->member[0]), count) + +static inline size_t flex_sizeof_impl(size_t align, size_t min, size_t offset, size_t size, size_t count) { + size_t ret = align_ceil(align, offset + size * count); + + // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the + // type has more padding than necessary for alignment + if (min > align_ceil(align, offset) && ret < min) { + ret = min; + } + + return ret; +} /** * Initialize a variable, unless sanitizers would detect uninitialized uses. diff --git a/tests/bfstd.c b/tests/bfstd.c index 8c61072..c6a9e9f 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,8 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#undef NDEBUG #include "../src/bfstd.h" +#include "../src/config.h" +#include #include +#include #include #include @@ -30,6 +34,15 @@ static void check_base_dir(const char *path, const char *dir, const char *base) } int main(void) { + // Check flex_sizeof() + struct flexible { + alignas(64) int foo; + int bar[]; + }; + assert(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); + assert(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); + assert(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); + // From man 3p basename check_base_dir("usr", ".", "usr"); check_base_dir("usr/", ".", "usr"); -- cgit v1.2.3 From a7932050f65844fb1f1145fee87c72aadaf4f995 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 11 May 2023 13:08:43 -0400 Subject: config: Saturate on overflow in flex_sizeof() --- src/config.h | 12 +++++++++++- tests/bfstd.c | 1 + 2 files changed, 12 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/config.h b/src/config.h index c6dcc1e..b2c58be 100644 --- a/src/config.h +++ b/src/config.h @@ -162,7 +162,17 @@ static inline size_t align_ceil(size_t align, size_t size) { flex_sizeof_impl(alignof(type), sizeof(type), offsetof(type, member), sizeof(((type *)NULL)->member[0]), count) static inline size_t flex_sizeof_impl(size_t align, size_t min, size_t offset, size_t size, size_t count) { - size_t ret = align_ceil(align, offset + size * count); + size_t ret = size * count; + size_t overflow = ret / size != count; + + ret += offset; + overflow |= ret < offset; + + size_t mask = align - 1; + ret += mask; + overflow |= ret < mask; + ret &= ~mask; + ret |= -overflow; // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the // type has more padding than necessary for alignment diff --git a/tests/bfstd.c b/tests/bfstd.c index c6a9e9f..1917a53 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -41,6 +41,7 @@ int main(void) { }; assert(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); assert(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); + assert(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) == SIZE_MAX); assert(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); // From man 3p basename -- cgit v1.2.3 From 6b0209f04a7a2fd3f8aec78808225177647c3aec Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 16 May 2023 10:24:29 -0400 Subject: int: Backport C23's _WIDTH macros --- Makefile | 2 +- src/int.h | 100 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/int.c | 54 ++++++++++++++++++++++++++++++++ 3 files changed, 155 insertions(+), 1 deletion(-) create mode 100644 src/int.h create mode 100644 tests/int.c (limited to 'tests') diff --git a/Makefile b/Makefile index f0e4e1e..40f6b17 100644 --- a/Makefile +++ b/Makefile @@ -240,7 +240,7 @@ LIBBFS := \ $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) # Standalone unit tests -UNITS := bfstd trie xtimegm +UNITS := bfstd int trie xtimegm UNIT_TESTS := $(UNITS:%=$(BIN)/tests/%) UNIT_CHECKS := $(UNITS:%=check-%) diff --git a/src/int.h b/src/int.h new file mode 100644 index 0000000..56fabee --- /dev/null +++ b/src/int.h @@ -0,0 +1,100 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Bits & bytes. + */ + +#ifndef BFS_INT_H +#define BFS_INT_H + +#include +#include + +// C23 polyfill: _WIDTH macros + +// The U*_MAX macros are of the form 2**n - 1, and we want to extract the n. +// One way would be *_WIDTH = popcount(*_MAX). Alternatively, we can use +// Hallvard B. Furuseth's technique from [1], which is shorter. +// +// [1]: https://groups.google.com/g/comp.lang.c/c/NfedEFBFJ0k + +// Let mask be of the form 2**m - 1, e.g. 0b111, and let n range over +// [0b0, 0b1, 0b11, 0b111, 0b1111, ...]. Then we have +// +// n % 0b111 +// == [0b0, 0b1, 0b11, 0b0, 0b1, 0b11, ...] +// n / (n % 0b111 + 1) +// == [0b0 (x3), 0b111 (x3), 0b111111 (x3), ...] +// n / (n % 0b111 + 1) / 0b111 +// == [0b0 (x3), 0b1 (x3), 0b1001 (x3), 0b1001001 (x3), ...] +// n / (n % 0b111 + 1) / 0b111 % 0b111 +// == [0 (x3), 1 (x3), 2 (x3), ...] +// == UMAX_CHUNK(n, 0b111) +#define UMAX_CHUNK(n, mask) (n / (n % mask + 1) / mask % mask) + +// 8 * UMAX_CHUNK(n, 255) gives [0 (x8), 8 (x8), 16 (x8), ...]. To that we add +// [0, 1, 2, ..., 6, 7, 0, 1, ...], which we get from a linear interpolation on +// n % 255: +// +// n % 255 +// == [0, 1, 3, 7, 15, 31, 63, 127, 0, ...] +// 86 / (n % 255 + 12) +// == [7, 6, 5, 4, 3, 2, 1, 0, 7, ...] +#define UMAX_INTERP(n) (7 - 86 / (n % 255 + 12)) + +#define UMAX_WIDTH(n) (8 * UMAX_CHUNK(n, 255) + UMAX_INTERP(n)) + +#ifndef CHAR_WIDTH +# define CHAR_WIDTH CHAR_BIT +#endif +#ifndef UCHAR_WIDTH +# define UCHAR_WIDTH CHAR_WIDTH +#endif +#ifndef SCHAR_WIDTH +# define SCHAR_WIDTH CHAR_WIDTH +#endif +#ifndef USHRT_WIDTH +# define USHRT_WIDTH UMAX_WIDTH(USHRT_MAX) +#endif +#ifndef SHRT_WIDTH +# define SHRT_WIDTH USHRT_WIDTH +#endif +#ifndef UINT_WIDTH +# define UINT_WIDTH UMAX_WIDTH(UINT_MAX) +#endif +#ifndef INT_WIDTH +# define INT_WIDTH UINT_WIDTH +#endif +#ifndef ULONG_WIDTH +# define ULONG_WIDTH UMAX_WIDTH(ULONG_MAX) +#endif +#ifndef LONG_WIDTH +# define LONG_WIDTH ULONG_WIDTH +#endif +#ifndef ULLONG_WIDTH +# define ULLONG_WIDTH UMAX_WIDTH(ULLONG_MAX) +#endif +#ifndef LLONG_WIDTH +# define LLONG_WIDTH ULLONG_WIDTH +#endif +#ifndef SIZE_WIDTH +# define SIZE_WIDTH UMAX_WIDTH(SIZE_MAX) +#endif +#ifndef PTRDIFF_WIDTH +# define PTRDIFF_WIDTH (UMAX_WIDTH(PTRDIFF_MAX) + 1) +#endif +#ifndef UINTPTR_WIDTH +# define UINTPTR_WIDTH UMAX_WIDTH(UINTPTR_MAX) +#endif +#ifndef INTPTR_WIDTH +# define INTPTR_WIDTH UINTPTR_WIDTH +#endif +#ifndef UINTMAX_WIDTH +# define UINTMAX_WIDTH UMAX_WIDTH(UINTMAX_MAX) +#endif +#ifndef INTMAX_WIDTH +# define INTMAX_WIDTH UINTMAX_WIDTH +#endif + +#endif // BFS_INT_H diff --git a/tests/int.c b/tests/int.c new file mode 100644 index 0000000..db59e90 --- /dev/null +++ b/tests/int.c @@ -0,0 +1,54 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "../src/int.h" +#include "../src/diag.h" +#include +#include + +bfs_static_assert(UMAX_WIDTH(0x1) == 1); +bfs_static_assert(UMAX_WIDTH(0x3) == 2); +bfs_static_assert(UMAX_WIDTH(0x7) == 3); +bfs_static_assert(UMAX_WIDTH(0xF) == 4); +bfs_static_assert(UMAX_WIDTH(0xFF) == 8); +bfs_static_assert(UMAX_WIDTH(0xFFF) == 12); +bfs_static_assert(UMAX_WIDTH(0xFFFF) == 16); + +#define UWIDTH_MAX(n) (2 * ((UINTMAX_C(1) << ((n) - 1)) - 1) + 1) +#define IWIDTH_MAX(n) UWIDTH_MAX((n) - 1) +#define IWIDTH_MIN(n) (-(intmax_t)IWIDTH_MAX(n) - 1) + +bfs_static_assert(UCHAR_MAX == UWIDTH_MAX(UCHAR_WIDTH)); +bfs_static_assert(SCHAR_MIN == IWIDTH_MIN(SCHAR_WIDTH)); +bfs_static_assert(SCHAR_MAX == IWIDTH_MAX(SCHAR_WIDTH)); + +bfs_static_assert(USHRT_MAX == UWIDTH_MAX(USHRT_WIDTH)); +bfs_static_assert(SHRT_MIN == IWIDTH_MIN(SHRT_WIDTH)); +bfs_static_assert(SHRT_MAX == IWIDTH_MAX(SHRT_WIDTH)); + +bfs_static_assert(UINT_MAX == UWIDTH_MAX(UINT_WIDTH)); +bfs_static_assert(INT_MIN == IWIDTH_MIN(INT_WIDTH)); +bfs_static_assert(INT_MAX == IWIDTH_MAX(INT_WIDTH)); + +bfs_static_assert(ULONG_MAX == UWIDTH_MAX(ULONG_WIDTH)); +bfs_static_assert(LONG_MIN == IWIDTH_MIN(LONG_WIDTH)); +bfs_static_assert(LONG_MAX == IWIDTH_MAX(LONG_WIDTH)); + +bfs_static_assert(ULLONG_MAX == UWIDTH_MAX(ULLONG_WIDTH)); +bfs_static_assert(LLONG_MIN == IWIDTH_MIN(LLONG_WIDTH)); +bfs_static_assert(LLONG_MAX == IWIDTH_MAX(LLONG_WIDTH)); + +bfs_static_assert(SIZE_MAX == UWIDTH_MAX(SIZE_WIDTH)); +bfs_static_assert(PTRDIFF_MIN == IWIDTH_MIN(PTRDIFF_WIDTH)); +bfs_static_assert(PTRDIFF_MAX == IWIDTH_MAX(PTRDIFF_WIDTH)); + +bfs_static_assert(UINTPTR_MAX == UWIDTH_MAX(UINTPTR_WIDTH)); +bfs_static_assert(INTPTR_MIN == IWIDTH_MIN(INTPTR_WIDTH)); +bfs_static_assert(INTPTR_MAX == IWIDTH_MAX(INTPTR_WIDTH)); + +bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); +bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); +bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); + +int main(void) { +} -- cgit v1.2.3 From fe472f30e1b82f762993cbc5376ff9b25c605aa9 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 16 May 2023 10:48:50 -0400 Subject: int: Backport C23's endian utilities --- src/int.h | 68 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/int.c | 10 +++++++++ 2 files changed, 78 insertions(+) (limited to 'tests') diff --git a/src/int.h b/src/int.h index 56fabee..1cd455a 100644 --- a/src/int.h +++ b/src/int.h @@ -11,6 +11,10 @@ #include #include +#if __STDC_VERSION__ >= 202311L +# include +#endif + // C23 polyfill: _WIDTH macros // The U*_MAX macros are of the form 2**n - 1, and we want to extract the n. @@ -97,4 +101,68 @@ # define INTMAX_WIDTH UINTMAX_WIDTH #endif +// C23 polyfill: byte order + +#ifdef __STDC_ENDIAN_LITTLE__ +# define ENDIAN_LITTLE __STDC_ENDIAN_LITTLE__ +#elif defined(__ORDER_LITTLE_ENDIAN__) +# define ENDIAN_LITTLE __ORDER_LITTLE_ENDIAN__ +#else +# define ENDIAN_LITTLE 1234 +#endif + +#ifdef __STDC_ENDIAN_BIG__ +# define ENDIAN_BIG __STDC_ENDIAN_BIG__ +#elif defined(__ORDER_BIG_ENDIAN__) +# define ENDIAN_BIG __ORDER_BIG_ENDIAN__ +#else +# define ENDIAN_BIG 4321 +#endif + +#ifdef __STDC_ENDIAN_NATIVE__ +# define ENDIAN_NATIVE __STDC_ENDIAN_NATIVE__ +#elif defined(__ORDER_NATIVE_ENDIAN__) +# define ENDIAN_NATIVE __ORDER_NATIVE_ENDIAN__ +#else +# define ENDIAN_NATIVE 0 +#endif + +#if __STDC_VERSION__ >= 202311L +# define bswap16 stdc_memreverse8u16 +# define bswap32 stdc_memreverse8u32 +# define bswap64 stdc_memreverse8u64 +#elif __GNUC__ +# define bswap16 __builtin_bswap16 +# define bswap32 __builtin_bswap32 +# define bswap64 __builtin_bswap64 +#else + +static inline uint16_t bswap16(uint16_t n) { + return (n << 8) | (n >> 8); +} + +static inline uint32_t bswap32(uint32_t n) { + return ((uint32_t)bswap16(n) << 16) | bswap16(n >> 16); +} + +static inline uint64_t bswap64(uint64_t n) { + return ((uint64_t)bswap32(n) << 32) | bswap32(n >> 32); +} + +#endif + +static inline uint8_t bswap8(uint8_t n) { + return n; +} + +/** + * Reverse the byte order of an integer. + */ +#define bswap(n) \ + _Generic((n), \ + uint8_t: bswap8, \ + uint16_t: bswap16, \ + uint32_t: bswap32, \ + uint64_t: bswap64)(n) + #endif // BFS_INT_H diff --git a/tests/int.c b/tests/int.c index db59e90..0039862 100644 --- a/tests/int.c +++ b/tests/int.c @@ -1,10 +1,14 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#undef NDEBUG + #include "../src/int.h" #include "../src/diag.h" +#include #include #include +#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); @@ -51,4 +55,10 @@ bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); int main(void) { + assert(bswap((uint8_t)0x12) == 0x12); + assert(bswap((uint16_t)0x1234) == 0x3412); + assert(bswap((uint32_t)0x12345678) == 0x78563412); + assert(bswap((uint64_t)0x1234567812345678) == 0x7856341278563412); + + return EXIT_SUCCESS; } -- cgit v1.2.3 From c860ad15979ac0cc6060529aa2027b2724825ca0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 16 May 2023 11:01:16 -0400 Subject: int: Backport C23's bit utilities --- src/int.h | 187 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/int.c | 57 ++++++++++++++++++ 2 files changed, 244 insertions(+) (limited to 'tests') diff --git a/src/int.h b/src/int.h index 1cd455a..885933d 100644 --- a/src/int.h +++ b/src/int.h @@ -8,6 +8,7 @@ #ifndef BFS_INT_H #define BFS_INT_H +#include "config.h" #include #include @@ -165,4 +166,190 @@ static inline uint8_t bswap8(uint8_t n) { uint32_t: bswap32, \ uint64_t: bswap64)(n) +// Define an overload for each unsigned type +#define UINT_OVERLOADS(macro) \ + macro(unsigned char, _uc, UCHAR_WIDTH) \ + macro(unsigned short, _us, USHRT_WIDTH) \ + macro(unsigned int, _ui, UINT_WIDTH) \ + macro(unsigned long, _ul, ULONG_WIDTH) \ + macro(unsigned long long, _ull, ULLONG_WIDTH) + +// Select an overload based on an unsigned integer type +#define UINT_SELECT(n, name) \ + _Generic((n), \ + char: name##_uc, \ + signed char: name##_uc, \ + unsigned char: name##_uc, \ + signed short: name##_us, \ + unsigned short: name##_us, \ + signed int: name##_ui, \ + unsigned int: name##_ui, \ + signed long: name##_ul, \ + unsigned long: name##_ul, \ + signed long long: name##_ull, \ + unsigned long long: name##_ull) + +// C23 polyfill: bit utilities + +#if __STDC_VERSION__ >= 202311L +# define count_ones stdc_count_ones +# define count_zeros stdc_count_zeros +# define rotate_left stdc_rotate_left +# define rotate_right stdc_rotate_right +# define leading_zeros stdc_leading_zeros +# define leading_ones stdc_leading_ones +# define trailing_zeros stdc_trailing_zeros +# define trailing_ones stdc_trailing_ones +# define first_leading_zero stdc_first_leading_zero +# define first_leading_one stdc_first_leading_one +# define first_trailing_zero stdc_first_trailing_zero +# define first_trailing_one stdc_first_trailing_one +# define has_single_bit stdc_has_single_bit +# define bit_width stdc_bit_width +# define bit_ceil stdc_bit_ceil +# define bit_floor stdc_bit_floor +#else + +#if __GNUC__ + +// GCC provides builtins for unsigned {int,long,long long}, so promote char/short +#define UINT_BUILTIN_uc(name) __builtin_##name +#define UINT_BUILTIN_us(name) __builtin_##name +#define UINT_BUILTIN_ui(name) __builtin_##name +#define UINT_BUILTIN_ul(name) __builtin_##name##l +#define UINT_BUILTIN_ull(name) __builtin_##name##ll +#define UINT_BUILTIN(name, suffix) UINT_BUILTIN##suffix(name) + +#define BUILTIN_WIDTH_uc UINT_WIDTH +#define BUILTIN_WIDTH_us UINT_WIDTH +#define BUILTIN_WIDTH_ui UINT_WIDTH +#define BUILTIN_WIDTH_ul ULONG_WIDTH +#define BUILTIN_WIDTH_ull ULLONG_WIDTH +#define BUILTIN_WIDTH(suffix) BUILTIN_WIDTH##suffix + +#define COUNT_ONES(type, suffix, width) \ + static inline int count_ones##suffix(type n) { \ + return UINT_BUILTIN(popcount, suffix)(n); \ + } + +#define LEADING_ZEROS(type, suffix, width) \ + static inline int leading_zeros##suffix(type n) { \ + return n \ + ? UINT_BUILTIN(clz, suffix)(n) - (BUILTIN_WIDTH(suffix) - width) \ + : width; \ + } + +#define TRAILING_ZEROS(type, suffix, width) \ + static inline int trailing_zeros##suffix(type n) { \ + return n ? UINT_BUILTIN(ctz, suffix)(n) : width; \ + } + +#define FIRST_TRAILING_ONE(type, suffix, width) \ + static inline int first_trailing_one##suffix(type n) { \ + return UINT_BUILTIN(ffs, suffix)(n); \ + } + +#else // !__GNUC__ + +#define COUNT_ONES(type, suffix, width) \ + static inline int count_ones##suffix(type n) { \ + int ret; \ + for (ret = 0; n; ++ret) { \ + n &= n - 1; \ + } \ + return ret; \ + } + +#define LEADING_ZEROS(type, suffix, width) \ + static inline int leading_zeros##suffix(type n) { \ + type bit = (type)1 << (width - 1); \ + int ret; \ + for (ret = 0; bit && !(n & bit); ++ret, bit >>= 1); \ + return ret; \ + } + +#define TRAILING_ZEROS(type, suffix, width) \ + static inline int trailing_zeros##suffix(type n) { \ + type bit = 1; \ + int ret; \ + for (ret = 0; bit && !(n & bit); ++ret, bit <<= 1); \ + return ret; \ + } + +#define FIRST_TRAILING_ONE(type, suffix, width) \ + static inline int first_trailing_one##suffix(type n) { \ + return n ? trailing_zeros##suffix(n) + 1 : 0; \ + } + +#endif // !__GNUC__ + +UINT_OVERLOADS(COUNT_ONES) +UINT_OVERLOADS(LEADING_ZEROS) +UINT_OVERLOADS(TRAILING_ZEROS) +UINT_OVERLOADS(FIRST_TRAILING_ONE) + +#define ROTATE_LEFT(type, suffix, width) \ + static inline type rotate_left##suffix(type n, int c) { \ + return (n << c) | (n >> ((width - c) % width)); \ + } + +#define ROTATE_RIGHT(type, suffix, width) \ + static inline type rotate_right##suffix(type n, int c) { \ + return (n >> c) | (n << ((width - c) % width)); \ + } + +#define FIRST_LEADING_ONE(type, suffix, width) \ + static inline int first_leading_one##suffix(type n) { \ + return width - leading_zeros##suffix(n); \ + } + +#define HAS_SINGLE_BIT(type, suffix, width) \ + static inline bool has_single_bit##suffix(type n) { \ + return n && !(n & (n - 1)); \ + } + +UINT_OVERLOADS(ROTATE_LEFT) +UINT_OVERLOADS(ROTATE_RIGHT) +UINT_OVERLOADS(FIRST_LEADING_ONE) +UINT_OVERLOADS(HAS_SINGLE_BIT) + +#define count_ones(n) UINT_SELECT(n, count_ones)(n) +#define count_zeros(n) UINT_SELECT(n, count_ones)(~(n)) + +#define rotate_left(n, c) UINT_SELECT(n, rotate_left)(n, c) +#define rotate_right(n, c) UINT_SELECT(n, rotate_right)(n, c) + +#define leading_zeros(n) UINT_SELECT(n, leading_zeros)(n) +#define leading_ones(n) UINT_SELECT(n, leading_zeros)(~(n)) + +#define trailing_zeros(n) UINT_SELECT(n, trailing_zeros)(n) +#define trailing_ones(n) UINT_SELECT(n, trailing_zeros)(~(n)) + +#define first_leading_one(n) UINT_SELECT(n, first_leading_one)(n) +#define first_leading_zero(n) UINT_SELECT(n, first_leading_one)(~(n)) + +#define first_trailing_one(n) UINT_SELECT(n, first_trailing_one)(n) +#define first_trailing_zero(n) UINT_SELECT(n, first_trailing_one)(~(n)) + +#define has_single_bit(n) UINT_SELECT(n, has_single_bit)(n) + +#define BIT_FLOOR(type, suffix, width) \ + static inline type bit_floor##suffix(type n) { \ + return n ? (type)1 << (first_leading_one##suffix(n) - 1) : 0; \ + } + +#define BIT_CEIL(type, suffix, width) \ + static inline type bit_ceil##suffix(type n) { \ + return (type)1 << first_leading_one##suffix(n - !!n); \ + } + +UINT_OVERLOADS(BIT_FLOOR) +UINT_OVERLOADS(BIT_CEIL) + +#define bit_width(n) first_leading_one(n) +#define bit_floor(n) UINT_SELECT(n, bit_floor)(n) +#define bit_ceil(n) UINT_SELECT(n, bit_ceil)(n) + +#endif // __STDC_VERSION__ < 202311L + #endif // BFS_INT_H diff --git a/tests/int.c b/tests/int.c index 0039862..e59efde 100644 --- a/tests/int.c +++ b/tests/int.c @@ -60,5 +60,62 @@ int main(void) { assert(bswap((uint32_t)0x12345678) == 0x78563412); assert(bswap((uint64_t)0x1234567812345678) == 0x7856341278563412); + assert(count_ones(0x0) == 0); + assert(count_ones(0x1) == 1); + assert(count_ones(0x2) == 1); + assert(count_ones(0x3) == 2); + assert(count_ones(0x137F) == 10); + + assert(count_zeros(0) == INT_WIDTH); + assert(count_zeros(0L) == LONG_WIDTH); + assert(count_zeros(0LL) == LLONG_WIDTH); + assert(count_zeros((uint8_t)0) == 8); + assert(count_zeros((uint16_t)0) == 16); + assert(count_zeros((uint32_t)0) == 32); + assert(count_zeros((uint64_t)0) == 64); + + assert(rotate_left((uint8_t)0xA1, 4) == 0x1A); + assert(rotate_left((uint16_t)0x1234, 12) == 0x4123); + assert(rotate_left((uint32_t)0x12345678, 20) == 0x67812345); + assert(rotate_left((uint32_t)0x12345678, 0) == 0x12345678); + + assert(rotate_right((uint8_t)0xA1, 4) == 0x1A); + assert(rotate_right((uint16_t)0x1234, 12) == 0x2341); + assert(rotate_right((uint32_t)0x12345678, 20) == 0x45678123); + assert(rotate_right((uint32_t)0x12345678, 0) == 0x12345678); + + for (int i = 0; i < 16; ++i) { + uint16_t n = (uint16_t)1 << i; + for (int j = i; j < 16; ++j) { + uint16_t m = (uint16_t)1 << j; + uint16_t nm = n | m; + assert(count_ones(nm) == 1 + (n != m)); + assert(count_zeros(nm) == 15 - (n != m)); + assert(leading_zeros(nm) == 15 - j); + assert(trailing_zeros(nm) == i); + assert(first_leading_one(nm) == j + 1); + assert(first_trailing_one(nm) == i + 1); + assert(bit_width(nm) == j + 1); + assert(bit_floor(nm) == m); + if (n == m) { + assert(bit_ceil(nm) == m); + assert(has_single_bit(nm)); + } else { + if (j < 15) { + assert(bit_ceil(nm) == (m << 1)); + } + assert(!has_single_bit(nm)); + } + } + } + + assert(leading_zeros((uint16_t)0) == 16); + assert(trailing_zeros((uint16_t)0) == 16); + assert(first_leading_one(0) == 0); + assert(first_trailing_one(0) == 0); + assert(bit_width(0) == 0); + assert(bit_floor(0) == 0); + assert(bit_ceil(0) == 1); + return EXIT_SUCCESS; } -- cgit v1.2.3 From 8706515d905038ee7d869ab5ace0471c781d2624 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 16 May 2023 11:37:53 -0400 Subject: config: Align after saturating in flex_sizeof() This ensures that it's legal to call aligned_alloc() with the result, which requires a multiple of the alignment. --- src/config.h | 11 +++++++++-- tests/bfstd.c | 3 ++- 2 files changed, 11 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/src/config.h b/src/config.h index b2c58be..47aa529 100644 --- a/src/config.h +++ b/src/config.h @@ -140,11 +140,18 @@ */ #define countof(array) (sizeof(array) / sizeof(0[array])) +/** + * Round down to a multiple of an alignment. + */ +static inline size_t align_floor(size_t align, size_t size) { + return size & ~(align - 1); +} + /** * Round up to a multiple of an alignment. */ static inline size_t align_ceil(size_t align, size_t size) { - return (size + align - 1) & ~(align - 1); + return align_floor(align, size + align - 1); } /** @@ -171,8 +178,8 @@ static inline size_t flex_sizeof_impl(size_t align, size_t min, size_t offset, s size_t mask = align - 1; ret += mask; overflow |= ret < mask; - ret &= ~mask; ret |= -overflow; + ret &= ~mask; // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the // type has more padding than necessary for alignment diff --git a/tests/bfstd.c b/tests/bfstd.c index 1917a53..a986a23 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -41,7 +41,8 @@ int main(void) { }; assert(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); assert(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); - assert(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) == SIZE_MAX); + assert(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) + == align_floor(alignof(struct flexible), SIZE_MAX)); assert(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); // From man 3p basename -- cgit v1.2.3 From 4505819c576fc93f21d73e1bf85550250cdb72a2 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 May 2023 11:25:14 -0400 Subject: bit: Rename int.h to bit.h --- Makefile | 2 +- src/bit.h | 355 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/int.h | 355 ------------------------------------------------------------ src/trie.c | 2 +- tests/bit.c | 121 +++++++++++++++++++++ tests/int.c | 121 --------------------- 6 files changed, 478 insertions(+), 478 deletions(-) create mode 100644 src/bit.h delete mode 100644 src/int.h create mode 100644 tests/bit.c delete mode 100644 tests/int.c (limited to 'tests') diff --git a/Makefile b/Makefile index 361f230..1d3bd20 100644 --- a/Makefile +++ b/Makefile @@ -240,7 +240,7 @@ LIBBFS := \ $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) # Standalone unit tests -UNITS := bfstd int trie xtimegm +UNITS := bfstd bit trie xtimegm UNIT_TESTS := $(UNITS:%=$(BIN)/tests/%) UNIT_CHECKS := $(UNITS:%=check-%) diff --git a/src/bit.h b/src/bit.h new file mode 100644 index 0000000..efd7d27 --- /dev/null +++ b/src/bit.h @@ -0,0 +1,355 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Bits & bytes. + */ + +#ifndef BFS_BIT_H +#define BFS_BIT_H + +#include "config.h" +#include +#include + +#if __STDC_VERSION__ >= 202311L +# include +#endif + +// C23 polyfill: _WIDTH macros + +// The U*_MAX macros are of the form 2**n - 1, and we want to extract the n. +// One way would be *_WIDTH = popcount(*_MAX). Alternatively, we can use +// Hallvard B. Furuseth's technique from [1], which is shorter. +// +// [1]: https://groups.google.com/g/comp.lang.c/c/NfedEFBFJ0k + +// Let mask be of the form 2**m - 1, e.g. 0b111, and let n range over +// [0b0, 0b1, 0b11, 0b111, 0b1111, ...]. Then we have +// +// n % 0b111 +// == [0b0, 0b1, 0b11, 0b0, 0b1, 0b11, ...] +// n / (n % 0b111 + 1) +// == [0b0 (x3), 0b111 (x3), 0b111111 (x3), ...] +// n / (n % 0b111 + 1) / 0b111 +// == [0b0 (x3), 0b1 (x3), 0b1001 (x3), 0b1001001 (x3), ...] +// n / (n % 0b111 + 1) / 0b111 % 0b111 +// == [0 (x3), 1 (x3), 2 (x3), ...] +// == UMAX_CHUNK(n, 0b111) +#define UMAX_CHUNK(n, mask) (n / (n % mask + 1) / mask % mask) + +// 8 * UMAX_CHUNK(n, 255) gives [0 (x8), 8 (x8), 16 (x8), ...]. To that we add +// [0, 1, 2, ..., 6, 7, 0, 1, ...], which we get from a linear interpolation on +// n % 255: +// +// n % 255 +// == [0, 1, 3, 7, 15, 31, 63, 127, 0, ...] +// 86 / (n % 255 + 12) +// == [7, 6, 5, 4, 3, 2, 1, 0, 7, ...] +#define UMAX_INTERP(n) (7 - 86 / (n % 255 + 12)) + +#define UMAX_WIDTH(n) (8 * UMAX_CHUNK(n, 255) + UMAX_INTERP(n)) + +#ifndef CHAR_WIDTH +# define CHAR_WIDTH CHAR_BIT +#endif +#ifndef UCHAR_WIDTH +# define UCHAR_WIDTH CHAR_WIDTH +#endif +#ifndef SCHAR_WIDTH +# define SCHAR_WIDTH CHAR_WIDTH +#endif +#ifndef USHRT_WIDTH +# define USHRT_WIDTH UMAX_WIDTH(USHRT_MAX) +#endif +#ifndef SHRT_WIDTH +# define SHRT_WIDTH USHRT_WIDTH +#endif +#ifndef UINT_WIDTH +# define UINT_WIDTH UMAX_WIDTH(UINT_MAX) +#endif +#ifndef INT_WIDTH +# define INT_WIDTH UINT_WIDTH +#endif +#ifndef ULONG_WIDTH +# define ULONG_WIDTH UMAX_WIDTH(ULONG_MAX) +#endif +#ifndef LONG_WIDTH +# define LONG_WIDTH ULONG_WIDTH +#endif +#ifndef ULLONG_WIDTH +# define ULLONG_WIDTH UMAX_WIDTH(ULLONG_MAX) +#endif +#ifndef LLONG_WIDTH +# define LLONG_WIDTH ULLONG_WIDTH +#endif +#ifndef SIZE_WIDTH +# define SIZE_WIDTH UMAX_WIDTH(SIZE_MAX) +#endif +#ifndef PTRDIFF_WIDTH +# define PTRDIFF_WIDTH (UMAX_WIDTH(PTRDIFF_MAX) + 1) +#endif +#ifndef UINTPTR_WIDTH +# define UINTPTR_WIDTH UMAX_WIDTH(UINTPTR_MAX) +#endif +#ifndef INTPTR_WIDTH +# define INTPTR_WIDTH UINTPTR_WIDTH +#endif +#ifndef UINTMAX_WIDTH +# define UINTMAX_WIDTH UMAX_WIDTH(UINTMAX_MAX) +#endif +#ifndef INTMAX_WIDTH +# define INTMAX_WIDTH UINTMAX_WIDTH +#endif + +// C23 polyfill: byte order + +#ifdef __STDC_ENDIAN_LITTLE__ +# define ENDIAN_LITTLE __STDC_ENDIAN_LITTLE__ +#elif defined(__ORDER_LITTLE_ENDIAN__) +# define ENDIAN_LITTLE __ORDER_LITTLE_ENDIAN__ +#else +# define ENDIAN_LITTLE 1234 +#endif + +#ifdef __STDC_ENDIAN_BIG__ +# define ENDIAN_BIG __STDC_ENDIAN_BIG__ +#elif defined(__ORDER_BIG_ENDIAN__) +# define ENDIAN_BIG __ORDER_BIG_ENDIAN__ +#else +# define ENDIAN_BIG 4321 +#endif + +#ifdef __STDC_ENDIAN_NATIVE__ +# define ENDIAN_NATIVE __STDC_ENDIAN_NATIVE__ +#elif defined(__ORDER_NATIVE_ENDIAN__) +# define ENDIAN_NATIVE __ORDER_NATIVE_ENDIAN__ +#else +# define ENDIAN_NATIVE 0 +#endif + +#if __STDC_VERSION__ >= 202311L +# define bswap16 stdc_memreverse8u16 +# define bswap32 stdc_memreverse8u32 +# define bswap64 stdc_memreverse8u64 +#elif __GNUC__ +# define bswap16 __builtin_bswap16 +# define bswap32 __builtin_bswap32 +# define bswap64 __builtin_bswap64 +#else + +static inline uint16_t bswap16(uint16_t n) { + return (n << 8) | (n >> 8); +} + +static inline uint32_t bswap32(uint32_t n) { + return ((uint32_t)bswap16(n) << 16) | bswap16(n >> 16); +} + +static inline uint64_t bswap64(uint64_t n) { + return ((uint64_t)bswap32(n) << 32) | bswap32(n >> 32); +} + +#endif + +static inline uint8_t bswap8(uint8_t n) { + return n; +} + +/** + * Reverse the byte order of an integer. + */ +#define bswap(n) \ + _Generic((n), \ + uint8_t: bswap8, \ + uint16_t: bswap16, \ + uint32_t: bswap32, \ + uint64_t: bswap64)(n) + +// Define an overload for each unsigned type +#define UINT_OVERLOADS(macro) \ + macro(unsigned char, _uc, UCHAR_WIDTH) \ + macro(unsigned short, _us, USHRT_WIDTH) \ + macro(unsigned int, _ui, UINT_WIDTH) \ + macro(unsigned long, _ul, ULONG_WIDTH) \ + macro(unsigned long long, _ull, ULLONG_WIDTH) + +// Select an overload based on an unsigned integer type +#define UINT_SELECT(n, name) \ + _Generic((n), \ + char: name##_uc, \ + signed char: name##_uc, \ + unsigned char: name##_uc, \ + signed short: name##_us, \ + unsigned short: name##_us, \ + signed int: name##_ui, \ + unsigned int: name##_ui, \ + signed long: name##_ul, \ + unsigned long: name##_ul, \ + signed long long: name##_ull, \ + unsigned long long: name##_ull) + +// C23 polyfill: bit utilities + +#if __STDC_VERSION__ >= 202311L +# define count_ones stdc_count_ones +# define count_zeros stdc_count_zeros +# define rotate_left stdc_rotate_left +# define rotate_right stdc_rotate_right +# define leading_zeros stdc_leading_zeros +# define leading_ones stdc_leading_ones +# define trailing_zeros stdc_trailing_zeros +# define trailing_ones stdc_trailing_ones +# define first_leading_zero stdc_first_leading_zero +# define first_leading_one stdc_first_leading_one +# define first_trailing_zero stdc_first_trailing_zero +# define first_trailing_one stdc_first_trailing_one +# define has_single_bit stdc_has_single_bit +# define bit_width stdc_bit_width +# define bit_ceil stdc_bit_ceil +# define bit_floor stdc_bit_floor +#else + +#if __GNUC__ + +// GCC provides builtins for unsigned {int,long,long long}, so promote char/short +#define UINT_BUILTIN_uc(name) __builtin_##name +#define UINT_BUILTIN_us(name) __builtin_##name +#define UINT_BUILTIN_ui(name) __builtin_##name +#define UINT_BUILTIN_ul(name) __builtin_##name##l +#define UINT_BUILTIN_ull(name) __builtin_##name##ll +#define UINT_BUILTIN(name, suffix) UINT_BUILTIN##suffix(name) + +#define BUILTIN_WIDTH_uc UINT_WIDTH +#define BUILTIN_WIDTH_us UINT_WIDTH +#define BUILTIN_WIDTH_ui UINT_WIDTH +#define BUILTIN_WIDTH_ul ULONG_WIDTH +#define BUILTIN_WIDTH_ull ULLONG_WIDTH +#define BUILTIN_WIDTH(suffix) BUILTIN_WIDTH##suffix + +#define COUNT_ONES(type, suffix, width) \ + static inline int count_ones##suffix(type n) { \ + return UINT_BUILTIN(popcount, suffix)(n); \ + } + +#define LEADING_ZEROS(type, suffix, width) \ + static inline int leading_zeros##suffix(type n) { \ + return n \ + ? UINT_BUILTIN(clz, suffix)(n) - (BUILTIN_WIDTH(suffix) - width) \ + : width; \ + } + +#define TRAILING_ZEROS(type, suffix, width) \ + static inline int trailing_zeros##suffix(type n) { \ + return n ? UINT_BUILTIN(ctz, suffix)(n) : width; \ + } + +#define FIRST_TRAILING_ONE(type, suffix, width) \ + static inline int first_trailing_one##suffix(type n) { \ + return UINT_BUILTIN(ffs, suffix)(n); \ + } + +#else // !__GNUC__ + +#define COUNT_ONES(type, suffix, width) \ + static inline int count_ones##suffix(type n) { \ + int ret; \ + for (ret = 0; n; ++ret) { \ + n &= n - 1; \ + } \ + return ret; \ + } + +#define LEADING_ZEROS(type, suffix, width) \ + static inline int leading_zeros##suffix(type n) { \ + type bit = (type)1 << (width - 1); \ + int ret; \ + for (ret = 0; bit && !(n & bit); ++ret, bit >>= 1); \ + return ret; \ + } + +#define TRAILING_ZEROS(type, suffix, width) \ + static inline int trailing_zeros##suffix(type n) { \ + type bit = 1; \ + int ret; \ + for (ret = 0; bit && !(n & bit); ++ret, bit <<= 1); \ + return ret; \ + } + +#define FIRST_TRAILING_ONE(type, suffix, width) \ + static inline int first_trailing_one##suffix(type n) { \ + return n ? trailing_zeros##suffix(n) + 1 : 0; \ + } + +#endif // !__GNUC__ + +UINT_OVERLOADS(COUNT_ONES) +UINT_OVERLOADS(LEADING_ZEROS) +UINT_OVERLOADS(TRAILING_ZEROS) +UINT_OVERLOADS(FIRST_TRAILING_ONE) + +#define ROTATE_LEFT(type, suffix, width) \ + static inline type rotate_left##suffix(type n, int c) { \ + return (n << c) | (n >> ((width - c) % width)); \ + } + +#define ROTATE_RIGHT(type, suffix, width) \ + static inline type rotate_right##suffix(type n, int c) { \ + return (n >> c) | (n << ((width - c) % width)); \ + } + +#define FIRST_LEADING_ONE(type, suffix, width) \ + static inline int first_leading_one##suffix(type n) { \ + return width - leading_zeros##suffix(n); \ + } + +#define HAS_SINGLE_BIT(type, suffix, width) \ + static inline bool has_single_bit##suffix(type n) { \ + return n && !(n & (n - 1)); \ + } + +UINT_OVERLOADS(ROTATE_LEFT) +UINT_OVERLOADS(ROTATE_RIGHT) +UINT_OVERLOADS(FIRST_LEADING_ONE) +UINT_OVERLOADS(HAS_SINGLE_BIT) + +#define count_ones(n) UINT_SELECT(n, count_ones)(n) +#define count_zeros(n) UINT_SELECT(n, count_ones)(~(n)) + +#define rotate_left(n, c) UINT_SELECT(n, rotate_left)(n, c) +#define rotate_right(n, c) UINT_SELECT(n, rotate_right)(n, c) + +#define leading_zeros(n) UINT_SELECT(n, leading_zeros)(n) +#define leading_ones(n) UINT_SELECT(n, leading_zeros)(~(n)) + +#define trailing_zeros(n) UINT_SELECT(n, trailing_zeros)(n) +#define trailing_ones(n) UINT_SELECT(n, trailing_zeros)(~(n)) + +#define first_leading_one(n) UINT_SELECT(n, first_leading_one)(n) +#define first_leading_zero(n) UINT_SELECT(n, first_leading_one)(~(n)) + +#define first_trailing_one(n) UINT_SELECT(n, first_trailing_one)(n) +#define first_trailing_zero(n) UINT_SELECT(n, first_trailing_one)(~(n)) + +#define has_single_bit(n) UINT_SELECT(n, has_single_bit)(n) + +#define BIT_FLOOR(type, suffix, width) \ + static inline type bit_floor##suffix(type n) { \ + return n ? (type)1 << (first_leading_one##suffix(n) - 1) : 0; \ + } + +#define BIT_CEIL(type, suffix, width) \ + static inline type bit_ceil##suffix(type n) { \ + return (type)1 << first_leading_one##suffix(n - !!n); \ + } + +UINT_OVERLOADS(BIT_FLOOR) +UINT_OVERLOADS(BIT_CEIL) + +#define bit_width(n) first_leading_one(n) +#define bit_floor(n) UINT_SELECT(n, bit_floor)(n) +#define bit_ceil(n) UINT_SELECT(n, bit_ceil)(n) + +#endif // __STDC_VERSION__ < 202311L + +#endif // BFS_BIT_H diff --git a/src/int.h b/src/int.h deleted file mode 100644 index 885933d..0000000 --- a/src/int.h +++ /dev/null @@ -1,355 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -/** - * Bits & bytes. - */ - -#ifndef BFS_INT_H -#define BFS_INT_H - -#include "config.h" -#include -#include - -#if __STDC_VERSION__ >= 202311L -# include -#endif - -// C23 polyfill: _WIDTH macros - -// The U*_MAX macros are of the form 2**n - 1, and we want to extract the n. -// One way would be *_WIDTH = popcount(*_MAX). Alternatively, we can use -// Hallvard B. Furuseth's technique from [1], which is shorter. -// -// [1]: https://groups.google.com/g/comp.lang.c/c/NfedEFBFJ0k - -// Let mask be of the form 2**m - 1, e.g. 0b111, and let n range over -// [0b0, 0b1, 0b11, 0b111, 0b1111, ...]. Then we have -// -// n % 0b111 -// == [0b0, 0b1, 0b11, 0b0, 0b1, 0b11, ...] -// n / (n % 0b111 + 1) -// == [0b0 (x3), 0b111 (x3), 0b111111 (x3), ...] -// n / (n % 0b111 + 1) / 0b111 -// == [0b0 (x3), 0b1 (x3), 0b1001 (x3), 0b1001001 (x3), ...] -// n / (n % 0b111 + 1) / 0b111 % 0b111 -// == [0 (x3), 1 (x3), 2 (x3), ...] -// == UMAX_CHUNK(n, 0b111) -#define UMAX_CHUNK(n, mask) (n / (n % mask + 1) / mask % mask) - -// 8 * UMAX_CHUNK(n, 255) gives [0 (x8), 8 (x8), 16 (x8), ...]. To that we add -// [0, 1, 2, ..., 6, 7, 0, 1, ...], which we get from a linear interpolation on -// n % 255: -// -// n % 255 -// == [0, 1, 3, 7, 15, 31, 63, 127, 0, ...] -// 86 / (n % 255 + 12) -// == [7, 6, 5, 4, 3, 2, 1, 0, 7, ...] -#define UMAX_INTERP(n) (7 - 86 / (n % 255 + 12)) - -#define UMAX_WIDTH(n) (8 * UMAX_CHUNK(n, 255) + UMAX_INTERP(n)) - -#ifndef CHAR_WIDTH -# define CHAR_WIDTH CHAR_BIT -#endif -#ifndef UCHAR_WIDTH -# define UCHAR_WIDTH CHAR_WIDTH -#endif -#ifndef SCHAR_WIDTH -# define SCHAR_WIDTH CHAR_WIDTH -#endif -#ifndef USHRT_WIDTH -# define USHRT_WIDTH UMAX_WIDTH(USHRT_MAX) -#endif -#ifndef SHRT_WIDTH -# define SHRT_WIDTH USHRT_WIDTH -#endif -#ifndef UINT_WIDTH -# define UINT_WIDTH UMAX_WIDTH(UINT_MAX) -#endif -#ifndef INT_WIDTH -# define INT_WIDTH UINT_WIDTH -#endif -#ifndef ULONG_WIDTH -# define ULONG_WIDTH UMAX_WIDTH(ULONG_MAX) -#endif -#ifndef LONG_WIDTH -# define LONG_WIDTH ULONG_WIDTH -#endif -#ifndef ULLONG_WIDTH -# define ULLONG_WIDTH UMAX_WIDTH(ULLONG_MAX) -#endif -#ifndef LLONG_WIDTH -# define LLONG_WIDTH ULLONG_WIDTH -#endif -#ifndef SIZE_WIDTH -# define SIZE_WIDTH UMAX_WIDTH(SIZE_MAX) -#endif -#ifndef PTRDIFF_WIDTH -# define PTRDIFF_WIDTH (UMAX_WIDTH(PTRDIFF_MAX) + 1) -#endif -#ifndef UINTPTR_WIDTH -# define UINTPTR_WIDTH UMAX_WIDTH(UINTPTR_MAX) -#endif -#ifndef INTPTR_WIDTH -# define INTPTR_WIDTH UINTPTR_WIDTH -#endif -#ifndef UINTMAX_WIDTH -# define UINTMAX_WIDTH UMAX_WIDTH(UINTMAX_MAX) -#endif -#ifndef INTMAX_WIDTH -# define INTMAX_WIDTH UINTMAX_WIDTH -#endif - -// C23 polyfill: byte order - -#ifdef __STDC_ENDIAN_LITTLE__ -# define ENDIAN_LITTLE __STDC_ENDIAN_LITTLE__ -#elif defined(__ORDER_LITTLE_ENDIAN__) -# define ENDIAN_LITTLE __ORDER_LITTLE_ENDIAN__ -#else -# define ENDIAN_LITTLE 1234 -#endif - -#ifdef __STDC_ENDIAN_BIG__ -# define ENDIAN_BIG __STDC_ENDIAN_BIG__ -#elif defined(__ORDER_BIG_ENDIAN__) -# define ENDIAN_BIG __ORDER_BIG_ENDIAN__ -#else -# define ENDIAN_BIG 4321 -#endif - -#ifdef __STDC_ENDIAN_NATIVE__ -# define ENDIAN_NATIVE __STDC_ENDIAN_NATIVE__ -#elif defined(__ORDER_NATIVE_ENDIAN__) -# define ENDIAN_NATIVE __ORDER_NATIVE_ENDIAN__ -#else -# define ENDIAN_NATIVE 0 -#endif - -#if __STDC_VERSION__ >= 202311L -# define bswap16 stdc_memreverse8u16 -# define bswap32 stdc_memreverse8u32 -# define bswap64 stdc_memreverse8u64 -#elif __GNUC__ -# define bswap16 __builtin_bswap16 -# define bswap32 __builtin_bswap32 -# define bswap64 __builtin_bswap64 -#else - -static inline uint16_t bswap16(uint16_t n) { - return (n << 8) | (n >> 8); -} - -static inline uint32_t bswap32(uint32_t n) { - return ((uint32_t)bswap16(n) << 16) | bswap16(n >> 16); -} - -static inline uint64_t bswap64(uint64_t n) { - return ((uint64_t)bswap32(n) << 32) | bswap32(n >> 32); -} - -#endif - -static inline uint8_t bswap8(uint8_t n) { - return n; -} - -/** - * Reverse the byte order of an integer. - */ -#define bswap(n) \ - _Generic((n), \ - uint8_t: bswap8, \ - uint16_t: bswap16, \ - uint32_t: bswap32, \ - uint64_t: bswap64)(n) - -// Define an overload for each unsigned type -#define UINT_OVERLOADS(macro) \ - macro(unsigned char, _uc, UCHAR_WIDTH) \ - macro(unsigned short, _us, USHRT_WIDTH) \ - macro(unsigned int, _ui, UINT_WIDTH) \ - macro(unsigned long, _ul, ULONG_WIDTH) \ - macro(unsigned long long, _ull, ULLONG_WIDTH) - -// Select an overload based on an unsigned integer type -#define UINT_SELECT(n, name) \ - _Generic((n), \ - char: name##_uc, \ - signed char: name##_uc, \ - unsigned char: name##_uc, \ - signed short: name##_us, \ - unsigned short: name##_us, \ - signed int: name##_ui, \ - unsigned int: name##_ui, \ - signed long: name##_ul, \ - unsigned long: name##_ul, \ - signed long long: name##_ull, \ - unsigned long long: name##_ull) - -// C23 polyfill: bit utilities - -#if __STDC_VERSION__ >= 202311L -# define count_ones stdc_count_ones -# define count_zeros stdc_count_zeros -# define rotate_left stdc_rotate_left -# define rotate_right stdc_rotate_right -# define leading_zeros stdc_leading_zeros -# define leading_ones stdc_leading_ones -# define trailing_zeros stdc_trailing_zeros -# define trailing_ones stdc_trailing_ones -# define first_leading_zero stdc_first_leading_zero -# define first_leading_one stdc_first_leading_one -# define first_trailing_zero stdc_first_trailing_zero -# define first_trailing_one stdc_first_trailing_one -# define has_single_bit stdc_has_single_bit -# define bit_width stdc_bit_width -# define bit_ceil stdc_bit_ceil -# define bit_floor stdc_bit_floor -#else - -#if __GNUC__ - -// GCC provides builtins for unsigned {int,long,long long}, so promote char/short -#define UINT_BUILTIN_uc(name) __builtin_##name -#define UINT_BUILTIN_us(name) __builtin_##name -#define UINT_BUILTIN_ui(name) __builtin_##name -#define UINT_BUILTIN_ul(name) __builtin_##name##l -#define UINT_BUILTIN_ull(name) __builtin_##name##ll -#define UINT_BUILTIN(name, suffix) UINT_BUILTIN##suffix(name) - -#define BUILTIN_WIDTH_uc UINT_WIDTH -#define BUILTIN_WIDTH_us UINT_WIDTH -#define BUILTIN_WIDTH_ui UINT_WIDTH -#define BUILTIN_WIDTH_ul ULONG_WIDTH -#define BUILTIN_WIDTH_ull ULLONG_WIDTH -#define BUILTIN_WIDTH(suffix) BUILTIN_WIDTH##suffix - -#define COUNT_ONES(type, suffix, width) \ - static inline int count_ones##suffix(type n) { \ - return UINT_BUILTIN(popcount, suffix)(n); \ - } - -#define LEADING_ZEROS(type, suffix, width) \ - static inline int leading_zeros##suffix(type n) { \ - return n \ - ? UINT_BUILTIN(clz, suffix)(n) - (BUILTIN_WIDTH(suffix) - width) \ - : width; \ - } - -#define TRAILING_ZEROS(type, suffix, width) \ - static inline int trailing_zeros##suffix(type n) { \ - return n ? UINT_BUILTIN(ctz, suffix)(n) : width; \ - } - -#define FIRST_TRAILING_ONE(type, suffix, width) \ - static inline int first_trailing_one##suffix(type n) { \ - return UINT_BUILTIN(ffs, suffix)(n); \ - } - -#else // !__GNUC__ - -#define COUNT_ONES(type, suffix, width) \ - static inline int count_ones##suffix(type n) { \ - int ret; \ - for (ret = 0; n; ++ret) { \ - n &= n - 1; \ - } \ - return ret; \ - } - -#define LEADING_ZEROS(type, suffix, width) \ - static inline int leading_zeros##suffix(type n) { \ - type bit = (type)1 << (width - 1); \ - int ret; \ - for (ret = 0; bit && !(n & bit); ++ret, bit >>= 1); \ - return ret; \ - } - -#define TRAILING_ZEROS(type, suffix, width) \ - static inline int trailing_zeros##suffix(type n) { \ - type bit = 1; \ - int ret; \ - for (ret = 0; bit && !(n & bit); ++ret, bit <<= 1); \ - return ret; \ - } - -#define FIRST_TRAILING_ONE(type, suffix, width) \ - static inline int first_trailing_one##suffix(type n) { \ - return n ? trailing_zeros##suffix(n) + 1 : 0; \ - } - -#endif // !__GNUC__ - -UINT_OVERLOADS(COUNT_ONES) -UINT_OVERLOADS(LEADING_ZEROS) -UINT_OVERLOADS(TRAILING_ZEROS) -UINT_OVERLOADS(FIRST_TRAILING_ONE) - -#define ROTATE_LEFT(type, suffix, width) \ - static inline type rotate_left##suffix(type n, int c) { \ - return (n << c) | (n >> ((width - c) % width)); \ - } - -#define ROTATE_RIGHT(type, suffix, width) \ - static inline type rotate_right##suffix(type n, int c) { \ - return (n >> c) | (n << ((width - c) % width)); \ - } - -#define FIRST_LEADING_ONE(type, suffix, width) \ - static inline int first_leading_one##suffix(type n) { \ - return width - leading_zeros##suffix(n); \ - } - -#define HAS_SINGLE_BIT(type, suffix, width) \ - static inline bool has_single_bit##suffix(type n) { \ - return n && !(n & (n - 1)); \ - } - -UINT_OVERLOADS(ROTATE_LEFT) -UINT_OVERLOADS(ROTATE_RIGHT) -UINT_OVERLOADS(FIRST_LEADING_ONE) -UINT_OVERLOADS(HAS_SINGLE_BIT) - -#define count_ones(n) UINT_SELECT(n, count_ones)(n) -#define count_zeros(n) UINT_SELECT(n, count_ones)(~(n)) - -#define rotate_left(n, c) UINT_SELECT(n, rotate_left)(n, c) -#define rotate_right(n, c) UINT_SELECT(n, rotate_right)(n, c) - -#define leading_zeros(n) UINT_SELECT(n, leading_zeros)(n) -#define leading_ones(n) UINT_SELECT(n, leading_zeros)(~(n)) - -#define trailing_zeros(n) UINT_SELECT(n, trailing_zeros)(n) -#define trailing_ones(n) UINT_SELECT(n, trailing_zeros)(~(n)) - -#define first_leading_one(n) UINT_SELECT(n, first_leading_one)(n) -#define first_leading_zero(n) UINT_SELECT(n, first_leading_one)(~(n)) - -#define first_trailing_one(n) UINT_SELECT(n, first_trailing_one)(n) -#define first_trailing_zero(n) UINT_SELECT(n, first_trailing_one)(~(n)) - -#define has_single_bit(n) UINT_SELECT(n, has_single_bit)(n) - -#define BIT_FLOOR(type, suffix, width) \ - static inline type bit_floor##suffix(type n) { \ - return n ? (type)1 << (first_leading_one##suffix(n) - 1) : 0; \ - } - -#define BIT_CEIL(type, suffix, width) \ - static inline type bit_ceil##suffix(type n) { \ - return (type)1 << first_leading_one##suffix(n - !!n); \ - } - -UINT_OVERLOADS(BIT_FLOOR) -UINT_OVERLOADS(BIT_CEIL) - -#define bit_width(n) first_leading_one(n) -#define bit_floor(n) UINT_SELECT(n, bit_floor)(n) -#define bit_ceil(n) UINT_SELECT(n, bit_ceil)(n) - -#endif // __STDC_VERSION__ < 202311L - -#endif // BFS_INT_H diff --git a/src/trie.c b/src/trie.c index 2226890..7c2f65d 100644 --- a/src/trie.c +++ b/src/trie.c @@ -82,9 +82,9 @@ */ #include "trie.h" +#include "bit.h" #include "config.h" #include "diag.h" -#include "int.h" #include "list.h" #include #include diff --git a/tests/bit.c b/tests/bit.c new file mode 100644 index 0000000..7a7b0f3 --- /dev/null +++ b/tests/bit.c @@ -0,0 +1,121 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#undef NDEBUG + +#include "../src/bit.h" +#include "../src/diag.h" +#include +#include +#include +#include + +bfs_static_assert(UMAX_WIDTH(0x1) == 1); +bfs_static_assert(UMAX_WIDTH(0x3) == 2); +bfs_static_assert(UMAX_WIDTH(0x7) == 3); +bfs_static_assert(UMAX_WIDTH(0xF) == 4); +bfs_static_assert(UMAX_WIDTH(0xFF) == 8); +bfs_static_assert(UMAX_WIDTH(0xFFF) == 12); +bfs_static_assert(UMAX_WIDTH(0xFFFF) == 16); + +#define UWIDTH_MAX(n) (2 * ((UINTMAX_C(1) << ((n) - 1)) - 1) + 1) +#define IWIDTH_MAX(n) UWIDTH_MAX((n) - 1) +#define IWIDTH_MIN(n) (-(intmax_t)IWIDTH_MAX(n) - 1) + +bfs_static_assert(UCHAR_MAX == UWIDTH_MAX(UCHAR_WIDTH)); +bfs_static_assert(SCHAR_MIN == IWIDTH_MIN(SCHAR_WIDTH)); +bfs_static_assert(SCHAR_MAX == IWIDTH_MAX(SCHAR_WIDTH)); + +bfs_static_assert(USHRT_MAX == UWIDTH_MAX(USHRT_WIDTH)); +bfs_static_assert(SHRT_MIN == IWIDTH_MIN(SHRT_WIDTH)); +bfs_static_assert(SHRT_MAX == IWIDTH_MAX(SHRT_WIDTH)); + +bfs_static_assert(UINT_MAX == UWIDTH_MAX(UINT_WIDTH)); +bfs_static_assert(INT_MIN == IWIDTH_MIN(INT_WIDTH)); +bfs_static_assert(INT_MAX == IWIDTH_MAX(INT_WIDTH)); + +bfs_static_assert(ULONG_MAX == UWIDTH_MAX(ULONG_WIDTH)); +bfs_static_assert(LONG_MIN == IWIDTH_MIN(LONG_WIDTH)); +bfs_static_assert(LONG_MAX == IWIDTH_MAX(LONG_WIDTH)); + +bfs_static_assert(ULLONG_MAX == UWIDTH_MAX(ULLONG_WIDTH)); +bfs_static_assert(LLONG_MIN == IWIDTH_MIN(LLONG_WIDTH)); +bfs_static_assert(LLONG_MAX == IWIDTH_MAX(LLONG_WIDTH)); + +bfs_static_assert(SIZE_MAX == UWIDTH_MAX(SIZE_WIDTH)); +bfs_static_assert(PTRDIFF_MIN == IWIDTH_MIN(PTRDIFF_WIDTH)); +bfs_static_assert(PTRDIFF_MAX == IWIDTH_MAX(PTRDIFF_WIDTH)); + +bfs_static_assert(UINTPTR_MAX == UWIDTH_MAX(UINTPTR_WIDTH)); +bfs_static_assert(INTPTR_MIN == IWIDTH_MIN(INTPTR_WIDTH)); +bfs_static_assert(INTPTR_MAX == IWIDTH_MAX(INTPTR_WIDTH)); + +bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); +bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); +bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); + +int main(void) { + assert(bswap((uint8_t)0x12) == 0x12); + assert(bswap((uint16_t)0x1234) == 0x3412); + assert(bswap((uint32_t)0x12345678) == 0x78563412); + assert(bswap((uint64_t)0x1234567812345678) == 0x7856341278563412); + + assert(count_ones(0x0) == 0); + assert(count_ones(0x1) == 1); + assert(count_ones(0x2) == 1); + assert(count_ones(0x3) == 2); + assert(count_ones(0x137F) == 10); + + assert(count_zeros(0) == INT_WIDTH); + assert(count_zeros(0L) == LONG_WIDTH); + assert(count_zeros(0LL) == LLONG_WIDTH); + assert(count_zeros((uint8_t)0) == 8); + assert(count_zeros((uint16_t)0) == 16); + assert(count_zeros((uint32_t)0) == 32); + assert(count_zeros((uint64_t)0) == 64); + + assert(rotate_left((uint8_t)0xA1, 4) == 0x1A); + assert(rotate_left((uint16_t)0x1234, 12) == 0x4123); + assert(rotate_left((uint32_t)0x12345678, 20) == 0x67812345); + assert(rotate_left((uint32_t)0x12345678, 0) == 0x12345678); + + assert(rotate_right((uint8_t)0xA1, 4) == 0x1A); + assert(rotate_right((uint16_t)0x1234, 12) == 0x2341); + assert(rotate_right((uint32_t)0x12345678, 20) == 0x45678123); + assert(rotate_right((uint32_t)0x12345678, 0) == 0x12345678); + + for (int i = 0; i < 16; ++i) { + uint16_t n = (uint16_t)1 << i; + for (int j = i; j < 16; ++j) { + uint16_t m = (uint16_t)1 << j; + uint16_t nm = n | m; + assert(count_ones(nm) == 1 + (n != m)); + assert(count_zeros(nm) == 15 - (n != m)); + assert(leading_zeros(nm) == 15 - j); + assert(trailing_zeros(nm) == i); + assert(first_leading_one(nm) == j + 1); + assert(first_trailing_one(nm) == i + 1); + assert(bit_width(nm) == j + 1); + assert(bit_floor(nm) == m); + if (n == m) { + assert(bit_ceil(nm) == m); + assert(has_single_bit(nm)); + } else { + if (j < 15) { + assert(bit_ceil(nm) == (m << 1)); + } + assert(!has_single_bit(nm)); + } + } + } + + assert(leading_zeros((uint16_t)0) == 16); + assert(trailing_zeros((uint16_t)0) == 16); + assert(first_leading_one(0) == 0); + assert(first_trailing_one(0) == 0); + assert(bit_width(0) == 0); + assert(bit_floor(0) == 0); + assert(bit_ceil(0) == 1); + + return EXIT_SUCCESS; +} diff --git a/tests/int.c b/tests/int.c deleted file mode 100644 index e59efde..0000000 --- a/tests/int.c +++ /dev/null @@ -1,121 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#undef NDEBUG - -#include "../src/int.h" -#include "../src/diag.h" -#include -#include -#include -#include - -bfs_static_assert(UMAX_WIDTH(0x1) == 1); -bfs_static_assert(UMAX_WIDTH(0x3) == 2); -bfs_static_assert(UMAX_WIDTH(0x7) == 3); -bfs_static_assert(UMAX_WIDTH(0xF) == 4); -bfs_static_assert(UMAX_WIDTH(0xFF) == 8); -bfs_static_assert(UMAX_WIDTH(0xFFF) == 12); -bfs_static_assert(UMAX_WIDTH(0xFFFF) == 16); - -#define UWIDTH_MAX(n) (2 * ((UINTMAX_C(1) << ((n) - 1)) - 1) + 1) -#define IWIDTH_MAX(n) UWIDTH_MAX((n) - 1) -#define IWIDTH_MIN(n) (-(intmax_t)IWIDTH_MAX(n) - 1) - -bfs_static_assert(UCHAR_MAX == UWIDTH_MAX(UCHAR_WIDTH)); -bfs_static_assert(SCHAR_MIN == IWIDTH_MIN(SCHAR_WIDTH)); -bfs_static_assert(SCHAR_MAX == IWIDTH_MAX(SCHAR_WIDTH)); - -bfs_static_assert(USHRT_MAX == UWIDTH_MAX(USHRT_WIDTH)); -bfs_static_assert(SHRT_MIN == IWIDTH_MIN(SHRT_WIDTH)); -bfs_static_assert(SHRT_MAX == IWIDTH_MAX(SHRT_WIDTH)); - -bfs_static_assert(UINT_MAX == UWIDTH_MAX(UINT_WIDTH)); -bfs_static_assert(INT_MIN == IWIDTH_MIN(INT_WIDTH)); -bfs_static_assert(INT_MAX == IWIDTH_MAX(INT_WIDTH)); - -bfs_static_assert(ULONG_MAX == UWIDTH_MAX(ULONG_WIDTH)); -bfs_static_assert(LONG_MIN == IWIDTH_MIN(LONG_WIDTH)); -bfs_static_assert(LONG_MAX == IWIDTH_MAX(LONG_WIDTH)); - -bfs_static_assert(ULLONG_MAX == UWIDTH_MAX(ULLONG_WIDTH)); -bfs_static_assert(LLONG_MIN == IWIDTH_MIN(LLONG_WIDTH)); -bfs_static_assert(LLONG_MAX == IWIDTH_MAX(LLONG_WIDTH)); - -bfs_static_assert(SIZE_MAX == UWIDTH_MAX(SIZE_WIDTH)); -bfs_static_assert(PTRDIFF_MIN == IWIDTH_MIN(PTRDIFF_WIDTH)); -bfs_static_assert(PTRDIFF_MAX == IWIDTH_MAX(PTRDIFF_WIDTH)); - -bfs_static_assert(UINTPTR_MAX == UWIDTH_MAX(UINTPTR_WIDTH)); -bfs_static_assert(INTPTR_MIN == IWIDTH_MIN(INTPTR_WIDTH)); -bfs_static_assert(INTPTR_MAX == IWIDTH_MAX(INTPTR_WIDTH)); - -bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); -bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); -bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); - -int main(void) { - assert(bswap((uint8_t)0x12) == 0x12); - assert(bswap((uint16_t)0x1234) == 0x3412); - assert(bswap((uint32_t)0x12345678) == 0x78563412); - assert(bswap((uint64_t)0x1234567812345678) == 0x7856341278563412); - - assert(count_ones(0x0) == 0); - assert(count_ones(0x1) == 1); - assert(count_ones(0x2) == 1); - assert(count_ones(0x3) == 2); - assert(count_ones(0x137F) == 10); - - assert(count_zeros(0) == INT_WIDTH); - assert(count_zeros(0L) == LONG_WIDTH); - assert(count_zeros(0LL) == LLONG_WIDTH); - assert(count_zeros((uint8_t)0) == 8); - assert(count_zeros((uint16_t)0) == 16); - assert(count_zeros((uint32_t)0) == 32); - assert(count_zeros((uint64_t)0) == 64); - - assert(rotate_left((uint8_t)0xA1, 4) == 0x1A); - assert(rotate_left((uint16_t)0x1234, 12) == 0x4123); - assert(rotate_left((uint32_t)0x12345678, 20) == 0x67812345); - assert(rotate_left((uint32_t)0x12345678, 0) == 0x12345678); - - assert(rotate_right((uint8_t)0xA1, 4) == 0x1A); - assert(rotate_right((uint16_t)0x1234, 12) == 0x2341); - assert(rotate_right((uint32_t)0x12345678, 20) == 0x45678123); - assert(rotate_right((uint32_t)0x12345678, 0) == 0x12345678); - - for (int i = 0; i < 16; ++i) { - uint16_t n = (uint16_t)1 << i; - for (int j = i; j < 16; ++j) { - uint16_t m = (uint16_t)1 << j; - uint16_t nm = n | m; - assert(count_ones(nm) == 1 + (n != m)); - assert(count_zeros(nm) == 15 - (n != m)); - assert(leading_zeros(nm) == 15 - j); - assert(trailing_zeros(nm) == i); - assert(first_leading_one(nm) == j + 1); - assert(first_trailing_one(nm) == i + 1); - assert(bit_width(nm) == j + 1); - assert(bit_floor(nm) == m); - if (n == m) { - assert(bit_ceil(nm) == m); - assert(has_single_bit(nm)); - } else { - if (j < 15) { - assert(bit_ceil(nm) == (m << 1)); - } - assert(!has_single_bit(nm)); - } - } - } - - assert(leading_zeros((uint16_t)0) == 16); - assert(trailing_zeros((uint16_t)0) == 16); - assert(first_leading_one(0) == 0); - assert(first_trailing_one(0) == 0); - assert(bit_width(0) == 0); - assert(bit_floor(0) == 0); - assert(bit_ceil(0) == 1); - - return EXIT_SUCCESS; -} -- cgit v1.2.3 From 526133c11eb9a26a4cffb20bcd10bcbb36d940de Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 May 2023 16:44:30 -0400 Subject: Switch from assert() to bfs_assert()/bfs_verify() --- src/bfstd.c | 3 +- src/bftw.c | 28 ++++++++-------- src/color.c | 5 ++- src/diag.c | 3 +- src/dir.c | 4 +-- src/eval.c | 11 +++---- src/exec.c | 7 ++-- src/opt.c | 13 ++++---- src/parse.c | 7 ++-- src/printf.c | 5 ++- src/trie.c | 33 ++++++++++--------- src/xregex.c | 4 +-- tests/bfstd.c | 13 ++++---- tests/bit.c | 100 +++++++++++++++++++++++++++++----------------------------- tests/trie.c | 56 ++++++++++++++++---------------- 15 files changed, 140 insertions(+), 152 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index 91383a2..37f5276 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -5,7 +5,6 @@ #include "config.h" #include "diag.h" #include "xregex.h" -#include #include #include #include @@ -393,7 +392,7 @@ void close_quietly(int fd) { int xclose(int fd) { int ret = close(fd); if (ret != 0) { - assert(errno != EBADF); + bfs_verify(errno != EBADF); } return ret; } diff --git a/src/bftw.c b/src/bftw.c index 14805de..6ec5cfa 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -19,13 +19,13 @@ #include "bftw.h" #include "bfstd.h" #include "config.h" +#include "diag.h" #include "dir.h" #include "dstring.h" #include "list.h" #include "mtab.h" #include "stat.h" #include "trie.h" -#include #include #include #include @@ -114,7 +114,7 @@ static void bftw_cache_remove(struct bftw_cache *cache, struct bftw_file *file) /** Close a bftw_file. */ static void bftw_file_close(struct bftw_cache *cache, struct bftw_file *file) { - assert(file->fd >= 0); + bfs_assert(file->fd >= 0); if (LIST_ATTACHED(cache, file, lru)) { bftw_cache_remove(cache, file); @@ -137,7 +137,7 @@ static int bftw_cache_pop(struct bftw_cache *cache) { /** Add a bftw_file to the cache. */ static int bftw_cache_add(struct bftw_cache *cache, struct bftw_file *file) { - assert(file->fd >= 0); + bfs_assert(file->fd >= 0); if (cache->capacity == 0 && bftw_cache_pop(cache) != 0) { bftw_file_close(cache, file); @@ -145,7 +145,7 @@ static int bftw_cache_add(struct bftw_cache *cache, struct bftw_file *file) { return -1; } - assert(cache->capacity > 0); + bfs_assert(cache->capacity > 0); --cache->capacity; LIST_INSERT(cache, cache->target, file, lru); @@ -169,9 +169,9 @@ static size_t bftw_child_nameoff(const struct bftw_file *parent) { /** Destroy a cache. */ static void bftw_cache_destroy(struct bftw_cache *cache) { - assert(!cache->head); - assert(!cache->tail); - assert(!cache->target); + bfs_assert(!cache->head); + bfs_assert(!cache->tail); + bfs_assert(!cache->target); } /** Create a new bftw_file. */ @@ -230,7 +230,7 @@ static struct bftw_file *bftw_file_new(struct bftw_file *parent, const char *nam * The opened file descriptor, or negative on error. */ static int bftw_file_openat(struct bftw_cache *cache, struct bftw_file *file, struct bftw_file *base, const char *at_path) { - assert(file->fd < 0); + bfs_assert(file->fd < 0); int at_fd = AT_FDCWD; if (base) { @@ -332,7 +332,7 @@ static struct bfs_dir *bftw_file_opendir(struct bftw_cache *cache, struct bftw_f /** Free a bftw_file. */ static void bftw_file_free(struct bftw_cache *cache, struct bftw_file *file) { - assert(file->refcount == 0); + bfs_assert(file->refcount == 0); if (file->fd >= 0) { bftw_file_close(cache, file); @@ -770,7 +770,7 @@ static enum bftw_action bftw_call_back(struct bftw_state *state, const char *nam /** Pop a directory to read from the queue. */ static bool bftw_pop_dir(struct bftw_state *state) { - assert(!state->file); + bfs_assert(!state->file); if (!state->dirs.head) { return false; @@ -787,7 +787,7 @@ static bool bftw_pop_dir(struct bftw_state *state) { /** Pop a file to visit from the queue. */ static bool bftw_pop_file(struct bftw_state *state) { - assert(!state->file); + bfs_assert(!state->file); state->file = state->files.head; if (state->file) { @@ -802,8 +802,8 @@ static bool bftw_pop_file(struct bftw_state *state) { * Open the current directory. */ static int bftw_opendir(struct bftw_state *state) { - assert(!state->dir); - assert(!state->de); + bfs_assert(!state->dir); + bfs_assert(!state->de); state->direrror = 0; @@ -863,7 +863,7 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { if (state->dir) { struct bftw_file *file = state->file; - assert(file && file->fd >= 0); + bfs_assert(file && file->fd >= 0); if (file->refcount > 1) { // Keep the fd around if any subdirectories exist diff --git a/src/color.c b/src/color.c index 43ea9a4..a723084 100644 --- a/src/color.c +++ b/src/color.c @@ -12,7 +12,6 @@ #include "fsade.h" #include "stat.h" #include "trie.h" -#include #include #include #include @@ -699,7 +698,7 @@ static int print_colored(CFILE *cfile, const char *esc, const char *str, size_t /** Find the offset of the first broken path component. */ static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags, size_t max) { ssize_t ret = max; - assert(ret >= 0); + bfs_assert(ret >= 0); if (bftw_type(ftwbuf, flags) != BFS_ERROR) { goto out; @@ -1100,7 +1099,7 @@ static int cbuff(CFILE *cfile, const char *format, ...) { } int cvfprintf(CFILE *cfile, const char *format, va_list args) { - assert(dstrlen(cfile->buffer) == 0); + bfs_assert(dstrlen(cfile->buffer) == 0); int ret = -1; if (cvbuff(cfile, format, args) == 0) { diff --git a/src/diag.c b/src/diag.c index d7ffaa6..04e3678 100644 --- a/src/diag.c +++ b/src/diag.c @@ -6,7 +6,6 @@ #include "ctx.h" #include "color.h" #include "expr.h" -#include #include #include #include @@ -117,7 +116,7 @@ static bool highlight_expr_recursive(const struct bfs_ctx *ctx, const struct bfs for (size_t i = 0; i < ctx->argc; ++i) { if (&ctx->argv[i] == expr->argv) { for (size_t j = 0; j < expr->argc; ++j) { - assert(i + j < ctx->argc); + bfs_assert(i + j < ctx->argc); args[i + j] = true; ret = true; } diff --git a/src/dir.c b/src/dir.c index 6739f10..d9ee63b 100644 --- a/src/dir.c +++ b/src/dir.c @@ -4,7 +4,7 @@ #include "dir.h" #include "bfstd.h" #include "config.h" -#include +#include "diag.h" #include #include #include @@ -249,7 +249,7 @@ int bfs_closedir(struct bfs_dir *dir) { int ret = xclose(dir->fd); #else int ret = closedir(dir->dir); - assert(ret == 0 || errno != EBADF); + bfs_verify(ret == 0 || errno != EBADF); #endif free(dir); return ret; diff --git a/src/eval.c b/src/eval.c index 6cad3a9..342debd 100644 --- a/src/eval.c +++ b/src/eval.c @@ -26,7 +26,6 @@ #include "trie.h" #include "xregex.h" #include "xtime.h" -#include #include #include #include @@ -990,7 +989,7 @@ static bool eval_expr(struct bfs_expr *expr, struct bfs_eval *state) { } } - assert(!state->quit); + bfs_assert(!state->quit); bool ret = expr->eval_fn(expr, state); @@ -1006,10 +1005,10 @@ static bool eval_expr(struct bfs_expr *expr, struct bfs_eval *state) { } if (bfs_expr_never_returns(expr)) { - assert(state->quit); + bfs_assert(state->quit); } else if (!state->quit) { - assert(!expr->always_true || ret); - assert(!expr->always_false || !ret); + bfs_assert(!expr->always_true || ret); + bfs_assert(!expr->always_false || !ret); } return ret; @@ -1511,7 +1510,7 @@ static void dump_bftw_flags(enum bftw_flags flags) { DEBUG_FLAG(flags, BFTW_SORT); DEBUG_FLAG(flags, BFTW_BUFFER); - assert(!flags); + bfs_assert(flags == 0, "Missing bftw flag 0x%X", flags); } /** diff --git a/src/exec.c b/src/exec.c index 7f22d36..5912ad6 100644 --- a/src/exec.c +++ b/src/exec.c @@ -10,7 +10,6 @@ #include "diag.h" #include "dstring.h" #include "xspawn.h" -#include #include #include #include @@ -276,12 +275,12 @@ static void bfs_exec_free_arg(char *arg, const char *tmpl) { /** Open a file to use as the working directory. */ static int bfs_exec_openwd(struct bfs_exec *execbuf, const struct BFTW *ftwbuf) { - assert(execbuf->wd_fd < 0); - assert(!execbuf->wd_path); + bfs_assert(execbuf->wd_fd < 0); + bfs_assert(!execbuf->wd_path); if (ftwbuf->at_fd != AT_FDCWD) { // Rely on at_fd being the immediate parent - assert(xbaseoff(ftwbuf->at_path) == 0); + bfs_assert(xbaseoff(ftwbuf->at_path) == 0); execbuf->wd_fd = ftwbuf->at_fd; if (!(execbuf->flags & BFS_EXEC_MULTI)) { diff --git a/src/opt.c b/src/opt.c index 4ce9425..4699af4 100644 --- a/src/opt.c +++ b/src/opt.c @@ -35,7 +35,6 @@ #include "exec.h" #include "expr.h" #include "pwcache.h" -#include #include #include #include @@ -308,7 +307,7 @@ struct opt_state { /** Log an optimization. */ BFS_FORMATTER(3, 4) static bool opt_debug(const struct opt_state *state, int level, const char *format, ...) { - assert(state->ctx->optlevel >= level); + bfs_assert(state->ctx->optlevel >= level); if (bfs_debug(state->ctx, DEBUG_OPT, "${cyn}-O%d${rs}: ", level)) { va_list args; @@ -387,7 +386,7 @@ static struct bfs_expr *de_morgan(const struct opt_state *state, struct bfs_expr has_parent = false; } - assert(expr->eval_fn == eval_and || expr->eval_fn == eval_or); + bfs_assert(expr->eval_fn == eval_and || expr->eval_fn == eval_or); if (expr->eval_fn == eval_and) { expr->eval_fn = eval_or; expr->argv = &fake_or_arg; @@ -446,7 +445,7 @@ static struct bfs_expr *optimize_expr_recursive(struct opt_state *state, struct * Optimize a negation. */ static struct bfs_expr *optimize_not_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_not); + bfs_assert(expr->eval_fn == eval_not); struct bfs_expr *rhs = expr->rhs; @@ -498,7 +497,7 @@ fail: /** Optimize a conjunction. */ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_and); + bfs_assert(expr->eval_fn == eval_and); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; @@ -569,7 +568,7 @@ fail: /** Optimize a disjunction. */ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_or); + bfs_assert(expr->eval_fn == eval_or); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; @@ -673,7 +672,7 @@ static struct bfs_expr *ignore_result(const struct opt_state *state, struct bfs_ /** Optimize a comma expression. */ static struct bfs_expr *optimize_comma_expr(const struct opt_state *state, struct bfs_expr *expr) { - assert(expr->eval_fn == eval_comma); + bfs_assert(expr->eval_fn == eval_comma); struct bfs_expr *lhs = expr->lhs; struct bfs_expr *rhs = expr->rhs; diff --git a/src/parse.c b/src/parse.c index 807892c..1a04e68 100644 --- a/src/parse.c +++ b/src/parse.c @@ -29,7 +29,6 @@ #include "xregex.h" #include "xspawn.h" #include "xtime.h" -#include #include #include #include @@ -134,7 +133,7 @@ static struct bfs_expr *new_unary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *rh expr->lhs = NULL; expr->rhs = rhs; - assert(bfs_expr_is_parent(expr)); + bfs_assert(bfs_expr_is_parent(expr)); expr->persistent_fds = rhs->persistent_fds; expr->ephemeral_fds = rhs->ephemeral_fds; @@ -154,7 +153,7 @@ static struct bfs_expr *new_binary_expr(bfs_eval_fn *eval_fn, struct bfs_expr *l expr->lhs = lhs; expr->rhs = rhs; - assert(bfs_expr_is_parent(expr)); + bfs_assert(bfs_expr_is_parent(expr)); expr->persistent_fds = lhs->persistent_fds + rhs->persistent_fds; if (lhs->ephemeral_fds > rhs->ephemeral_fds) { @@ -263,7 +262,7 @@ static void init_highlight(const struct bfs_ctx *ctx, bool *args) { static void highlight_args(const struct bfs_ctx *ctx, char **argv, size_t argc, bool *args) { size_t i = argv - ctx->argv; for (size_t j = 0; j < argc; ++j) { - assert(i + j < ctx->argc); + bfs_assert(i + j < ctx->argc); args[i + j] = true; } } diff --git a/src/printf.c b/src/printf.c index 9ccc216..6520d2d 100644 --- a/src/printf.c +++ b/src/printf.c @@ -16,7 +16,6 @@ #include "pwcache.h" #include "stat.h" #include "xtime.h" -#include #include #include #include @@ -74,7 +73,7 @@ static bool should_color(CFILE *cfile, const struct bfs_printf *directive) { #define BFS_PRINTF_BUF(buf, format, ...) \ char buf[256]; \ int ret = snprintf(buf, sizeof(buf), format, __VA_ARGS__); \ - assert(ret >= 0 && (size_t)ret < sizeof(buf)); \ + bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); \ (void)ret /** @@ -190,7 +189,7 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; } - assert(ret >= 0 && (size_t)ret < sizeof(buf)); + bfs_assert(ret >= 0 && (size_t)ret < sizeof(buf)); (void)ret; return dyn_fprintf(cfile->file, directive, buf); diff --git a/src/trie.c b/src/trie.c index a2921de..8543eb1 100644 --- a/src/trie.c +++ b/src/trie.c @@ -86,7 +86,6 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include #include #include @@ -139,27 +138,27 @@ static bool trie_is_leaf(uintptr_t ptr) { /** Decode a pointer to a leaf. */ static struct trie_leaf *trie_decode_leaf(uintptr_t ptr) { - assert(trie_is_leaf(ptr)); + bfs_assert(trie_is_leaf(ptr)); return (struct trie_leaf *)(ptr ^ 1); } /** Encode a pointer to a leaf. */ static uintptr_t trie_encode_leaf(const struct trie_leaf *leaf) { uintptr_t ptr = (uintptr_t)leaf ^ 1; - assert(trie_is_leaf(ptr)); + bfs_assert(trie_is_leaf(ptr)); return ptr; } /** Decode a pointer to an internal node. */ static struct trie_node *trie_decode_node(uintptr_t ptr) { - assert(!trie_is_leaf(ptr)); + bfs_assert(!trie_is_leaf(ptr)); return (struct trie_node *)ptr; } /** Encode a pointer to an internal node. */ static uintptr_t trie_encode_node(const struct trie_node *node) { uintptr_t ptr = (uintptr_t)node; - assert(!trie_is_leaf(ptr)); + bfs_assert(!trie_is_leaf(ptr)); return ptr; } @@ -341,9 +340,9 @@ static void trie_leaf_free(struct trie *trie, struct trie_leaf *leaf) { /** Compute the size of a trie node with a certain number of children. */ static size_t trie_node_size(unsigned int size) { // Empty nodes aren't supported - assert(size > 0); + bfs_assert(size > 0); // Node size must be a power of two - assert(has_single_bit(size)); + bfs_assert(has_single_bit(size)); return flex_sizeof(struct trie_node, children, size); } @@ -435,7 +434,7 @@ static struct trie_leaf *trie_node_insert(struct trie *trie, uintptr_t *ptr, str unsigned int bit = 1U << nibble; // The child must not already be present - assert(!(node->bitmap & bit)); + bfs_assert(!(node->bitmap & bit)); node->bitmap |= bit; unsigned int target = count_ones(node->bitmap & (bit - 1)); @@ -474,7 +473,7 @@ static struct trie_leaf *trie_node_insert(struct trie *trie, uintptr_t *ptr, str static uintptr_t *trie_jump(uintptr_t *ptr, const char *key, size_t *offset) { // We only ever need to jump to leaf nodes, since internal nodes are // guaranteed to be within OFFSET_MAX anyway - assert(trie_is_leaf(*ptr)); + bfs_assert(trie_is_leaf(*ptr)); struct trie_node *node = malloc(trie_node_size(1)); if (!node) { @@ -512,7 +511,7 @@ static uintptr_t *trie_jump(uintptr_t *ptr, const char *key, size_t *offset) { static struct trie_leaf *trie_split(struct trie *trie, uintptr_t *ptr, struct trie_leaf *leaf, struct trie_leaf *rep, size_t offset, size_t mismatch) { unsigned char key_nibble = trie_key_nibble(leaf->key, mismatch); unsigned char rep_nibble = trie_key_nibble(rep->key, mismatch); - assert(key_nibble != rep_nibble); + bfs_assert(key_nibble != rep_nibble); struct trie_node *node = malloc(trie_node_size(2)); if (!node) { @@ -570,11 +569,11 @@ static struct trie_leaf *trie_insert_mem_impl(struct trie *trie, const void *key unsigned char nibble = trie_key_nibble(key, offset); unsigned int bit = 1U << nibble; if (node->bitmap & bit) { - assert(offset < mismatch); + bfs_assert(offset < mismatch); unsigned int index = count_ones(node->bitmap & (bit - 1)); ptr = &node->children[index]; } else { - assert(offset == mismatch); + bfs_assert(offset == mismatch); return trie_node_insert(trie, ptr, leaf, nibble); } } @@ -600,7 +599,7 @@ static void trie_free_singletons(struct trie *trie, uintptr_t ptr) { struct trie_node *node = trie_decode_node(ptr); // Make sure the bitmap is a power of two, i.e. it has just one child - assert(has_single_bit(node->bitmap)); + bfs_assert(has_single_bit(node->bitmap)); ptr = node->children[0]; free(node); @@ -651,12 +650,12 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { while (!trie_is_leaf(*child)) { struct trie_node *node = trie_decode_node(*child); offset += node->offset; - assert((offset >> 1) < leaf->length); + bfs_assert((offset >> 1) < leaf->length); unsigned char nibble = trie_key_nibble(leaf->key, offset); unsigned int bit = 1U << nibble; unsigned int bitmap = node->bitmap; - assert(bitmap & bit); + bfs_assert(bitmap & bit); unsigned int index = count_ones(bitmap & (bit - 1)); // Advance the parent pointer, unless this node had only one child @@ -669,7 +668,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { child = &node->children[index]; } - assert(trie_decode_leaf(*child) == leaf); + bfs_assert(trie_decode_leaf(*child) == leaf); if (!parent) { trie_free_singletons(trie, trie->root); @@ -683,7 +682,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { node->bitmap ^= child_bit; unsigned int parent_size = count_ones(node->bitmap); - assert(parent_size > 0); + bfs_assert(parent_size > 0); if (parent_size == 1 && trie_collapse_node(parent, node, child_index) == 0) { return; } diff --git a/src/xregex.c b/src/xregex.c index a7153b7..1143f23 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -3,7 +3,7 @@ #include "xregex.h" #include "config.h" -#include +#include "diag.h" #include #include #include @@ -140,7 +140,7 @@ int bfs_regcomp(struct bfs_regex **preg, const char *pattern, enum bfs_regex_typ syntax = ONIG_SYNTAX_GREP; break; } - assert(syntax); + bfs_assert(syntax); OnigOptionType options = syntax->options; if (flags & BFS_REGEX_ICASE) { diff --git a/tests/bfstd.c b/tests/bfstd.c index a986a23..1812a00 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,10 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#undef NDEBUG #include "../src/bfstd.h" #include "../src/config.h" -#include +#include "../src/diag.h" #include #include #include @@ -39,11 +38,11 @@ int main(void) { alignas(64) int foo; int bar[]; }; - assert(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); - assert(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); - assert(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) - == align_floor(alignof(struct flexible), SIZE_MAX)); - assert(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); + bfs_verify(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); + bfs_verify(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); + bfs_verify(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) + == align_floor(alignof(struct flexible), SIZE_MAX)); + bfs_verify(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); // From man 3p basename check_base_dir("usr", ".", "usr"); diff --git a/tests/bit.c b/tests/bit.c index 7a7b0f3..cb339f4 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -1,11 +1,8 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#undef NDEBUG - #include "../src/bit.h" #include "../src/diag.h" -#include #include #include #include @@ -54,68 +51,71 @@ bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); +#define verify_eq(a, b) \ + bfs_verify((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) + int main(void) { - assert(bswap((uint8_t)0x12) == 0x12); - assert(bswap((uint16_t)0x1234) == 0x3412); - assert(bswap((uint32_t)0x12345678) == 0x78563412); - assert(bswap((uint64_t)0x1234567812345678) == 0x7856341278563412); - - assert(count_ones(0x0) == 0); - assert(count_ones(0x1) == 1); - assert(count_ones(0x2) == 1); - assert(count_ones(0x3) == 2); - assert(count_ones(0x137F) == 10); - - assert(count_zeros(0) == INT_WIDTH); - assert(count_zeros(0L) == LONG_WIDTH); - assert(count_zeros(0LL) == LLONG_WIDTH); - assert(count_zeros((uint8_t)0) == 8); - assert(count_zeros((uint16_t)0) == 16); - assert(count_zeros((uint32_t)0) == 32); - assert(count_zeros((uint64_t)0) == 64); - - assert(rotate_left((uint8_t)0xA1, 4) == 0x1A); - assert(rotate_left((uint16_t)0x1234, 12) == 0x4123); - assert(rotate_left((uint32_t)0x12345678, 20) == 0x67812345); - assert(rotate_left((uint32_t)0x12345678, 0) == 0x12345678); - - assert(rotate_right((uint8_t)0xA1, 4) == 0x1A); - assert(rotate_right((uint16_t)0x1234, 12) == 0x2341); - assert(rotate_right((uint32_t)0x12345678, 20) == 0x45678123); - assert(rotate_right((uint32_t)0x12345678, 0) == 0x12345678); + verify_eq(bswap((uint8_t)0x12), 0x12); + verify_eq(bswap((uint16_t)0x1234), 0x3412); + verify_eq(bswap((uint32_t)0x12345678), 0x78563412); + verify_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); + + verify_eq(count_ones(0x0), 0); + verify_eq(count_ones(0x1), 1); + verify_eq(count_ones(0x2), 1); + verify_eq(count_ones(0x3), 2); + verify_eq(count_ones(0x137F), 10); + + verify_eq(count_zeros(0), INT_WIDTH); + verify_eq(count_zeros(0L), LONG_WIDTH); + verify_eq(count_zeros(0LL), LLONG_WIDTH); + verify_eq(count_zeros((uint8_t)0), 8); + verify_eq(count_zeros((uint16_t)0), 16); + verify_eq(count_zeros((uint32_t)0), 32); + verify_eq(count_zeros((uint64_t)0), 64); + + verify_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); + verify_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); + verify_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); + verify_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); + + verify_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); + verify_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); + verify_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); + verify_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); for (int i = 0; i < 16; ++i) { uint16_t n = (uint16_t)1 << i; for (int j = i; j < 16; ++j) { uint16_t m = (uint16_t)1 << j; uint16_t nm = n | m; - assert(count_ones(nm) == 1 + (n != m)); - assert(count_zeros(nm) == 15 - (n != m)); - assert(leading_zeros(nm) == 15 - j); - assert(trailing_zeros(nm) == i); - assert(first_leading_one(nm) == j + 1); - assert(first_trailing_one(nm) == i + 1); - assert(bit_width(nm) == j + 1); - assert(bit_floor(nm) == m); + verify_eq(count_ones(nm), 1 + (n != m)); + verify_eq(count_zeros(nm), 15 - (n != m)); + verify_eq(leading_zeros(nm), 15 - j); + verify_eq(trailing_zeros(nm), i); + verify_eq(first_leading_one(nm), j + 1); + verify_eq(first_trailing_one(nm), i + 1); + verify_eq(bit_width(nm), j + 1); + verify_eq(bit_floor(nm), m); if (n == m) { - assert(bit_ceil(nm) == m); - assert(has_single_bit(nm)); + verify_eq(bit_ceil(nm), m); + bfs_verify(has_single_bit(nm)); } else { if (j < 15) { - assert(bit_ceil(nm) == (m << 1)); + verify_eq(bit_ceil(nm), (m << 1)); } - assert(!has_single_bit(nm)); + bfs_verify(!has_single_bit(nm)); } } } - assert(leading_zeros((uint16_t)0) == 16); - assert(trailing_zeros((uint16_t)0) == 16); - assert(first_leading_one(0) == 0); - assert(first_trailing_one(0) == 0); - assert(bit_width(0) == 0); - assert(bit_floor(0) == 0); - assert(bit_ceil(0) == 1); + verify_eq(leading_zeros((uint16_t)0), 16); + verify_eq(trailing_zeros((uint16_t)0), 16); + verify_eq(first_leading_one(0), 0); + verify_eq(first_trailing_one(0), 0); + verify_eq(bit_width(0), 0); + verify_eq(bit_floor(0), 0); + verify_eq(bit_ceil(0), 1); return EXIT_SUCCESS; } diff --git a/tests/trie.c b/tests/trie.c index ced14d4..e687f96 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,11 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#undef NDEBUG - #include "../src/trie.h" #include "../src/config.h" -#include +#include "../src/diag.h" #include #include @@ -45,7 +43,7 @@ int main(void) { trie_init(&trie); for (size_t i = 0; i < nkeys; ++i) { - assert(!trie_find_str(&trie, keys[i])); + bfs_verify(!trie_find_str(&trie, keys[i])); const char *prefix = NULL; for (size_t j = 0; j < i; ++j) { @@ -58,38 +56,38 @@ int main(void) { struct trie_leaf *leaf = trie_find_prefix(&trie, keys[i]); if (prefix) { - assert(leaf); - assert(strcmp(prefix, leaf->key) == 0); + bfs_verify(leaf); + bfs_verify(strcmp(prefix, leaf->key) == 0); } else { - assert(!leaf); + bfs_verify(!leaf); } leaf = trie_insert_str(&trie, keys[i]); - assert(leaf); - assert(strcmp(keys[i], leaf->key) == 0); - assert(leaf->length == strlen(keys[i]) + 1); + bfs_verify(leaf); + bfs_verify(strcmp(keys[i], leaf->key) == 0); + bfs_verify(leaf->length == strlen(keys[i]) + 1); } { size_t i = 0; TRIE_FOR_EACH(&trie, leaf) { - assert(leaf == trie_find_str(&trie, keys[i])); - assert(!leaf->prev || leaf->prev->next == leaf); - assert(!leaf->next || leaf->next->prev == leaf); + bfs_verify(leaf == trie_find_str(&trie, keys[i])); + bfs_verify(!leaf->prev || leaf->prev->next == leaf); + bfs_verify(!leaf->next || leaf->next->prev == leaf); ++i; } - assert(i == nkeys); + bfs_verify(i == nkeys); } for (size_t i = 0; i < nkeys; ++i) { struct trie_leaf *leaf = trie_find_str(&trie, keys[i]); - assert(leaf); - assert(strcmp(keys[i], leaf->key) == 0); - assert(leaf->length == strlen(keys[i]) + 1); + bfs_verify(leaf); + bfs_verify(strcmp(keys[i], leaf->key) == 0); + bfs_verify(leaf->length == strlen(keys[i]) + 1); trie_remove(&trie, leaf); leaf = trie_find_str(&trie, keys[i]); - assert(!leaf); + bfs_verify(!leaf); const char *postfix = NULL; for (size_t j = i + 1; j < nkeys; ++j) { @@ -102,33 +100,33 @@ int main(void) { leaf = trie_find_postfix(&trie, keys[i]); if (postfix) { - assert(leaf); - assert(strcmp(postfix, leaf->key) == 0); + bfs_verify(leaf); + bfs_verify(strcmp(postfix, leaf->key) == 0); } else { - assert(!leaf); + bfs_verify(!leaf); } } TRIE_FOR_EACH(&trie, leaf) { - assert(false); + bfs_verify(false); } // This tests the "jump" node handling on 32-bit platforms size_t longsize = 1 << 20; char *longstr = malloc(longsize); - assert(longstr); + bfs_verify(longstr); memset(longstr, 0xAC, longsize); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + bfs_verify(!trie_find_mem(&trie, longstr, longsize)); + bfs_verify(trie_insert_mem(&trie, longstr, longsize)); memset(longstr + longsize/2, 0xAB, longsize/2); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + bfs_verify(!trie_find_mem(&trie, longstr, longsize)); + bfs_verify(trie_insert_mem(&trie, longstr, longsize)); memset(longstr, 0xAA, longsize/2); - assert(!trie_find_mem(&trie, longstr, longsize)); - assert(trie_insert_mem(&trie, longstr, longsize)); + bfs_verify(!trie_find_mem(&trie, longstr, longsize)); + bfs_verify(trie_insert_mem(&trie, longstr, longsize)); free(longstr); trie_destroy(&trie); -- cgit v1.2.3 From f642b3dd1271e4a1503ce7629ba36e25d6a59fce Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 19 May 2023 11:33:32 -0400 Subject: tests/bfstd: Use bfs_verify() over explicit abort() --- tests/bfstd.c | 19 +++++-------------- 1 file changed, 5 insertions(+), 14 deletions(-) (limited to 'tests') diff --git a/tests/bfstd.c b/tests/bfstd.c index 1812a00..7fea9b5 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -4,6 +4,7 @@ #include "../src/bfstd.h" #include "../src/config.h" #include "../src/diag.h" +#include #include #include #include @@ -12,23 +13,13 @@ /** Check the result of xdirname()/xbasename(). */ static void check_base_dir(const char *path, const char *dir, const char *base) { char *xdir = xdirname(path); - if (!xdir) { - perror("xdirname()"); - abort(); - } else if (strcmp(xdir, dir) != 0) { - fprintf(stderr, "xdirname(\"%s\") == \"%s\" (!= \"%s\")\n", path, xdir, dir); - abort(); - } + bfs_verify(xdir, "xdirname(): %s", strerror(errno)); + bfs_verify(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); free(xdir); char *xbase = xbasename(path); - if (!xbase) { - perror("xbasename()"); - abort(); - } else if (strcmp(xbase, base) != 0) { - fprintf(stderr, "xbasename(\"%s\") == \"%s\" (!= \"%s\")\n", path, xbase, base); - abort(); - } + bfs_verify(xbase, "xbasename(): %s", strerror(errno)); + bfs_verify(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); free(xbase); } -- cgit v1.2.3 From 1d1443193878ae72e54560bb21de79668cd954b9 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 8 Jun 2023 13:19:57 -0400 Subject: color: `fi=0` should not fall back to `no` --- src/color.c | 8 ++++---- tests/bfs/color_cd0_no.out | 20 ++++++++++++++++++++ tests/bfs/color_cd0_no.sh | 1 + tests/bfs/color_fi0_no.out | 20 ++++++++++++++++++++ tests/bfs/color_fi0_no.sh | 1 + tests/bfs/color_fi_no.out | 20 ++++++++++++++++++++ tests/bfs/color_fi_no.sh | 1 + tests/bfs/color_no.out | 20 ++++++++++++++++++++ tests/bfs/color_no.sh | 1 + 9 files changed, 88 insertions(+), 4 deletions(-) create mode 100644 tests/bfs/color_cd0_no.out create mode 100644 tests/bfs/color_cd0_no.sh create mode 100644 tests/bfs/color_fi0_no.out create mode 100644 tests/bfs/color_fi0_no.sh create mode 100644 tests/bfs/color_fi_no.out create mode 100644 tests/bfs/color_fi_no.sh create mode 100644 tests/bfs/color_no.out create mode 100644 tests/bfs/color_no.sh (limited to 'tests') diff --git a/src/color.c b/src/color.c index a723084..87575c9 100644 --- a/src/color.c +++ b/src/color.c @@ -436,10 +436,11 @@ struct colors *parse_colors(void) { ret |= init_color(colors, "err", "01;31", &colors->error); // Defaults from man dir_colors + // "" means fall back to ->normal ret |= init_color(colors, "no", NULL, &colors->normal); - ret |= init_color(colors, "fi", NULL, &colors->file); + ret |= init_color(colors, "fi", "", &colors->file); ret |= init_color(colors, "mh", NULL, &colors->multi_hard); ret |= init_color(colors, "ex", "01;32", &colors->executable); ret |= init_color(colors, "ca", NULL, &colors->capable); @@ -472,8 +473,7 @@ struct colors *parse_colors(void) { if (colors->link && strcmp(colors->link, "target") == 0) { colors->link_as_target = true; - dstrfree(colors->link); - colors->link = NULL; + dstresize(&colors->link, 0); } return colors; @@ -634,7 +634,7 @@ static const char *file_color(const struct colors *colors, const char *filename, break; } - if (!color) { + if (color && !color[0]) { color = colors->normal; } diff --git a/tests/bfs/color_cd0_no.out b/tests/bfs/color_cd0_no.out new file mode 100644 index 0000000..d5c98a9 --- /dev/null +++ b/tests/bfs/color_cd0_no.out @@ -0,0 +1,20 @@ +rainbow +rainbow/exec.sh +rainbow/socket +rainbow/broken +rainbow/file.dat +rainbow/file.txt +rainbow/link.txt +rainbow/mh1 +rainbow/mh2 +rainbow/star.gz +rainbow/star.tar +rainbow/star.tar.gz +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky +rainbow/chardev_link diff --git a/tests/bfs/color_cd0_no.sh b/tests/bfs/color_cd0_no.sh new file mode 100644 index 0000000..325a782 --- /dev/null +++ b/tests/bfs/color_cd0_no.sh @@ -0,0 +1 @@ +LS_COLORS="ln=target:cd=0:no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_fi0_no.out b/tests/bfs/color_fi0_no.out new file mode 100644 index 0000000..77fc8a8 --- /dev/null +++ b/tests/bfs/color_fi0_no.out @@ -0,0 +1,20 @@ +rainbow +rainbow/exec.sh +rainbow/socket +rainbow/broken +rainbow/chardev_link +rainbow/link.txt +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky +rainbow/file.dat +rainbow/file.txt +rainbow/mh1 +rainbow/mh2 +rainbow/star.gz +rainbow/star.tar +rainbow/star.tar.gz diff --git a/tests/bfs/color_fi0_no.sh b/tests/bfs/color_fi0_no.sh new file mode 100644 index 0000000..f947d64 --- /dev/null +++ b/tests/bfs/color_fi0_no.sh @@ -0,0 +1 @@ +LS_COLORS="fi=0:no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_fi_no.out b/tests/bfs/color_fi_no.out new file mode 100644 index 0000000..7162ded --- /dev/null +++ b/tests/bfs/color_fi_no.out @@ -0,0 +1,20 @@ +rainbow +rainbow/exec.sh +rainbow/socket +rainbow/broken +rainbow/chardev_link +rainbow/link.txt +rainbow/file.dat +rainbow/file.txt +rainbow/mh1 +rainbow/mh2 +rainbow/star.gz +rainbow/star.tar +rainbow/star.tar.gz +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky diff --git a/tests/bfs/color_fi_no.sh b/tests/bfs/color_fi_no.sh new file mode 100644 index 0000000..c2b4ec7 --- /dev/null +++ b/tests/bfs/color_fi_no.sh @@ -0,0 +1 @@ +LS_COLORS="fi=01;91:no=01;92:" bfs_diff rainbow -color diff --git a/tests/bfs/color_no.out b/tests/bfs/color_no.out new file mode 100644 index 0000000..9f0dd66 --- /dev/null +++ b/tests/bfs/color_no.out @@ -0,0 +1,20 @@ +rainbow +rainbow/exec.sh +rainbow/socket +rainbow/broken +rainbow/chardev_link +rainbow/link.txt +rainbow/file.dat +rainbow/file.txt +rainbow/mh1 +rainbow/mh2 +rainbow/star.gz +rainbow/star.tar +rainbow/star.tar.gz +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky diff --git a/tests/bfs/color_no.sh b/tests/bfs/color_no.sh new file mode 100644 index 0000000..b7527cb --- /dev/null +++ b/tests/bfs/color_no.sh @@ -0,0 +1 @@ +LS_COLORS="no=01;92:" bfs_diff rainbow -color -- cgit v1.2.3 From 90ded13e589b0089167ef25ca3d26be599dfec9b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 19 Jun 2023 12:11:36 -0400 Subject: alloc: New header for memory allocation utilities --- Makefile | 3 +- src/alloc.c | 50 ++++++++++++++++++++ src/alloc.h | 149 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/bfstd.c | 13 ----- src/bfstd.h | 12 ----- src/bftw.c | 5 +- src/color.c | 5 +- src/config.h | 50 -------------------- src/ctx.c | 31 ++---------- src/dstring.c | 3 +- src/exec.c | 26 ++++------ src/ioq.c | 26 ++++------ src/ioq.h | 4 +- src/main.c | 1 + src/mtab.c | 5 +- src/parse.c | 28 ++--------- src/pwcache.c | 5 +- src/trie.c | 7 ++- src/xregex.c | 3 +- src/xspawn.c | 3 +- tests/alloc.c | 24 ++++++++++ tests/bfstd.c | 13 +---- 22 files changed, 271 insertions(+), 195 deletions(-) create mode 100644 src/alloc.c create mode 100644 src/alloc.h create mode 100644 tests/alloc.c (limited to 'tests') diff --git a/Makefile b/Makefile index fb28e29..d38f581 100644 --- a/Makefile +++ b/Makefile @@ -217,6 +217,7 @@ $(OBJ)/FLAGS: $(OBJ)/FLAGS.new # All object files except the entry point LIBBFS := \ + $(OBJ)/src/alloc.o \ $(OBJ)/src/bar.o \ $(OBJ)/src/bfstd.o \ $(OBJ)/src/bftw.o \ @@ -246,7 +247,7 @@ LIBBFS := \ $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) # Standalone unit tests -UNITS := bfstd bit trie xtimegm +UNITS := alloc bfstd bit trie xtimegm UNIT_TESTS := $(UNITS:%=$(BIN)/tests/%) UNIT_CHECKS := $(UNITS:%=check-%) diff --git a/src/alloc.c b/src/alloc.c new file mode 100644 index 0000000..0003108 --- /dev/null +++ b/src/alloc.c @@ -0,0 +1,50 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "alloc.h" +#include "bit.h" +#include "diag.h" +#include +#include +#include + +/** Portable aligned_alloc()/posix_memalign(). */ +static void *xmemalign(size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert(align >= sizeof(void *)); + bfs_assert((size & (align - 1)) == 0); + +#if __APPLE__ + void *ptr = NULL; + errno = posix_memalign(&ptr, align, size); + return ptr; +#else + return aligned_alloc(align, size); +#endif +} + +void *alloc(size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert((size & (align - 1)) == 0); + + if (align <= alignof(max_align_t)) { + return malloc(size); + } else { + return xmemalign(align, size); + } +} + +void *zalloc(size_t align, size_t size) { + bfs_assert(has_single_bit(align)); + bfs_assert((size & (align - 1)) == 0); + + if (align <= alignof(max_align_t)) { + return calloc(1, size); + } + + void *ret = xmemalign(align, size); + if (ret) { + memset(ret, 0, size); + } + return ret; +} diff --git a/src/alloc.h b/src/alloc.h new file mode 100644 index 0000000..899a4ec --- /dev/null +++ b/src/alloc.h @@ -0,0 +1,149 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Memory allocation. + */ + +#ifndef BFS_ALLOC_H +#define BFS_ALLOC_H + +#include "config.h" +#include + +/** Round down to a multiple of an alignment. */ +static inline size_t align_floor(size_t align, size_t size) { + return size & ~(align - 1); +} + +/** Round up to a multiple of an alignment. */ +static inline size_t align_ceil(size_t align, size_t size) { + return align_floor(align, size + align - 1); +} + +/** + * Saturating array size. + * + * @param align + * Array element alignment. + * @param size + * Array element size. + * @param count + * Array element count. + * @return + * size * count, saturating to the maximum aligned value on overflow. + */ +static inline size_t array_size(size_t align, size_t size, size_t count) { + size_t ret = size * count; + return ret / size == count ? ret : ~(align - 1); +} + +/** Saturating array sizeof. */ +#define sizeof_array(type, count) \ + array_size(alignof(type), sizeof(type), count) + +/** Size of a struct/union field. */ +#define sizeof_member(type, member) \ + sizeof(((type *)NULL)->member) + +/** + * Saturating flexible struct size. + * + * @param align + * Struct alignment. + * @param min + * Minimum struct size. + * @param offset + * Flexible array member offset. + * @param size + * Flexible array element size. + * @param count + * Flexible array element count. + * @return + * The size of the struct with count flexible array elements. Saturates + * to the maximum aligned value on overflow. + */ +static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t size, size_t count) { + size_t ret = size * count; + size_t overflow = ret / size != count; + + size_t extra = offset + align - 1; + ret += extra; + overflow |= ret < extra; + ret |= -overflow; + ret = align_floor(align, ret); + + // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the + // type has more padding than necessary for alignment + if (min > align_ceil(align, offset)) { + ret = ret < min ? min : ret; + } + + return ret; +} + +/** + * Computes the size of a flexible struct. + * + * @param type + * The type of the struct containing the flexible array. + * @param member + * The name of the flexible array member. + * @param count + * The length of the flexible array. + * @return + * The size of the struct with count flexible array elements. Saturates + * to the maximum aligned value on overflow. + */ +#define sizeof_flex(type, member, count) \ + flex_size(alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0]), count) + +/** + * General memory allocator. + * + * @param align + * The required alignment. + * @param size + * The size of the allocation. + * @return + * The allocated memory, or NULL on failure. + */ +void *alloc(size_t align, size_t size); + +/** + * Zero-initialized memory allocator. + * + * @param align + * The required alignment. + * @param size + * The size of the allocation. + * @return + * The allocated memory, or NULL on failure. + */ +void *zalloc(size_t align, size_t size); + +/** Allocate memory for the given type. */ +#define ALLOC(type) \ + (type *)alloc(alignof(type), sizeof(type)) + +/** Allocate zeroed memory for the given type. */ +#define ZALLOC(type) \ + (type *)zalloc(alignof(type), sizeof(type)) + +/** Allocate memory for an array. */ +#define ALLOC_ARRAY(type, count) \ + (type *)alloc(alignof(type), sizeof_array(type, count)); + +/** Allocate zeroed memory for an array. */ +#define ZALLOC_ARRAY(type, count) \ + (type *)zalloc(alignof(type), sizeof_array(type, count)); + +/** Allocate memory for a flexible struct. */ +#define ALLOC_FLEX(type, member, count) \ + (type *)alloc(alignof(type), sizeof_flex(type, member, count)) + +/** Allocate zeroed memory for a flexible struct. */ +#define ZALLOC_FLEX(type, member, count) \ + (type *)zalloc(alignof(type), sizeof_flex(type, member, count)) + +#endif // BFS_ALLOC_H diff --git a/src/bfstd.c b/src/bfstd.c index 856c76c..0e8ba5f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -143,19 +143,6 @@ char *xgetdelim(FILE *file, char delim) { } } -void *xmemalign(size_t align, size_t size) { - bfs_assert(has_single_bit(align)); - bfs_assert((size & (align - 1)) == 0); - -#if __APPLE__ - void *ptr = NULL; - errno = posix_memalign(&ptr, align, size); - return ptr; -#else - return aligned_alloc(align, size); -#endif -} - /** Compile and execute a regular expression for xrpmatch(). */ static int xrpregex(nl_item item, const char *response) { const char *pattern = nl_langinfo(item); diff --git a/src/bfstd.h b/src/bfstd.h index 6f2e21e..cafe28f 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -105,18 +105,6 @@ char *xgetdelim(FILE *file, char delim); // #include -/** - * Portable version of aligned_alloc()/posix_memalign(). - * - * @param align - * The allocation's alignment. - * @param size - * The allocation's size. - * @return - * The allocation, or NULL on failure. - */ -void *xmemalign(size_t align, size_t size); - /** * Process a yes/no prompt. * diff --git a/src/bftw.c b/src/bftw.c index e711963..7ab14c7 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -17,6 +17,7 @@ */ #include "bftw.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "diag.h" @@ -241,9 +242,7 @@ static void bftw_cache_destroy(struct bftw_cache *cache) { /** Create a new bftw_file. */ static struct bftw_file *bftw_file_new(struct bftw_file *parent, const char *name) { size_t namelen = strlen(name); - size_t size = flex_sizeof(struct bftw_file, name, namelen + 1); - - struct bftw_file *file = malloc(size); + struct bftw_file *file = ALLOC_FLEX(struct bftw_file, name, namelen + 1); if (!file) { return NULL; } diff --git a/src/color.c b/src/color.c index 1edd8b5..b54ad53 100644 --- a/src/color.c +++ b/src/color.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "color.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "config.h" @@ -404,7 +405,7 @@ static void parse_gnu_ls_colors(struct colors *colors, const char *ls_colors) { } struct colors *parse_colors(void) { - struct colors *colors = malloc(sizeof(struct colors)); + struct colors *colors = ALLOC(struct colors); if (!colors) { return NULL; } @@ -497,7 +498,7 @@ void free_colors(struct colors *colors) { } CFILE *cfwrap(FILE *file, const struct colors *colors, bool close) { - CFILE *cfile = malloc(sizeof(*cfile)); + CFILE *cfile = ALLOC(CFILE); if (!cfile) { return NULL; } diff --git a/src/config.h b/src/config.h index 73348ac..1671a0d 100644 --- a/src/config.h +++ b/src/config.h @@ -141,56 +141,6 @@ */ #define countof(array) (sizeof(array) / sizeof(0[array])) -/** - * Round down to a multiple of an alignment. - */ -static inline size_t align_floor(size_t align, size_t size) { - return size & ~(align - 1); -} - -/** - * Round up to a multiple of an alignment. - */ -static inline size_t align_ceil(size_t align, size_t size) { - return align_floor(align, size + align - 1); -} - -/** - * Computes the size of a struct containing a flexible array member of the given - * length. - * - * @param type - * The type of the struct containing the flexible array. - * @param member - * The name of the flexible array member. - * @param count - * The length of the flexible array. - */ -#define flex_sizeof(type, member, count) \ - flex_sizeof_impl(alignof(type), sizeof(type), offsetof(type, member), sizeof(((type *)NULL)->member[0]), count) - -static inline size_t flex_sizeof_impl(size_t align, size_t min, size_t offset, size_t size, size_t count) { - size_t ret = size * count; - size_t overflow = ret / size != count; - - ret += offset; - overflow |= ret < offset; - - size_t mask = align - 1; - ret += mask; - overflow |= ret < mask; - ret |= -overflow; - ret &= ~mask; - - // Make sure flex_sizeof(type, member, 0) >= sizeof(type), even if the - // type has more padding than necessary for alignment - if (min > align_ceil(align, offset) && ret < min) { - ret = min; - } - - return ret; -} - /** * False sharing/destructive interference/largest cache line size. */ diff --git a/src/ctx.c b/src/ctx.c index e8ce0e8..a940bed 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "ctx.h" +#include "alloc.h" #include "color.h" #include "darray.h" #include "diag.h" @@ -42,43 +43,17 @@ const char *debug_flag_name(enum debug_flags flag) { } struct bfs_ctx *bfs_ctx_new(void) { - struct bfs_ctx *ctx = malloc(sizeof(*ctx)); + struct bfs_ctx *ctx = ZALLOC(struct bfs_ctx); if (!ctx) { return NULL; } - ctx->argv = NULL; - ctx->paths = NULL; - ctx->expr = NULL; - ctx->exclude = NULL; - - ctx->mindepth = 0; ctx->maxdepth = INT_MAX; ctx->flags = BFTW_RECOVER; ctx->strategy = BFTW_BFS; - ctx->threads = 0; ctx->optlevel = 3; - ctx->debug = 0; - ctx->ignore_races = false; - ctx->posixly_correct = false; - ctx->status = false; - ctx->unique = false; - ctx->warn = false; - ctx->xargs_safe = false; - - ctx->colors = NULL; - ctx->colors_error = 0; - ctx->cout = NULL; - ctx->cerr = NULL; - - ctx->users = NULL; - ctx->groups = NULL; - - ctx->mtab = NULL; - ctx->mtab_error = 0; trie_init(&ctx->files); - ctx->nfiles = 0; struct rlimit rl; if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { @@ -155,7 +130,7 @@ CFILE *bfs_ctx_dedup(struct bfs_ctx *ctx, CFILE *cfile, const char *path) { return ctx_file->cfile; } - leaf->value = ctx_file = malloc(sizeof(*ctx_file)); + leaf->value = ctx_file = ALLOC(struct bfs_ctx_file); if (!ctx_file) { trie_remove(&ctx->files, leaf); return NULL; diff --git a/src/dstring.c b/src/dstring.c index 2c9869d..7ca74d0 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "dstring.h" +#include "alloc.h" #include "diag.h" #include #include @@ -24,7 +25,7 @@ static struct dstring *dstrheader(const char *dstr) { /** Get the correct size for a dstring with the given capacity. */ static size_t dstrsize(size_t capacity) { - return flex_sizeof(struct dstring, data, capacity + 1); + return sizeof_flex(struct dstring, data, capacity + 1); } /** Allocate a dstring with the given contents. */ diff --git a/src/exec.c b/src/exec.c index 5912ad6..ea7f897 100644 --- a/src/exec.c +++ b/src/exec.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "exec.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "ctx.h" @@ -124,26 +125,16 @@ static void bfs_exec_parse_error(const struct bfs_ctx *ctx, const struct bfs_exe } struct bfs_exec *bfs_exec_parse(const struct bfs_ctx *ctx, char **argv, enum bfs_exec_flags flags) { - struct bfs_exec *execbuf = malloc(sizeof(*execbuf)); + struct bfs_exec *execbuf = ZALLOC(struct bfs_exec); if (!execbuf) { - bfs_perror(ctx, "malloc()"); + bfs_perror(ctx, "zalloc()"); goto fail; } execbuf->flags = flags; execbuf->ctx = ctx; execbuf->tmpl_argv = argv + 1; - execbuf->tmpl_argc = 0; - execbuf->argv = NULL; - execbuf->argc = 0; - execbuf->argv_cap = 0; - execbuf->arg_size = 0; - execbuf->arg_max = 0; - execbuf->arg_min = 0; execbuf->wd_fd = -1; - execbuf->wd_path = NULL; - execbuf->wd_len = 0; - execbuf->ret = 0; while (true) { const char *arg = execbuf->tmpl_argv[execbuf->tmpl_argc]; @@ -176,9 +167,9 @@ struct bfs_exec *bfs_exec_parse(const struct bfs_ctx *ctx, char **argv, enum bfs } execbuf->argv_cap = execbuf->tmpl_argc + 1; - execbuf->argv = malloc(execbuf->argv_cap*sizeof(*execbuf->argv)); + execbuf->argv = ALLOC_ARRAY(char *, execbuf->argv_cap); if (!execbuf->argv) { - bfs_perror(ctx, "malloc()"); + bfs_perror(ctx, "alloc()"); goto fail; } @@ -224,9 +215,8 @@ static char *bfs_exec_format_path(const struct bfs_exec *execbuf, const struct B return NULL; } - strcpy(path, "./"); - strcpy(path + 2, name); - + char *cur = stpcpy(path, "./"); + cur = stpcpy(cur, name); return path; } @@ -612,7 +602,7 @@ static int bfs_exec_push(struct bfs_exec *execbuf, char *arg) { if (execbuf->argc + 1 >= execbuf->argv_cap) { size_t cap = 2*execbuf->argv_cap; - char **argv = realloc(execbuf->argv, cap*sizeof(*argv)); + char **argv = realloc(execbuf->argv, sizeof_array(char *, cap)); if (!argv) { return -1; } diff --git a/src/ioq.c b/src/ioq.c index 5550c91..3e304ce 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "ioq.h" +#include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" @@ -114,7 +115,7 @@ static struct ioqq *ioqq_create(size_t size) { // Circular buffer size must be a power of two size = bit_ceil(size); - struct ioqq *ioqq = xmemalign(alignof(struct ioqq), flex_sizeof(struct ioqq, slots, size)); + struct ioqq *ioqq = ALLOC_FLEX(struct ioqq, slots, size); if (!ioqq) { return NULL; } @@ -124,7 +125,7 @@ static struct ioqq *ioqq_create(size_t size) { // Use a pool of monitors size_t nmonitors = size < 64 ? size : 64; - ioqq->monitors = xmemalign(alignof(struct ioq_monitor), nmonitors * sizeof(struct ioq_monitor)); + ioqq->monitors = ALLOC_ARRAY(struct ioq_monitor, nmonitors); if (!ioqq->monitors) { ioqq_destroy(ioqq); return NULL; @@ -273,7 +274,7 @@ struct ioq { /** The number of background threads. */ size_t nthreads; /** The background threads themselves. */ - pthread_t *threads; + pthread_t threads[]; }; /** Background thread entry point. */ @@ -303,18 +304,13 @@ static void *ioq_work(void *ptr) { return NULL; } -struct ioq *ioq_create(size_t depth, size_t threads) { - struct ioq *ioq = malloc(sizeof(*ioq)); +struct ioq *ioq_create(size_t depth, size_t nthreads) { + struct ioq *ioq = ZALLOC_FLEX(struct ioq, threads, nthreads); if (!ioq) { goto fail; } ioq->depth = depth; - ioq->size = 0; - - ioq->pending = NULL; - ioq->ready = NULL; - ioq->nthreads = 0; ioq->pending = ioqq_create(depth); if (!ioq->pending) { @@ -326,12 +322,7 @@ struct ioq *ioq_create(size_t depth, size_t threads) { goto fail; } - ioq->threads = malloc(threads * sizeof(ioq->threads[0])); - if (!ioq->threads) { - goto fail; - } - - for (size_t i = 0; i < threads; ++i) { + for (size_t i = 0; i < nthreads; ++i) { errno = pthread_create(&ioq->threads[i], NULL, ioq_work, ioq); if (errno != 0) { goto fail; @@ -354,7 +345,7 @@ int ioq_opendir(struct ioq *ioq, int dfd, const char *path, void *ptr) { return -1; } - union ioq_cmd *cmd = malloc(sizeof(*cmd)); + union ioq_cmd *cmd = ALLOC(union ioq_cmd); if (!cmd) { return -1; } @@ -412,7 +403,6 @@ void ioq_destroy(struct ioq *ioq) { abort(); } } - free(ioq->threads); ioqq_destroy(ioq->ready); ioqq_destroy(ioq->pending); diff --git a/src/ioq.h b/src/ioq.h index 9492034..0af5779 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -33,12 +33,12 @@ struct ioq_res { * * @param depth * The maximum depth of the queue. - * @param threads + * @param nthreads * The maximum number of background threads. * @return * The new I/O queue, or NULL on failure. */ -struct ioq *ioq_create(size_t depth, size_t threads); +struct ioq *ioq_create(size_t depth, size_t nthreads); /** * Asynchronous bfs_opendir(). diff --git a/src/main.c b/src/main.c index 76dde86..b7a08c1 100644 --- a/src/main.c +++ b/src/main.c @@ -20,6 +20,7 @@ * - bftw.[ch] (an extended version of nftw(3)) * * - Utilities: + * - alloc.[ch] (memory allocation) * - atomic.h (atomic operations) * - bar.[ch] (a terminal status bar) * - bit.h (bit manipulation) diff --git a/src/mtab.c b/src/mtab.c index 1d1ad94..e5c25ba 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "mtab.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "darray.h" @@ -87,15 +88,13 @@ fail: } struct bfs_mtab *bfs_mtab_parse(void) { - struct bfs_mtab *mtab = malloc(sizeof(*mtab)); + struct bfs_mtab *mtab = ZALLOC(struct bfs_mtab); if (!mtab) { return NULL; } - mtab->entries = NULL; trie_init(&mtab->names); trie_init(&mtab->types); - mtab->types_filled = false; int error = 0; diff --git a/src/parse.c b/src/parse.c index 64e08cd..cf4f696 100644 --- a/src/parse.c +++ b/src/parse.c @@ -9,6 +9,7 @@ */ #include "parse.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -55,39 +56,16 @@ static char *fake_print_arg = "-print"; static char *fake_true_arg = "-true"; struct bfs_expr *bfs_expr_new(bfs_eval_fn *eval_fn, size_t argc, char **argv) { - struct bfs_expr *expr = malloc(sizeof(*expr)); + struct bfs_expr *expr = ZALLOC(struct bfs_expr); if (!expr) { - perror("malloc()"); + perror("zalloc()"); return NULL; } expr->eval_fn = eval_fn; expr->argc = argc; expr->argv = argv; - expr->persistent_fds = 0; - expr->ephemeral_fds = 0; - expr->pure = false; - expr->always_true = false; - expr->always_false = false; - expr->cost = 0.0; expr->probability = 0.5; - expr->evaluations = 0; - expr->successes = 0; - expr->elapsed.tv_sec = 0; - expr->elapsed.tv_nsec = 0; - - // Prevent bfs_expr_free() from freeing uninitialized pointers on error paths - if (bfs_expr_is_parent(expr)) { - expr->lhs = NULL; - expr->rhs = NULL; - } else if (eval_fn == eval_exec) { - expr->exec = NULL; - } else if (eval_fn == eval_fprintf) { - expr->printf = NULL; - } else if (eval_fn == eval_regex) { - expr->regex = NULL; - } - return expr; } diff --git a/src/pwcache.c b/src/pwcache.c index f52e4e1..9f32eb0 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "pwcache.h" +#include "alloc.h" #include "config.h" #include "darray.h" #include "trie.h" @@ -71,7 +72,7 @@ struct bfs_users { }; struct bfs_users *bfs_users_new(void) { - struct bfs_users *users = malloc(sizeof(*users)); + struct bfs_users *users = ALLOC(struct bfs_users); if (!users) { return NULL; } @@ -144,7 +145,7 @@ struct bfs_groups { }; struct bfs_groups *bfs_groups_new(void) { - struct bfs_groups *groups = malloc(sizeof(*groups)); + struct bfs_groups *groups = ALLOC(struct bfs_groups); if (!groups) { return NULL; } diff --git a/src/trie.c b/src/trie.c index 8543eb1..19423cf 100644 --- a/src/trie.c +++ b/src/trie.c @@ -82,6 +82,7 @@ */ #include "trie.h" +#include "alloc.h" #include "bit.h" #include "config.h" #include "diag.h" @@ -317,7 +318,7 @@ struct trie_leaf *trie_find_prefix(const struct trie *trie, const char *key) { /** Create a new leaf, holding a copy of the given key. */ static struct trie_leaf *trie_leaf_alloc(struct trie *trie, const void *key, size_t length) { - struct trie_leaf *leaf = malloc(flex_sizeof(struct trie_leaf, key, length)); + struct trie_leaf *leaf = ALLOC_FLEX(struct trie_leaf, key, length); if (!leaf) { return NULL; } @@ -339,12 +340,10 @@ static void trie_leaf_free(struct trie *trie, struct trie_leaf *leaf) { /** Compute the size of a trie node with a certain number of children. */ static size_t trie_node_size(unsigned int size) { - // Empty nodes aren't supported - bfs_assert(size > 0); // Node size must be a power of two bfs_assert(has_single_bit(size)); - return flex_sizeof(struct trie_node, children, size); + return sizeof_flex(struct trie_node, children, size); } #if ENDIAN_NATIVE == ENDIAN_LITTLE diff --git a/src/xregex.c b/src/xregex.c index 89c2e90..ab5f793 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "xregex.h" +#include "alloc.h" #include "config.h" #include "diag.h" #include "sanity.h" @@ -115,7 +116,7 @@ static int bfs_onig_initialize(OnigEncoding *enc) { #endif int bfs_regcomp(struct bfs_regex **preg, const char *pattern, enum bfs_regex_type type, enum bfs_regcomp_flags flags) { - struct bfs_regex *regex = *preg = malloc(sizeof(*regex)); + struct bfs_regex *regex = *preg = ALLOC(struct bfs_regex); if (!regex) { return -1; } diff --git a/src/xspawn.c b/src/xspawn.c index 740e38e..2cabdcc 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "xspawn.h" +#include "alloc.h" #include "bfstd.h" #include "config.h" #include "list.h" @@ -62,7 +63,7 @@ int bfs_spawn_setflags(struct bfs_spawn *ctx, enum bfs_spawn_flags flags) { /** Add a spawn action to the chain. */ static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_spawn_op op) { - struct bfs_spawn_action *action = malloc(sizeof(*action)); + struct bfs_spawn_action *action = ALLOC(struct bfs_spawn_action); if (!action) { return NULL; } diff --git a/tests/alloc.c b/tests/alloc.c new file mode 100644 index 0000000..91b1b43 --- /dev/null +++ b/tests/alloc.c @@ -0,0 +1,24 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "../src/alloc.h" +#include "../src/diag.h" +#include + +int main(void) { + // Check sizeof_flex() + struct flexible { + alignas(64) int foo; + int bar[]; + }; + bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); + bfs_verify(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); + bfs_verify(sizeof_flex(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) + == align_floor(alignof(struct flexible), SIZE_MAX)); + + // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) + // Doesn't happen in typical ABIs + bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + + return EXIT_SUCCESS; +} diff --git a/tests/bfstd.c b/tests/bfstd.c index 7fea9b5..fa854a8 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -24,17 +24,6 @@ static void check_base_dir(const char *path, const char *dir, const char *base) } int main(void) { - // Check flex_sizeof() - struct flexible { - alignas(64) int foo; - int bar[]; - }; - bfs_verify(flex_sizeof(struct flexible, bar, 0) >= sizeof(struct flexible)); - bfs_verify(flex_sizeof(struct flexible, bar, 16) % alignof(struct flexible) == 0); - bfs_verify(flex_sizeof(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) - == align_floor(alignof(struct flexible), SIZE_MAX)); - bfs_verify(flex_sizeof_impl(8, 16, 4, 4, 1) == 16); - // From man 3p basename check_base_dir("usr", ".", "usr"); check_base_dir("usr/", ".", "usr"); @@ -46,4 +35,6 @@ int main(void) { check_base_dir("/usr/lib", "/usr", "lib"); check_base_dir("//usr//lib//", "//usr", "lib"); check_base_dir("/home//dwc//test", "/home//dwc", "test"); + + return EXIT_SUCCESS; } -- cgit v1.2.3 From cbe2c473c9aee71f5f770dc7f41973d2ef4c273c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 19 Jun 2023 16:58:24 -0400 Subject: alloc: Implement an arena for flexible structs --- src/alloc.c | 118 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/alloc.h | 92 +++++++++++++++++++++++++++++++++++++++++++++ tests/alloc.c | 14 ++++++- 3 files changed, 223 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/alloc.c b/src/alloc.c index b437975..a6910ce 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -157,5 +157,123 @@ void arena_destroy(struct arena *arena) { for (size_t i = 0; i < arena->nslabs; ++i) { free(arena->slabs[i]); } + free(arena->slabs); sanitize_uninit(arena); } + +void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, size_t size) { + varena->align = align; + varena->offset = offset; + varena->size = size; + varena->narenas = 0; + varena->arenas = NULL; + + // The smallest size class is at least as many as fit in the smallest + // aligned allocation size + size_t min_count = (flex_size(align, min, offset, size, 1) - offset + size - 1) / size; + varena->shift = bit_width(min_count - 1); +} + +/** Get the size class for the given array length. */ +static size_t varena_size_class(struct varena *varena, size_t count) { + // Since powers of two are common array lengths, make them the + // (inclusive) upper bound for each size class + return bit_width((count - !!count) >> varena->shift); +} + +/** Get the exact size of a flexible struct. */ +static size_t varena_exact_size(const struct varena *varena, size_t count) { + return flex_size(varena->align, 0, varena->offset, varena->size, count); +} + +/** Get the arena for the given array length. */ +static struct arena *varena_get(struct varena *varena, size_t count) { + size_t i = varena_size_class(varena, count); + + if (i >= varena->narenas) { + size_t narenas = i + 1; + struct arena *arenas = realloc(varena->arenas, sizeof_array(struct arena, narenas)); + if (!arenas) { + return NULL; + } + + for (size_t j = varena->narenas; j < narenas; ++j) { + size_t shift = j + varena->shift; + size_t size = varena_exact_size(varena, (size_t)1 << shift); + arena_init(&arenas[j], varena->align, size); + } + + varena->narenas = narenas; + varena->arenas = arenas; + } + + return &varena->arenas[i]; +} + +void *varena_alloc(struct varena *varena, size_t count) { + struct arena *arena = varena_get(varena, count); + if (!arena) { + return NULL; + } + + void *ret = arena_alloc(arena); + if (!ret) { + return NULL; + } + + // Tell the sanitizers the exact size of the allocated struct + sanitize_free(ret, arena->size); + sanitize_alloc(ret, varena_exact_size(varena, count)); + + return ret; +} + +void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t new_count) { + struct arena *new_arena = varena_get(varena, new_count); + struct arena *old_arena = varena_get(varena, old_count); + if (!new_arena) { + return NULL; + } + + size_t new_exact_size = varena_exact_size(varena, new_count); + size_t old_exact_size = varena_exact_size(varena, old_count); + + if (new_arena == old_arena) { + if (new_count < old_count) { + sanitize_free((char *)ptr + new_exact_size, old_exact_size - new_exact_size); + } else if (new_count > old_count) { + sanitize_alloc((char *)ptr + old_exact_size, new_exact_size - old_exact_size); + } + return ptr; + } + + void *ret = arena_alloc(new_arena); + if (!ret) { + return NULL; + } + + size_t old_size = old_arena->size; + sanitize_alloc((char *)ptr + old_exact_size, old_size - old_exact_size); + + size_t new_size = new_arena->size; + size_t min_size = new_size < old_size ? new_size : old_size; + memcpy(ret, ptr, min_size); + + arena_free(old_arena, ptr); + sanitize_free((char *)ret + new_exact_size, new_size - new_exact_size); + + return ret; +} + +void varena_free(struct varena *varena, void *ptr, size_t count) { + struct arena *arena = varena_get(varena, count); + arena_free(arena, ptr); +} + +void varena_destroy(struct varena *varena) { + for (size_t i = 0; i < varena->narenas; ++i) { + arena_destroy(&varena->arenas[i]); + } + free(varena->arenas); + sanitize_uninit(varena); +} diff --git a/src/alloc.h b/src/alloc.h index 65edb92..c2ea09b 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -190,4 +190,96 @@ void arena_free(struct arena *arena, void *ptr); */ void arena_destroy(struct arena *arena); +/** + * An arena allocator for flexibly-sized types. + */ +struct varena { + /** The alignment of the struct. */ + size_t align; + /** The offset of the flexible array. */ + size_t offset; + /** The size of the flexible array elements. */ + size_t size; + /** Shift amount for the smallest size class. */ + size_t shift; + /** The number of arenas of different sizes. */ + size_t narenas; + /** The array of differently-sized arenas. */ + struct arena *arenas; +}; + +/** + * Initialize a varena for a struct with the given layout. + * + * @param varena + * The varena to initialize. + * @param align + * alignof(type) + * @param min + * sizeof(type) + * @param offset + * offsetof(type, flexible_array) + * @param size + * sizeof(flexible_array[i]) + */ +void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, size_t size); + +/** + * Initialize a varena for the given type and flexible array. + * + * @param varena + * The varena to initialize. + * @param type + * A struct type containing a flexible array. + * @param member + * The name of the flexible array member. + */ +#define VARENA_INIT(varena, type, member) \ + varena_init(varena, alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0])) + +/** + * Arena-allocate a flexible struct. + * + * @param varena + * The varena to allocate from. + * @param count + * The length of the flexible array. + * @return + * The allocated struct, or NULL on failure. + */ +void *varena_alloc(struct varena *varena, size_t count); + +/** + * Resize a flexible struct. + * + * @param varena + * The varena to allocate from. + * @param ptr + * The object to resize. + * @param old_count + * The old array lenth. + * @param new_count + * The new array length. + * @return + * The resized struct, or NULL on failure. + */ +void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t new_count); + +/** + * Free an arena-allocated flexible struct. + * + * @param varena + * The that allocated the object. + * @param ptr + * The object to free. + * @param count + * The length of the flexible array. + */ +void varena_free(struct varena *varena, void *ptr, size_t count); + +/** + * Destroy a varena, freeing all allocations. + */ +void varena_destroy(struct varena *varena); + #endif // BFS_ALLOC_H diff --git a/tests/alloc.c b/tests/alloc.c index 91b1b43..9e6e892 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -8,7 +8,7 @@ int main(void) { // Check sizeof_flex() struct flexible { - alignas(64) int foo; + alignas(64) int foo[8]; int bar[]; }; bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); @@ -20,5 +20,17 @@ int main(void) { // Doesn't happen in typical ABIs bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + // varena tests + struct varena varena; + VARENA_INIT(&varena, struct flexible, bar); + + for (size_t i = 0; i < 256; ++i) { + bfs_verify(varena_alloc(&varena, i)); + struct arena *arena = &varena.arenas[varena.narenas - 1]; + bfs_verify(arena->size >= sizeof_flex(struct flexible, bar, i)); + } + + varena_destroy(&varena); + return EXIT_SUCCESS; } -- cgit v1.2.3 From 93749993508eaa9035dbcb005f960bc0b64752f0 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 22 Jun 2023 17:02:31 -0400 Subject: tests/gnu/printf_times: Correct %T@ to %A@ --- tests/gnu/printf_times.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/gnu/printf_times.sh b/tests/gnu/printf_times.sh index d952620..e4f5155 100644 --- a/tests/gnu/printf_times.sh +++ b/tests/gnu/printf_times.sh @@ -1 +1 @@ -bfs_diff times -type f -printf '%p | %a %AY-%Am-%Ad %AH:%AI:%AS %T@ | %t %TY-%Tm-%Td %TH:%TI:%TS %T@\n' +bfs_diff times -type f -printf '%p | %a %AY-%Am-%Ad %AH:%AI:%AS %A@ | %t %TY-%Tm-%Td %TH:%TI:%TS %T@\n' -- cgit v1.2.3 From d9bcbaf71b0173fe44d7e07c05d110c49c07b6f2 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 27 Jun 2023 11:52:24 -0400 Subject: tests: Get more tests passing with --bfs=tests/find-color.sh --- tests/ls-color.sh | 37 ++++++++++++++++++++++++++++++++----- 1 file changed, 32 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/ls-color.sh b/tests/ls-color.sh index 6d33f53..9fdd59c 100755 --- a/tests/ls-color.sh +++ b/tests/ls-color.sh @@ -7,17 +7,44 @@ set -e +function parse_ls_colors() { + for key; do + local -n var="$key" + if [[ "$LS_COLORS" =~ (^|:)$key=(([^:]|\\:)*) ]]; then + var="${BASH_REMATCH[2]}" + # Interpret escapes + var=$(printf "$var" | sed $'s/\^\[/\033/g; s/\\\\:/:/g') + fi + done +} + +function re_escape() { + # https://stackoverflow.com/a/29613573/502399 + sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$1" +} + +rs=0 +lc=$'\033[' +rc=m +ec= +no= + +parse_ls_colors rs lc rc ec no +: "${ec:=$lc$rs$rc}" + +strip="(($(re_escape "$lc$no$rc"))?($(re_escape "$ec")|$(re_escape "$lc$rc")))+" + +function ls_color() { + # Strip the leading reset sequence from the ls output + ls -1d --color "$@" | sed -E "s/^$strip([a-z].*)$strip/\4/; s/^$strip//" +} + L= if [ "$1" = "-L" ]; then L="$1" shift fi -function ls_color() { - # Strip the leading reset sequence from the ls output - ls -1d --color "$@" | sed $'s/^\033\\[0m//' -} - DIR="${1%/*}" if [ "$DIR" = "$1" ]; then ls_color "$1" -- cgit v1.2.3 From ec50b98d5584b0bb291a463a0c39905ce05acfe7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 27 Jun 2023 12:07:14 -0400 Subject: tests/bfs/color: Add mixed-case extensions --- tests/bfs/color.out | 11 ++++++++--- tests/bfs/color_L.out | 11 ++++++++--- tests/bfs/color_L_ln_target.out | 11 ++++++++--- tests/bfs/color_L_no_stat.out | 11 ++++++++--- tests/bfs/color_cd0_no.out | 11 ++++++++--- tests/bfs/color_escapes.out | 11 ++++++++--- tests/bfs/color_ext.out | 11 ++++++++--- tests/bfs/color_ext0.out | 11 ++++++++--- tests/bfs/color_ext_override.out | 11 ++++++++--- tests/bfs/color_ext_underride.out | 11 ++++++++--- tests/bfs/color_fi0_no.out | 11 ++++++++--- tests/bfs/color_fi_no.out | 11 ++++++++--- tests/bfs/color_ln_target.out | 11 ++++++++--- tests/bfs/color_mh.out | 11 ++++++++--- tests/bfs/color_mh0.out | 11 ++++++++--- tests/bfs/color_mi.out | 11 ++++++++--- tests/bfs/color_missing_colon.out | 11 ++++++++--- tests/bfs/color_no.out | 11 ++++++++--- tests/bfs/color_no_stat.out | 11 ++++++++--- tests/bfs/color_or.out | 11 ++++++++--- tests/bfs/color_or0_mi.out | 11 ++++++++--- tests/bfs/color_or0_mi0.out | 11 ++++++++--- tests/bfs/color_or_mi.out | 11 ++++++++--- tests/bfs/color_or_mi0.out | 11 ++++++++--- tests/bfs/color_rs_lc_rc_ec.out | 11 ++++++++--- tests/bfs/color_st0_tw0_ow.out | 11 ++++++++--- tests/bfs/color_st0_tw0_ow0.out | 11 ++++++++--- tests/bfs/color_st0_tw_ow.out | 11 ++++++++--- tests/bfs/color_st0_tw_ow0.out | 11 ++++++++--- tests/bfs/color_st_tw0_ow.out | 11 ++++++++--- tests/bfs/color_st_tw0_ow0.out | 11 ++++++++--- tests/bfs/color_st_tw_ow0.out | 11 ++++++++--- tests/bfs/color_star.out | 11 ++++++++--- tests/bfs/color_su0_sg.out | 11 ++++++++--- tests/bfs/color_su0_sg0.out | 11 ++++++++--- tests/bfs/color_su_sg0.out | 11 ++++++++--- tests/bfs/nocolor.out | 11 ++++++++--- tests/bfs/printf_color.out | 11 ++++++++--- tests/gnu/empty_special.out | 11 ++++++++--- tests/tests.sh | 4 +++- 40 files changed, 315 insertions(+), 118 deletions(-) (limited to 'tests') diff --git a/tests/bfs/color.out b/tests/bfs/color.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color.out +++ b/tests/bfs/color.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_L.out b/tests/bfs/color_L.out index b60dd4a..89f9410 100644 --- a/tests/bfs/color_L.out +++ b/tests/bfs/color_L.out @@ -13,8 +13,13 @@ rainbow/file.dat rainbow/file.txt rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_L_ln_target.out b/tests/bfs/color_L_ln_target.out index cd4ec5e..2562c98 100644 --- a/tests/bfs/color_L_ln_target.out +++ b/tests/bfs/color_L_ln_target.out @@ -13,8 +13,13 @@ rainbow/file.dat rainbow/file.txt rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_L_no_stat.out b/tests/bfs/color_L_no_stat.out index c0bb1be..4fe99c0 100644 --- a/tests/bfs/color_L_no_stat.out +++ b/tests/bfs/color_L_no_stat.out @@ -10,11 +10,16 @@ rainbow/pipe rainbow/exec.sh rainbow/file.dat +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/sgid -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/sugid rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_cd0_no.out b/tests/bfs/color_cd0_no.out index d5c98a9..30ad97f 100644 --- a/tests/bfs/color_cd0_no.out +++ b/tests/bfs/color_cd0_no.out @@ -5,11 +5,16 @@ rainbow/file.dat rainbow/file.txt rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ rainbow/sticky_ow rainbow/sgid rainbow/pipe diff --git a/tests/bfs/color_escapes.out b/tests/bfs/color_escapes.out index b71e138..808585e 100644 --- a/tests/bfs/color_escapes.out +++ b/tests/bfs/color_escapes.out @@ -13,8 +13,13 @@ :rainbow/:sticky :rainbow/file.dat :rainbow/file.txt +:rainbow/lower.gz +:rainbow/lower.tar +:rainbow/lower.tar.gz +:rainbow/lu.tar.GZ :rainbow/mh1 :rainbow/mh2 -:rainbow/star.gz -:rainbow/star.tar -:rainbow/star.tar.gz +:rainbow/ul.TAR.gz +:rainbow/upper.GZ +:rainbow/upper.TAR +:rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_ext.out b/tests/bfs/color_ext.out index cf26e73..be22b82 100644 --- a/tests/bfs/color_ext.out +++ b/tests/bfs/color_ext.out @@ -13,8 +13,13 @@ rainbow/suid rainbow/sticky rainbow/file.dat +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_ext0.out b/tests/bfs/color_ext0.out index e764a6b..d151319 100644 --- a/tests/bfs/color_ext0.out +++ b/tests/bfs/color_ext0.out @@ -13,8 +13,13 @@ rainbow/suid rainbow/sticky rainbow/file.dat +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_ext_override.out b/tests/bfs/color_ext_override.out index 1377b65..3a09cd2 100644 --- a/tests/bfs/color_ext_override.out +++ b/tests/bfs/color_ext_override.out @@ -1,8 +1,13 @@ rainbow rainbow/exec.sh -rainbow/star.tar -rainbow/star.gz -rainbow/star.tar.gz +rainbow/lower.tar +rainbow/upper.TAR +rainbow/lower.gz +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR.GZ rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext_underride.out b/tests/bfs/color_ext_underride.out index 787248a..073d176 100644 --- a/tests/bfs/color_ext_underride.out +++ b/tests/bfs/color_ext_underride.out @@ -1,8 +1,13 @@ rainbow -rainbow/star.tar.gz +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/ul.TAR.gz +rainbow/upper.TAR.GZ rainbow/exec.sh -rainbow/star.tar -rainbow/star.gz +rainbow/lower.tar +rainbow/upper.TAR +rainbow/lower.gz +rainbow/upper.GZ rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_fi0_no.out b/tests/bfs/color_fi0_no.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color_fi0_no.out +++ b/tests/bfs/color_fi0_no.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_fi_no.out b/tests/bfs/color_fi_no.out index 7162ded..8df9355 100644 --- a/tests/bfs/color_fi_no.out +++ b/tests/bfs/color_fi_no.out @@ -6,11 +6,16 @@ rainbow/link.txt rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ rainbow/sticky_ow rainbow/sgid rainbow/pipe diff --git a/tests/bfs/color_ln_target.out b/tests/bfs/color_ln_target.out index cd4ec5e..2562c98 100644 --- a/tests/bfs/color_ln_target.out +++ b/tests/bfs/color_ln_target.out @@ -13,8 +13,13 @@ rainbow/file.dat rainbow/file.txt rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_mh.out b/tests/bfs/color_mh.out index 757a6a1..7521b31 100644 --- a/tests/bfs/color_mh.out +++ b/tests/bfs/color_mh.out @@ -15,6 +15,11 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_mh0.out b/tests/bfs/color_mh0.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color_mh0.out +++ b/tests/bfs/color_mh0.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_mi.out b/tests/bfs/color_mi.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color_mi.out +++ b/tests/bfs/color_mi.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_missing_colon.out b/tests/bfs/color_missing_colon.out index cf26e73..be22b82 100644 --- a/tests/bfs/color_missing_colon.out +++ b/tests/bfs/color_missing_colon.out @@ -13,8 +13,13 @@ rainbow/suid rainbow/sticky rainbow/file.dat +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_no.out b/tests/bfs/color_no.out index 9f0dd66..b70e47d 100644 --- a/tests/bfs/color_no.out +++ b/tests/bfs/color_no.out @@ -6,11 +6,16 @@ rainbow/link.txt rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ rainbow/sticky_ow rainbow/sgid rainbow/pipe diff --git a/tests/bfs/color_no_stat.out b/tests/bfs/color_no_stat.out index 1fc5324..7d7d767 100644 --- a/tests/bfs/color_no_stat.out +++ b/tests/bfs/color_no_stat.out @@ -10,11 +10,16 @@ rainbow/pipe rainbow/exec.sh rainbow/file.dat +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/sgid -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/sugid rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_or.out b/tests/bfs/color_or.out index 9e1fe5c..98efb63 100644 --- a/tests/bfs/color_or.out +++ b/tests/bfs/color_or.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_or0_mi.out b/tests/bfs/color_or0_mi.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color_or0_mi.out +++ b/tests/bfs/color_or0_mi.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_or0_mi0.out b/tests/bfs/color_or0_mi0.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color_or0_mi0.out +++ b/tests/bfs/color_or0_mi0.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_or_mi.out b/tests/bfs/color_or_mi.out index 5667f56..a9dc229 100644 --- a/tests/bfs/color_or_mi.out +++ b/tests/bfs/color_or_mi.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_or_mi0.out b/tests/bfs/color_or_mi0.out index 5667f56..a9dc229 100644 --- a/tests/bfs/color_or_mi0.out +++ b/tests/bfs/color_or_mi0.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_rs_lc_rc_ec.out b/tests/bfs/color_rs_lc_rc_ec.out index d39bbe7..82d94ec 100644 --- a/tests/bfs/color_rs_lc_rc_ec.out +++ b/tests/bfs/color_rs_lc_rc_ec.out @@ -12,9 +12,14 @@ LC01;34RCrainbow/ECLC37;41RCsuidEC LC01;34RCrainbow/ECLC37;44RCstickyEC LC01;34RCrainbow/ECfile.dat LC01;34RCrainbow/ECfile.txt +LC01;34RCrainbow/EClower.gz +LC01;34RCrainbow/EClower.tar +LC01;34RCrainbow/EClower.tar.gz +LC01;34RCrainbow/EClu.tar.GZ LC01;34RCrainbow/ECmh1 LC01;34RCrainbow/ECmh2 -LC01;34RCrainbow/ECstar.gz -LC01;34RCrainbow/ECstar.tar -LC01;34RCrainbow/ECstar.tar.gz +LC01;34RCrainbow/ECul.TAR.gz +LC01;34RCrainbow/ECupper.GZ +LC01;34RCrainbow/ECupper.TAR +LC01;34RCrainbow/ECupper.TAR.GZ LC01;34RCrainbowEC diff --git a/tests/bfs/color_st0_tw0_ow.out b/tests/bfs/color_st0_tw0_ow.out index 9a47ef2..bdc5942 100644 --- a/tests/bfs/color_st0_tw0_ow.out +++ b/tests/bfs/color_st0_tw0_ow.out @@ -13,8 +13,13 @@ rainbow/suid rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_st0_tw0_ow0.out b/tests/bfs/color_st0_tw0_ow0.out index 2b86fe4..f13b7f3 100644 --- a/tests/bfs/color_st0_tw0_ow0.out +++ b/tests/bfs/color_st0_tw0_ow0.out @@ -13,8 +13,13 @@ rainbow/suid rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_st0_tw_ow.out b/tests/bfs/color_st0_tw_ow.out index 42549a1..2d7f682 100644 --- a/tests/bfs/color_st0_tw_ow.out +++ b/tests/bfs/color_st0_tw_ow.out @@ -13,8 +13,13 @@ rainbow/sticky_ow rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_st0_tw_ow0.out b/tests/bfs/color_st0_tw_ow0.out index 535b8ae..7e343b8 100644 --- a/tests/bfs/color_st0_tw_ow0.out +++ b/tests/bfs/color_st0_tw_ow0.out @@ -13,8 +13,13 @@ rainbow/sticky_ow rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_st_tw0_ow.out b/tests/bfs/color_st_tw0_ow.out index c9a86f4..c61a327 100644 --- a/tests/bfs/color_st_tw0_ow.out +++ b/tests/bfs/color_st_tw0_ow.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_st_tw0_ow0.out b/tests/bfs/color_st_tw0_ow0.out index 2d94f3a..929a993 100644 --- a/tests/bfs/color_st_tw0_ow0.out +++ b/tests/bfs/color_st_tw0_ow0.out @@ -13,8 +13,13 @@ rainbow/sticky_ow rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_st_tw_ow0.out b/tests/bfs/color_st_tw_ow0.out index 317ef90..7092f5a 100644 --- a/tests/bfs/color_st_tw_ow0.out +++ b/tests/bfs/color_st_tw_ow0.out @@ -13,8 +13,13 @@ rainbow/sticky_ow rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_star.out b/tests/bfs/color_star.out index 77fc8a8..34c7153 100644 --- a/tests/bfs/color_star.out +++ b/tests/bfs/color_star.out @@ -13,8 +13,13 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_su0_sg.out b/tests/bfs/color_su0_sg.out index 8b8c8b8..2bce534 100644 --- a/tests/bfs/color_su0_sg.out +++ b/tests/bfs/color_su0_sg.out @@ -12,9 +12,14 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_su0_sg0.out b/tests/bfs/color_su0_sg0.out index 0cd5f9a..0c3d757 100644 --- a/tests/bfs/color_su0_sg0.out +++ b/tests/bfs/color_su0_sg0.out @@ -10,11 +10,16 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/sgid -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/sugid rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_su_sg0.out b/tests/bfs/color_su_sg0.out index a9e8c5d..7a70598 100644 --- a/tests/bfs/color_su_sg0.out +++ b/tests/bfs/color_su_sg0.out @@ -12,9 +12,14 @@ rainbow/sticky rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/sgid -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/nocolor.out b/tests/bfs/nocolor.out index b53fe03..29e7de8 100644 --- a/tests/bfs/nocolor.out +++ b/tests/bfs/nocolor.out @@ -5,16 +5,21 @@ rainbow/exec.sh rainbow/file.dat rainbow/file.txt rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/ow rainbow/pipe rainbow/sgid rainbow/socket -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/sticky rainbow/sticky_ow rainbow/sugid rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/printf_color.out b/tests/bfs/printf_color.out index d9cd1a4..0468f7d 100644 --- a/tests/bfs/printf_color.out +++ b/tests/bfs/printf_color.out @@ -13,8 +13,13 @@ . ./rainbow sticky ./rainbow/sticky rainbow/sticky . ./rainbow file.dat ./rainbow/file.dat rainbow/file.dat . ./rainbow file.txt ./rainbow/file.txt rainbow/file.txt +. ./rainbow lower.gz ./rainbow/lower.gz rainbow/lower.gz +. ./rainbow lower.tar ./rainbow/lower.tar rainbow/lower.tar +. ./rainbow lower.tar.gz ./rainbow/lower.tar.gz rainbow/lower.tar.gz +. ./rainbow lu.tar.GZ ./rainbow/lu.tar.GZ rainbow/lu.tar.GZ . ./rainbow mh1 ./rainbow/mh1 rainbow/mh1 . ./rainbow mh2 ./rainbow/mh2 rainbow/mh2 -. ./rainbow star.gz ./rainbow/star.gz rainbow/star.gz -. ./rainbow star.tar ./rainbow/star.tar rainbow/star.tar -. ./rainbow star.tar.gz ./rainbow/star.tar.gz rainbow/star.tar.gz +. ./rainbow ul.TAR.gz ./rainbow/ul.TAR.gz rainbow/ul.TAR.gz +. ./rainbow upper.GZ ./rainbow/upper.GZ rainbow/upper.GZ +. ./rainbow upper.TAR ./rainbow/upper.TAR rainbow/upper.TAR +. ./rainbow upper.TAR.GZ ./rainbow/upper.TAR.GZ rainbow/upper.TAR.GZ diff --git a/tests/gnu/empty_special.out b/tests/gnu/empty_special.out index 3927f2b..3aa57d2 100644 --- a/tests/gnu/empty_special.out +++ b/tests/gnu/empty_special.out @@ -1,14 +1,19 @@ rainbow/exec.sh rainbow/file.dat rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ rainbow/mh1 rainbow/mh2 rainbow/ow rainbow/sgid -rainbow/star.gz -rainbow/star.tar -rainbow/star.tar.gz rainbow/sticky rainbow/sticky_ow rainbow/sugid rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/tests.sh b/tests/tests.sh index 9265481..46e3e33 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -413,7 +413,9 @@ make_deep "$TMP/deep" function make_rainbow() { "$XTOUCH" -p "$1/file.txt" "$XTOUCH" -p "$1/file.dat" - "$XTOUCH" -p "$1/star".{gz,tar,tar.gz} + "$XTOUCH" -p "$1/lower".{gz,tar,tar.gz} + "$XTOUCH" -p "$1/upper".{GZ,TAR,TAR.GZ} + "$XTOUCH" -p "$1/lu.tar.GZ" "$1/ul.TAR.gz" ln -s file.txt "$1/link.txt" "$XTOUCH" -p "$1/mh1" ln "$1/mh1" "$1/mh2" -- cgit v1.2.3 From 27dc7a126d6c00b7a41e0559254928555200ee42 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 29 Jun 2023 15:17:27 -0400 Subject: color: Implement smart casing Since coreutils 9.2, ls does case-sensitive extension matching if the same extension is capitalized differently in $LS_COLORS. Implement the same logic. Link: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=33123 Link: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=9086 --- src/color.c | 591 +++++++++++++++++++++++++++---------------- tests/bfs/color_ext_case.out | 25 ++ tests/bfs/color_ext_case.sh | 1 + tests/bfs/color_nul.out | Bin 20 -> 908 bytes tests/bfs/color_nul.sh | 3 +- tests/bfs/color_star.out | 25 -- tests/bfs/color_star.sh | 2 +- 7 files changed, 401 insertions(+), 246 deletions(-) create mode 100644 tests/bfs/color_ext_case.out create mode 100644 tests/bfs/color_ext_case.sh delete mode 100644 tests/bfs/color_star.out (limited to 'tests') diff --git a/src/color.c b/src/color.c index b54ad53..82ff763 100644 --- a/src/color.c +++ b/src/color.c @@ -22,69 +22,122 @@ #include #include +/** + * An escape sequence, which may contain embedded NUL bytes. + */ +struct esc_seq { + /** The length of the escape sequence. */ + size_t len; + /** The escape sequence iteself, without a terminating NUL. */ + char seq[]; +}; + +/** + * A colored file extension, like `*.tar=01;31`. + */ +struct ext_color { + /** Priority, to disambiguate case-sensitive and insensitive matches. */ + size_t priority; + /** The escape sequence associated with this extension. */ + struct esc_seq *esc; + /** The length of the extension to match. */ + size_t len; + /** Whether the comparison should be case-insensitive. */ + bool icase; + /** The extension to match (NUL-terminated). */ + char ext[]; +}; + struct colors { - char *reset; - char *leftcode; - char *rightcode; - char *endcode; - char *clear_to_eol; - - char *bold; - char *gray; - char *red; - char *green; - char *yellow; - char *blue; - char *magenta; - char *cyan; - char *white; - - char *warning; - char *error; - - char *normal; - - char *file; - char *multi_hard; - char *executable; - char *capable; - char *setgid; - char *setuid; - - char *directory; - char *sticky; - char *other_writable; - char *sticky_other_writable; - - char *link; - char *orphan; - char *missing; + /** esc_seq allocator. */ + struct varena esc_arena; + /** ext_color allocator. */ + struct varena ext_arena; + + // Known dircolors keys + + struct esc_seq *reset; + struct esc_seq *leftcode; + struct esc_seq *rightcode; + struct esc_seq *endcode; + struct esc_seq *clear_to_eol; + + struct esc_seq *bold; + struct esc_seq *gray; + struct esc_seq *red; + struct esc_seq *green; + struct esc_seq *yellow; + struct esc_seq *blue; + struct esc_seq *magenta; + struct esc_seq *cyan; + struct esc_seq *white; + + struct esc_seq *warning; + struct esc_seq *error; + + struct esc_seq *normal; + + struct esc_seq *file; + struct esc_seq *multi_hard; + struct esc_seq *executable; + struct esc_seq *capable; + struct esc_seq *setgid; + struct esc_seq *setuid; + + struct esc_seq *directory; + struct esc_seq *sticky; + struct esc_seq *other_writable; + struct esc_seq *sticky_other_writable; + + struct esc_seq *link; + struct esc_seq *orphan; + struct esc_seq *missing; bool link_as_target; - char *blockdev; - char *chardev; - char *door; - char *pipe; - char *socket; + struct esc_seq *blockdev; + struct esc_seq *chardev; + struct esc_seq *door; + struct esc_seq *pipe; + struct esc_seq *socket; /** A mapping from color names (fi, di, ln, etc.) to struct fields. */ struct trie names; - /** A mapping from file extensions to colors. */ - struct trie ext_colors; + /** Number of extensions. */ + size_t ext_count; + /** Case-sensitive extension trie. */ + struct trie ext_trie; + /** Case-insensitive extension trie. */ + struct trie iext_trie; }; +/** Allocate an escape sequence. */ +static struct esc_seq *new_esc(struct colors *colors, const char *seq, size_t len) { + struct esc_seq *esc = varena_alloc(&colors->esc_arena, len); + if (esc) { + esc->len = len; + memcpy(esc->seq, seq, len); + } + return esc; +} + +/** Free an escape sequence. */ +static void free_esc(struct colors *colors, struct esc_seq *seq) { + varena_free(&colors->esc_arena, seq, seq->len); +} + /** Initialize a color in the table. */ -static int init_color(struct colors *colors, const char *name, const char *value, char **field) { +static int init_esc(struct colors *colors, const char *name, const char *value, struct esc_seq **field) { + struct esc_seq *esc = NULL; if (value) { - *field = dstrdup(value); - if (!*field) { + esc = new_esc(colors, value, strlen(value)); + if (!esc) { return -1; } - } else { - *field = NULL; } + *field = esc; + struct trie_leaf *leaf = trie_insert_str(&colors->names, name); if (leaf) { leaf->value = field; @@ -94,100 +147,182 @@ static int init_color(struct colors *colors, const char *name, const char *value } } -/** Get a color from the table. */ -static char **get_color(const struct colors *colors, const char *name) { +/** Get an escape sequence from the table. */ +static struct esc_seq **get_esc(const struct colors *colors, const char *name) { const struct trie_leaf *leaf = trie_find_str(&colors->names, name); - if (leaf) { - return (char **)leaf->value; - } else { - return NULL; - } + return leaf ? leaf->value : NULL; } -/** Set the value of a color. */ -static int set_color(struct colors *colors, const char *name, char *value) { - char **color = get_color(colors, name); - if (color) { - dstrfree(*color); - *color = value; +/** Set a named escape sequence. */ +static int set_esc(struct colors *colors, const char *name, char *value) { + struct esc_seq **field = get_esc(colors, name); + if (!field) { return 0; - } else { - return -1; } + + if (*field) { + free_esc(colors, *field); + *field = NULL; + } + + if (value) { + *field = new_esc(colors, value, dstrlen(value)); + if (!*field) { + return -1; + } + } + + return 0; } -/** - * Transform a file extension for fast lookups, by reversing and lowercasing it. - */ -static void extxfrm(char *ext, size_t len) { - for (size_t i = 0; i < len - i; ++i) { - char a = ext[i]; - char b = ext[len - i - 1]; +/** Reverse a string, to turn suffix matches into prefix matches. */ +static void ext_reverse(char *ext, size_t len) { + for (size_t i = 0, j = len - 1; len && i < j; ++i, --j) { + char c = ext[i]; + ext[i] = ext[j]; + ext[j] = c; + } +} + +/** Convert a string to lowercase for case-insensitive matching. */ +static void ext_tolower(char *ext, size_t len) { + for (size_t i = 0; i < len; ++i) { + char c = ext[i]; // What's internationalization? Doesn't matter, this is what // GNU ls does. Luckily, since there's no standard C way to // casefold. Not using tolower() here since it respects the // current locale, which GNU ls doesn't do. - if (a >= 'A' && a <= 'Z') { - a += 'a' - 'A'; - } - if (b >= 'A' && b <= 'Z') { - b += 'a' - 'A'; + if (c >= 'A' && c <= 'Z') { + c += 'a' - 'A'; } - ext[i] = b; - ext[len - i - 1] = a; + ext[i] = c; } } /** Maximum supported extension length. */ #define EXT_MAX 255 -/** - * Set the color for an extension. - */ -static int set_ext_color(struct colors *colors, char *key, char *value) { +/** Set the color for an extension. */ +static int set_ext(struct colors *colors, char *key, char *value) { size_t len = dstrlen(key); - if (len > EXT_MAX) { + struct ext_color *ext = varena_alloc(&colors->ext_arena, len + 1); + if (!ext) { return -1; } - extxfrm(key, len); + ext->priority = colors->ext_count++; + ext->len = len; + ext->icase = true; + ext->esc = new_esc(colors, value, dstrlen(value)); + if (!ext->esc) { + goto fail; + } + + key = memcpy(ext->ext, key, len + 1); + + // Reverse the extension (`*.y.x` -> `x.y.*`) so we can use trie_find_prefix() + ext_reverse(key, len); + + // Find any pre-existing exact match + struct ext_color *prev = NULL; + struct trie_leaf *leaf = trie_find_str(&colors->ext_trie, key); + if (leaf) { + prev = leaf->value; + trie_remove(&colors->ext_trie, leaf); + } // A later *.x should override any earlier *.x, *.y.x, etc. - struct trie_leaf *match; - while ((match = trie_find_postfix(&colors->ext_colors, key))) { - dstrfree(match->value); - trie_remove(&colors->ext_colors, match); + while ((leaf = trie_find_postfix(&colors->ext_trie, key))) { + trie_remove(&colors->ext_trie, leaf); } - struct trie_leaf *leaf = trie_insert_str(&colors->ext_colors, key); - if (leaf) { - leaf->value = value; - return 0; - } else { - return -1; + // Insert the extension into the case-sensitive trie + leaf = trie_insert_str(&colors->ext_trie, key); + if (!leaf) { + goto fail; } + leaf->value = ext; + + // "Smart case": if the same extension is given with two different + // capitalizations (e.g. `*.y.x=31:*.Y.Z=32:`), make it case-sensitive + ext_tolower(key, len); + leaf = trie_insert_str(&colors->iext_trie, key); + if (!leaf) { + goto fail; + } + + // If a match for the lowercased extension exists and is different from + // the exact match, or is already case-sensitive, mark this one too + struct ext_color *iprev = leaf->value; + if (iprev && (iprev != prev || !iprev->icase)) { + iprev->icase = false; + ext->icase = false; + } + leaf->value = ext; + + return 0; + +fail: + if (ext->esc) { + free_esc(colors, ext->esc); + } + varena_free(&colors->ext_arena, ext, len + 1); + return -1; +} + +/** Rebuild the case-insensitive trie after all extensions have been parsed. */ +static int build_iext_trie(struct colors *colors) { + trie_destroy(&colors->iext_trie); + trie_init(&colors->iext_trie); + + TRIE_FOR_EACH(&colors->ext_trie, leaf) { + struct ext_color *ext = leaf->value; + if (!ext->icase) { + continue; + } + + // set_ext() already reversed and lowercased the extension + struct trie_leaf *ileaf; + while ((ileaf = trie_find_postfix(&colors->iext_trie, ext->ext))) { + trie_remove(&colors->ext_trie, ileaf); + } + + ileaf = trie_insert_str(&colors->iext_trie, ext->ext); + if (!ileaf) { + return -1; + } + ileaf->value = ext; + } + + return 0; } /** * Find a color by an extension. */ -static const char *get_ext_color(const struct colors *colors, const char *filename) { +static const struct esc_seq *get_ext(const struct colors *colors, const char *filename) { size_t name_len = strlen(filename); size_t ext_len = name_len < EXT_MAX ? name_len : EXT_MAX; - const char *ext = filename + name_len - ext_len; + const char *suffix = filename + name_len - ext_len; char xfrm[ext_len + 1]; - memcpy(xfrm, ext, sizeof(xfrm)); - extxfrm(xfrm, ext_len); + memcpy(xfrm, suffix, sizeof(xfrm)); - const struct trie_leaf *leaf = trie_find_prefix(&colors->ext_colors, xfrm); - if (leaf) { - return leaf->value; - } else { - return NULL; + ext_reverse(xfrm, ext_len); + const struct trie_leaf *leaf = trie_find_prefix(&colors->ext_trie, xfrm); + const struct ext_color *ext = leaf ? leaf->value : NULL; + + ext_tolower(xfrm, ext_len); + const struct trie_leaf *ileaf = trie_find_prefix(&colors->iext_trie, xfrm); + const struct ext_color *iext = ileaf ? ileaf->value : NULL; + + if (iext && (!ext || ext->priority < iext->priority)) { + ext = iext; } + + return ext ? ext->esc : NULL; } /** @@ -211,6 +346,8 @@ static const char *get_ext_color(const struct colors *colors, const char *filena * * See man dir_colors. * + * @param str + * A dstring to fill with the unescaped chunk. * @param value * The value to parse. * @param end @@ -218,16 +355,18 @@ static const char *get_ext_color(const struct colors *colors, const char *filena * @param[out] next * Will be set to the next chunk. * @return - * The parsed chunk as a dstring. + * 0 on success, -1 on failure. */ -static char *unescape(const char *value, char end, const char **next) { +static int unescape(char **str, const char *value, char end, const char **next) { + *next = NULL; + if (!value) { - goto fail; + errno = EINVAL; + return -1; } - char *str = dstralloc(0); - if (!str) { - goto fail_str; + if (dstresize(str, 0) != 0) { + return -1; } const char *i; @@ -304,7 +443,8 @@ static char *unescape(const char *value, char end, const char **next) { break; case '\0': - goto fail_str; + errno = EINVAL; + return -1; default: c = *i; @@ -318,7 +458,8 @@ static char *unescape(const char *value, char end, const char **next) { c = '\177'; break; case '\0': - goto fail_str; + errno = EINVAL; + return -1; default: // CTRL masks bits 6 and 7 c = *i & 0x1F; @@ -331,77 +472,70 @@ static char *unescape(const char *value, char end, const char **next) { break; } - if (dstrapp(&str, c) != 0) { - goto fail_str; + if (dstrapp(str, c) != 0) { + return -1; } } if (*i) { *next = i + 1; - } else { - *next = NULL; } - return str; - -fail_str: - dstrfree(str); -fail: - *next = NULL; - return NULL; + return 0; } /** Parse the GNU $LS_COLORS format. */ -static void parse_gnu_ls_colors(struct colors *colors, const char *ls_colors) { +static int parse_gnu_ls_colors(struct colors *colors, const char *ls_colors) { + int ret = -1; + char *key = NULL; + char *value = NULL; + for (const char *chunk = ls_colors, *next; chunk; chunk = next) { if (chunk[0] == '*') { - char *key = unescape(chunk + 1, '=', &next); - if (!key) { - continue; + if (unescape(&key, chunk + 1, '=', &next) != 0) { + goto fail; } - - char *value = unescape(next, ':', &next); - if (value) { - if (set_ext_color(colors, key, value) != 0) { - dstrfree(value); - } + if (unescape(&value, next, ':', &next) != 0) { + goto fail; + } + if (set_ext(colors, key, value) != 0) { + goto fail; } - - dstrfree(key); } else { const char *equals = strchr(chunk, '='); if (!equals) { break; } - char *value = unescape(equals + 1, ':', &next); - if (!value) { - continue; + if (dstrncpy(&key, chunk, equals - chunk) != 0) { + goto fail; } - - char *key = strndup(chunk, equals - chunk); - if (!key) { - dstrfree(value); - continue; + if (unescape(&value, equals + 1, ':', &next) != 0) { + goto fail; } // All-zero values should be treated like NULL, to fall // back on any other relevant coloring for that file + char *esc = value; if (strspn(value, "0") == strlen(value) && strcmp(key, "rs") != 0 && strcmp(key, "lc") != 0 && strcmp(key, "rc") != 0 && strcmp(key, "ec") != 0) { - dstrfree(value); - value = NULL; + esc = NULL; } - if (set_color(colors, key, value) != 0) { - dstrfree(value); + if (set_esc(colors, key, esc) != 0) { + goto fail; } - free(key); } } + + ret = 0; +fail: + dstrfree(value); + dstrfree(key); + return ret; } struct colors *parse_colors(void) { @@ -410,91 +544,105 @@ struct colors *parse_colors(void) { return NULL; } + VARENA_INIT(&colors->esc_arena, struct esc_seq, seq); + VARENA_INIT(&colors->ext_arena, struct ext_color, ext); trie_init(&colors->names); - trie_init(&colors->ext_colors); + colors->ext_count = 0; + trie_init(&colors->ext_trie); + trie_init(&colors->iext_trie); int ret = 0; // From man console_codes - ret |= init_color(colors, "rs", "0", &colors->reset); - ret |= init_color(colors, "lc", "\033[", &colors->leftcode); - ret |= init_color(colors, "rc", "m", &colors->rightcode); - ret |= init_color(colors, "ec", NULL, &colors->endcode); - ret |= init_color(colors, "cl", "\033[K", &colors->clear_to_eol); - - ret |= init_color(colors, "bld", "01;39", &colors->bold); - ret |= init_color(colors, "gry", "01;30", &colors->gray); - ret |= init_color(colors, "red", "01;31", &colors->red); - ret |= init_color(colors, "grn", "01;32", &colors->green); - ret |= init_color(colors, "ylw", "01;33", &colors->yellow); - ret |= init_color(colors, "blu", "01;34", &colors->blue); - ret |= init_color(colors, "mag", "01;35", &colors->magenta); - ret |= init_color(colors, "cyn", "01;36", &colors->cyan); - ret |= init_color(colors, "wht", "01;37", &colors->white); - - ret |= init_color(colors, "wrn", "01;33", &colors->warning); - ret |= init_color(colors, "err", "01;31", &colors->error); + ret |= init_esc(colors, "rs", "0", &colors->reset); + ret |= init_esc(colors, "lc", "\033[", &colors->leftcode); + ret |= init_esc(colors, "rc", "m", &colors->rightcode); + ret |= init_esc(colors, "ec", NULL, &colors->endcode); + ret |= init_esc(colors, "cl", "\033[K", &colors->clear_to_eol); + + ret |= init_esc(colors, "bld", "01;39", &colors->bold); + ret |= init_esc(colors, "gry", "01;30", &colors->gray); + ret |= init_esc(colors, "red", "01;31", &colors->red); + ret |= init_esc(colors, "grn", "01;32", &colors->green); + ret |= init_esc(colors, "ylw", "01;33", &colors->yellow); + ret |= init_esc(colors, "blu", "01;34", &colors->blue); + ret |= init_esc(colors, "mag", "01;35", &colors->magenta); + ret |= init_esc(colors, "cyn", "01;36", &colors->cyan); + ret |= init_esc(colors, "wht", "01;37", &colors->white); + + ret |= init_esc(colors, "wrn", "01;33", &colors->warning); + ret |= init_esc(colors, "err", "01;31", &colors->error); // Defaults from man dir_colors // "" means fall back to ->normal - ret |= init_color(colors, "no", NULL, &colors->normal); + ret |= init_esc(colors, "no", NULL, &colors->normal); - ret |= init_color(colors, "fi", "", &colors->file); - ret |= init_color(colors, "mh", NULL, &colors->multi_hard); - ret |= init_color(colors, "ex", "01;32", &colors->executable); - ret |= init_color(colors, "ca", NULL, &colors->capable); - ret |= init_color(colors, "sg", "30;43", &colors->setgid); - ret |= init_color(colors, "su", "37;41", &colors->setuid); + ret |= init_esc(colors, "fi", "", &colors->file); + ret |= init_esc(colors, "mh", NULL, &colors->multi_hard); + ret |= init_esc(colors, "ex", "01;32", &colors->executable); + ret |= init_esc(colors, "ca", NULL, &colors->capable); + ret |= init_esc(colors, "sg", "30;43", &colors->setgid); + ret |= init_esc(colors, "su", "37;41", &colors->setuid); - ret |= init_color(colors, "di", "01;34", &colors->directory); - ret |= init_color(colors, "st", "37;44", &colors->sticky); - ret |= init_color(colors, "ow", "34;42", &colors->other_writable); - ret |= init_color(colors, "tw", "30;42", &colors->sticky_other_writable); + ret |= init_esc(colors, "di", "01;34", &colors->directory); + ret |= init_esc(colors, "st", "37;44", &colors->sticky); + ret |= init_esc(colors, "ow", "34;42", &colors->other_writable); + ret |= init_esc(colors, "tw", "30;42", &colors->sticky_other_writable); - ret |= init_color(colors, "ln", "01;36", &colors->link); - ret |= init_color(colors, "or", NULL, &colors->orphan); - ret |= init_color(colors, "mi", NULL, &colors->missing); + ret |= init_esc(colors, "ln", "01;36", &colors->link); + ret |= init_esc(colors, "or", NULL, &colors->orphan); + ret |= init_esc(colors, "mi", NULL, &colors->missing); colors->link_as_target = false; - ret |= init_color(colors, "bd", "01;33", &colors->blockdev); - ret |= init_color(colors, "cd", "01;33", &colors->chardev); - ret |= init_color(colors, "do", "01;35", &colors->door); - ret |= init_color(colors, "pi", "33", &colors->pipe); - ret |= init_color(colors, "so", "01;35", &colors->socket); + ret |= init_esc(colors, "bd", "01;33", &colors->blockdev); + ret |= init_esc(colors, "cd", "01;33", &colors->chardev); + ret |= init_esc(colors, "do", "01;35", &colors->door); + ret |= init_esc(colors, "pi", "33", &colors->pipe); + ret |= init_esc(colors, "so", "01;35", &colors->socket); - if (ret) { - free_colors(colors); - return NULL; + if (ret != 0) { + goto fail; } - parse_gnu_ls_colors(colors, getenv("LS_COLORS")); - parse_gnu_ls_colors(colors, getenv("BFS_COLORS")); + if (parse_gnu_ls_colors(colors, getenv("LS_COLORS")) != 0) { + goto fail; + } + if (parse_gnu_ls_colors(colors, getenv("BFS_COLORS")) != 0) { + goto fail; + } + if (build_iext_trie(colors) != 0) { + goto fail; + } - if (colors->link && strcmp(colors->link, "target") == 0) { - colors->link_as_target = true; - dstresize(&colors->link, 0); + if (colors->link) { + size_t len = strlen("target"); + if (colors->link->len == len && memcmp(colors->link->seq, "target", len) == 0) { + colors->link_as_target = true; + colors->link->len = 0; + } } return colors; + +fail: + free_colors(colors); + return NULL; } void free_colors(struct colors *colors) { - if (colors) { - TRIE_FOR_EACH(&colors->ext_colors, leaf) { - dstrfree(leaf->value); - } - trie_destroy(&colors->ext_colors); + if (!colors) { + return; + } - TRIE_FOR_EACH(&colors->names, leaf) { - char **field = leaf->value; - dstrfree(*field); - } - trie_destroy(&colors->names); + trie_destroy(&colors->iext_trie); + trie_destroy(&colors->ext_trie); + trie_destroy(&colors->names); + varena_destroy(&colors->ext_arena); + varena_destroy(&colors->esc_arena); - free(colors); - } + free(colors); } CFILE *cfwrap(FILE *file, const struct colors *colors, bool close) { @@ -547,14 +695,14 @@ static bool is_link_broken(const struct BFTW *ftwbuf) { } /** Get the color for a file. */ -static const char *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { +static const struct esc_seq *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { enum bfs_type type = bftw_type(ftwbuf, flags); if (type == BFS_ERROR) { goto error; } const struct bfs_stat *statbuf = NULL; - const char *color = NULL; + const struct esc_seq *color = NULL; switch (type) { case BFS_REG: @@ -578,7 +726,7 @@ static const char *file_color(const struct colors *colors, const char *filename, } if (!color) { - color = get_ext_color(colors, filename); + color = get_ext(colors, filename); } if (!color) { @@ -635,7 +783,7 @@ static const char *file_color(const struct colors *colors, const char *filename, break; } - if (color && !color[0]) { + if (color && color->len == 0) { color = colors->normal; } @@ -649,17 +797,22 @@ error: } } +/** Print an escape sequence chunk. */ +static int print_esc_chunk(CFILE *cfile, const struct esc_seq *esc) { + return dstrxcat(&cfile->buffer, esc->seq, esc->len); +} + /** Print an ANSI escape sequence. */ -static int print_esc(CFILE *cfile, const char *esc) { +static int print_esc(CFILE *cfile, const struct esc_seq *esc) { const struct colors *colors = cfile->colors; - if (dstrdcat(&cfile->buffer, colors->leftcode) != 0) { + if (print_esc_chunk(cfile, colors->leftcode) != 0) { return -1; } - if (dstrdcat(&cfile->buffer, esc) != 0) { + if (print_esc_chunk(cfile, esc) != 0) { return -1; } - if (dstrdcat(&cfile->buffer, colors->rightcode) != 0) { + if (print_esc_chunk(cfile, colors->rightcode) != 0) { return -1; } @@ -671,20 +824,20 @@ static int print_reset(CFILE *cfile) { const struct colors *colors = cfile->colors; if (colors->endcode) { - return dstrdcat(&cfile->buffer, colors->endcode); + return dstrxcat(&cfile->buffer, colors->endcode->seq, colors->endcode->len); } else { return print_esc(cfile, colors->reset); } } /** Print a string with an optional color. */ -static int print_colored(CFILE *cfile, const char *esc, const char *str, size_t len) { +static int print_colored(CFILE *cfile, const struct esc_seq *esc, const char *str, size_t len) { if (esc) { if (print_esc(cfile, esc) != 0) { return -1; } } - if (dstrncat(&cfile->buffer, str, len) != 0) { + if (dstrxcat(&cfile->buffer, str, len) != 0) { return -1; } if (esc) { @@ -772,7 +925,7 @@ static int print_dirs_colored(CFILE *cfile, const char *path, const struct BFTW } if ((size_t)broken < nameoff) { - const char *color = colors->missing; + const struct esc_seq *color = colors->missing; if (!color) { color = colors->orphan; } @@ -786,8 +939,8 @@ static int print_dirs_colored(CFILE *cfile, const char *path, const struct BFTW /** Print a file name with colors. */ static int print_name_colored(CFILE *cfile, const char *name, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { - const char *color = file_color(cfile->colors, name, ftwbuf, flags); - return print_colored(cfile, color, name, strlen(name)); + const struct esc_seq *esc = file_color(cfile->colors, name, ftwbuf, flags); + return print_colored(cfile, esc, name, strlen(name)); } /** Print a path with colors. */ @@ -1077,7 +1230,7 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { memcpy(name, i, len); name[len] = '\0'; - char **esc = get_color(colors, name); + struct esc_seq **esc = get_esc(colors, name); if (!esc) { goto invalid; } diff --git a/tests/bfs/color_ext_case.out b/tests/bfs/color_ext_case.out new file mode 100644 index 0000000..c3ded3a --- /dev/null +++ b/tests/bfs/color_ext_case.out @@ -0,0 +1,25 @@ +rainbow +rainbow/lower.gz +rainbow/lower.tar.gz +rainbow/exec.sh +rainbow/upper.GZ +rainbow/upper.TAR.GZ +rainbow/lower.tar +rainbow/upper.TAR +rainbow/ul.TAR.gz +rainbow/lu.tar.GZ +rainbow/socket +rainbow/broken +rainbow/chardev_link +rainbow/link.txt +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky +rainbow/file.dat +rainbow/file.txt +rainbow/mh1 +rainbow/mh2 diff --git a/tests/bfs/color_ext_case.sh b/tests/bfs/color_ext_case.sh new file mode 100644 index 0000000..91f1f59 --- /dev/null +++ b/tests/bfs/color_ext_case.sh @@ -0,0 +1 @@ +LS_COLORS="*.gz=01;31:*.GZ=01;32:*.tAr=01;33:*.TAR.gz=01;34:*.tar.GZ=01;35:" bfs_diff rainbow -color diff --git a/tests/bfs/color_nul.out b/tests/bfs/color_nul.out index c328f82..4ea4d85 100644 Binary files a/tests/bfs/color_nul.out and b/tests/bfs/color_nul.out differ diff --git a/tests/bfs/color_nul.sh b/tests/bfs/color_nul.sh index 4979569..cb662d6 100644 --- a/tests/bfs/color_nul.sh +++ b/tests/bfs/color_nul.sh @@ -1,2 +1,3 @@ -LS_COLORS="ec=\33[m\0:" invoke_bfs rainbow -color -maxdepth 0 >"$OUT" +LS_COLORS="ec=\33[\0m:*.gz=\0\61;31:" invoke_bfs rainbow -color | tr '\0' '0' >"$OUT" +sort_output diff_output diff --git a/tests/bfs/color_star.out b/tests/bfs/color_star.out deleted file mode 100644 index 34c7153..0000000 --- a/tests/bfs/color_star.out +++ /dev/null @@ -1,25 +0,0 @@ -rainbow -rainbow/exec.sh -rainbow/socket -rainbow/broken -rainbow/chardev_link -rainbow/link.txt -rainbow/sticky_ow -rainbow/sgid -rainbow/pipe -rainbow/ow -rainbow/sugid -rainbow/suid -rainbow/sticky -rainbow/file.dat -rainbow/file.txt -rainbow/lower.gz -rainbow/lower.tar -rainbow/lower.tar.gz -rainbow/lu.tar.GZ -rainbow/mh1 -rainbow/mh2 -rainbow/ul.TAR.gz -rainbow/upper.GZ -rainbow/upper.TAR -rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_star.sh b/tests/bfs/color_star.sh index 3ada4fd..6d5312e 100644 --- a/tests/bfs/color_star.sh +++ b/tests/bfs/color_star.sh @@ -1,2 +1,2 @@ # Regression test: don't segfault on LS_COLORS="*" -LS_COLORS="*" bfs_diff rainbow -color +! LS_COLORS="*" invoke_bfs rainbow -color -- cgit v1.2.3 From 2ea6a1d7b99dbb389ec81054d136e5030717d52a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 30 Jun 2023 10:25:42 -0400 Subject: color: Compare values too when deciding to smart-case --- src/color.c | 81 +++++++++++++++++++++++++++++++++++--------- tests/bfs/color_ext_case.out | 2 +- tests/bfs/color_ext_case.sh | 7 +++- 3 files changed, 72 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/src/color.c b/src/color.c index 82ff763..ec24d98 100644 --- a/src/color.c +++ b/src/color.c @@ -42,8 +42,8 @@ struct ext_color { struct esc_seq *esc; /** The length of the extension to match. */ size_t len; - /** Whether the comparison should be case-insensitive. */ - bool icase; + /** Whether the comparison should be case-sensitive. */ + bool case_sensitive; /** The extension to match (NUL-terminated). */ char ext[]; }; @@ -147,6 +147,11 @@ static int init_esc(struct colors *colors, const char *name, const char *value, } } +/** Check if an escape sequence is equal to a string. */ +static bool esc_eq(const struct esc_seq *esc, const char *str, size_t len) { + return esc->len == len && memcmp(esc->seq, str, len) == 0; +} + /** Get an escape sequence from the table. */ static struct esc_seq **get_esc(const struct colors *colors, const char *name) { const struct trie_leaf *leaf = trie_find_str(&colors->names, name); @@ -204,6 +209,55 @@ static void ext_tolower(char *ext, size_t len) { /** Maximum supported extension length. */ #define EXT_MAX 255 +/** + * The "smart case" algorithm. + * + * @param ext + * The current extension being added. + * @param prev + * The previous case-sensitive match, if any, for the same extension. + * @param iprev + * The previous case-insensitive match, if any, for the same extension. + * @return + * Whether this extension should become case-sensitive. + */ +static bool ext_case_sensitive(struct ext_color *ext, struct ext_color *prev, struct ext_color *iprev) { + // This is the first case-insensitive occurrence of this extension, e.g. + // + // *.gz=01;31:*.tar.gz=01;33 + if (!iprev) { + bfs_assert(!prev); + return false; + } + + // If the last version of this extension is already case-sensitive, + // this one should be too, e.g. + // + // *.tar.gz=01;31:*.TAR.GZ=01;32:*.TAR.GZ=01;33 + if (iprev->case_sensitive) { + return true; + } + + // The case matches the last occurrence exactly, e.g. + // + // *.tar.gz=01;31:*.tar.gz=01;33 + if (iprev == prev) { + return false; + } + + // Different case, but same value, e.g. + // + // *.tar.gz=01;31:*.TAR.GZ=01;31 + if (esc_eq(iprev->esc, ext->esc->seq, ext->esc->len)) { + return false; + } + + // Different case, different value, e.g. + // + // *.tar.gz=01;31:*.TAR.GZ=01;33 + return true; +} + /** Set the color for an extension. */ static int set_ext(struct colors *colors, char *key, char *value) { size_t len = dstrlen(key); @@ -214,7 +268,7 @@ static int set_ext(struct colors *colors, char *key, char *value) { ext->priority = colors->ext_count++; ext->len = len; - ext->icase = true; + ext->case_sensitive = false; ext->esc = new_esc(colors, value, dstrlen(value)); if (!ext->esc) { goto fail; @@ -253,12 +307,10 @@ static int set_ext(struct colors *colors, char *key, char *value) { goto fail; } - // If a match for the lowercased extension exists and is different from - // the exact match, or is already case-sensitive, mark this one too struct ext_color *iprev = leaf->value; - if (iprev && (iprev != prev || !iprev->icase)) { - iprev->icase = false; - ext->icase = false; + if (ext_case_sensitive(ext, prev, iprev)) { + iprev->case_sensitive = true; + ext->case_sensitive = true; } leaf->value = ext; @@ -279,14 +331,14 @@ static int build_iext_trie(struct colors *colors) { TRIE_FOR_EACH(&colors->ext_trie, leaf) { struct ext_color *ext = leaf->value; - if (!ext->icase) { + if (ext->case_sensitive) { continue; } // set_ext() already reversed and lowercased the extension struct trie_leaf *ileaf; while ((ileaf = trie_find_postfix(&colors->iext_trie, ext->ext))) { - trie_remove(&colors->ext_trie, ileaf); + trie_remove(&colors->iext_trie, ileaf); } ileaf = trie_insert_str(&colors->iext_trie, ext->ext); @@ -616,12 +668,9 @@ struct colors *parse_colors(void) { goto fail; } - if (colors->link) { - size_t len = strlen("target"); - if (colors->link->len == len && memcmp(colors->link->seq, "target", len) == 0) { - colors->link_as_target = true; - colors->link->len = 0; - } + if (colors->link && esc_eq(colors->link, "target", strlen("target"))) { + colors->link_as_target = true; + colors->link->len = 0; } return colors; diff --git a/tests/bfs/color_ext_case.out b/tests/bfs/color_ext_case.out index c3ded3a..4e7258d 100644 --- a/tests/bfs/color_ext_case.out +++ b/tests/bfs/color_ext_case.out @@ -16,10 +16,10 @@ rainbow/sgid rainbow/pipe rainbow/ow +rainbow/file.txt rainbow/sugid rainbow/suid rainbow/sticky rainbow/file.dat -rainbow/file.txt rainbow/mh1 rainbow/mh2 diff --git a/tests/bfs/color_ext_case.sh b/tests/bfs/color_ext_case.sh index 91f1f59..4adba69 100644 --- a/tests/bfs/color_ext_case.sh +++ b/tests/bfs/color_ext_case.sh @@ -1 +1,6 @@ -LS_COLORS="*.gz=01;31:*.GZ=01;32:*.tAr=01;33:*.TAR.gz=01;34:*.tar.GZ=01;35:" bfs_diff rainbow -color +# *.gz=01;31:*.GZ=01;32 -- case sensitive +# *.tAr=01;33:*.TaR=01;33 -- case-insensitive +# *.TAR.gz=01;34:*.tar.GZ=01;35 -- case-sensitive +# *.txt=35:*TXT=36 -- case-insensitive +export LS_COLORS="*.gz=01;31:*.GZ=01;32:*.tAr=01;33:*.TaR=01;33:*.TAR.gz=01;34:*.tar.GZ=01;35:*.txt=35:*TXT=36" +bfs_diff rainbow -color -- cgit v1.2.3 From afdc5dca7ca378cb75fe2a2c9df3881a89ba17dd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 12 Jul 2023 14:10:29 -0400 Subject: tests/bfs: Add tests for -j --- tests/bfs/j1.out | 19 +++++++++++++++++++ tests/bfs/j1.sh | 1 + tests/bfs/j64.out | 19 +++++++++++++++++++ tests/bfs/j64.sh | 1 + tests/bfs/j_negative.sh | 1 + 5 files changed, 41 insertions(+) create mode 100644 tests/bfs/j1.out create mode 100644 tests/bfs/j1.sh create mode 100644 tests/bfs/j64.out create mode 100644 tests/bfs/j64.sh create mode 100644 tests/bfs/j_negative.sh (limited to 'tests') diff --git a/tests/bfs/j1.out b/tests/bfs/j1.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/j1.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/j1.sh b/tests/bfs/j1.sh new file mode 100644 index 0000000..972ac1b --- /dev/null +++ b/tests/bfs/j1.sh @@ -0,0 +1 @@ +bfs_diff -j1 basic diff --git a/tests/bfs/j64.out b/tests/bfs/j64.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/j64.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/j64.sh b/tests/bfs/j64.sh new file mode 100644 index 0000000..c56788f --- /dev/null +++ b/tests/bfs/j64.sh @@ -0,0 +1 @@ +bfs_diff -j64 basic diff --git a/tests/bfs/j_negative.sh b/tests/bfs/j_negative.sh new file mode 100644 index 0000000..809c98c --- /dev/null +++ b/tests/bfs/j_negative.sh @@ -0,0 +1 @@ +! invoke_bfs -j-1 basic -- cgit v1.2.3 From 663a2b938707458de4e87be68cffb6a970e771c6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 12 Jul 2023 14:12:27 -0400 Subject: parse: Reject -j0 --- src/parse.c | 10 +++++++++- tests/bfs/j0.sh | 1 + 2 files changed, 10 insertions(+), 1 deletion(-) create mode 100644 tests/bfs/j0.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index c225a5b..6b1eaa0 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1623,11 +1623,19 @@ static struct bfs_expr *parse_jobs(struct parser_state *state, int arg1, int arg return NULL; } - if (!parse_int(state, expr->argv, expr->argv[0] + 2, &state->ctx->threads, IF_INT | IF_UNSIGNED)) { + unsigned int n; + if (!parse_int(state, expr->argv, expr->argv[0] + 2, &n, IF_INT | IF_UNSIGNED)) { bfs_expr_free(expr); return NULL; } + if (n == 0) { + parse_expr_error(state, expr, "${bld}0${rs} is not enough threads.\n"); + bfs_expr_free(expr); + return NULL; + } + + state->ctx->threads = n; return expr; } diff --git a/tests/bfs/j0.sh b/tests/bfs/j0.sh new file mode 100644 index 0000000..97a7c5c --- /dev/null +++ b/tests/bfs/j0.sh @@ -0,0 +1 @@ +! invoke_bfs -j0 basic -- cgit v1.2.3 From b577bedfd37d93cb802c36beb93cf6581af51930 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 13 Jul 2023 13:40:46 -0400 Subject: color: Get rid of EXT_MAX --- src/color.c | 40 ++++++++++++++++++++++++++++++---------- tests/bfs/color_deep.out | 16 ++++++++++++++++ tests/bfs/color_deep.sh | 7 +++++++ 3 files changed, 53 insertions(+), 10 deletions(-) create mode 100644 tests/bfs/color_deep.out create mode 100644 tests/bfs/color_deep.sh (limited to 'tests') diff --git a/src/color.c b/src/color.c index 0f5829f..00d7920 100644 --- a/src/color.c +++ b/src/color.c @@ -106,6 +106,8 @@ struct colors { /** Number of extensions. */ size_t ext_count; + /** Longest extension. */ + size_t ext_len; /** Case-sensitive extension trie. */ struct trie ext_trie; /** Case-insensitive extension trie. */ @@ -207,9 +209,6 @@ static void ext_tolower(char *ext, size_t len) { } } -/** Maximum supported extension length. */ -#define EXT_MAX 255 - /** * The "smart case" algorithm. * @@ -331,6 +330,11 @@ static int build_iext_trie(struct colors *colors) { trie_init(&colors->iext_trie); TRIE_FOR_EACH(&colors->ext_trie, leaf) { + size_t len = leaf->length - 1; + if (colors->ext_len < len) { + colors->ext_len = len; + } + struct ext_color *ext = leaf->value; if (ext->case_sensitive) { continue; @@ -356,25 +360,40 @@ static int build_iext_trie(struct colors *colors) { * Find a color by an extension. */ static const struct esc_seq *get_ext(const struct colors *colors, const char *filename) { + size_t ext_len = colors->ext_len; size_t name_len = strlen(filename); - size_t ext_len = name_len < EXT_MAX ? name_len : EXT_MAX; + if (name_len < ext_len) { + ext_len = name_len; + } const char *suffix = filename + name_len - ext_len; - char xfrm[ext_len + 1]; - memcpy(xfrm, suffix, sizeof(xfrm)); + char buf[256]; + char *copy; + if (ext_len < sizeof(buf)) { + copy = memcpy(buf, suffix, ext_len + 1); + } else { + copy = strndup(suffix, ext_len); + if (!copy) { + return NULL; + } + } - ext_reverse(xfrm, ext_len); - const struct trie_leaf *leaf = trie_find_prefix(&colors->ext_trie, xfrm); + ext_reverse(copy, ext_len); + const struct trie_leaf *leaf = trie_find_prefix(&colors->ext_trie, copy); const struct ext_color *ext = leaf ? leaf->value : NULL; - ext_tolower(xfrm, ext_len); - const struct trie_leaf *ileaf = trie_find_prefix(&colors->iext_trie, xfrm); + ext_tolower(copy, ext_len); + const struct trie_leaf *ileaf = trie_find_prefix(&colors->iext_trie, copy); const struct ext_color *iext = ileaf ? ileaf->value : NULL; if (iext && (!ext || ext->priority < iext->priority)) { ext = iext; } + if (copy != buf) { + free(copy); + } + return ext ? ext->esc : NULL; } @@ -601,6 +620,7 @@ struct colors *parse_colors(void) { VARENA_INIT(&colors->ext_arena, struct ext_color, ext); trie_init(&colors->names); colors->ext_count = 0; + colors->ext_len = 0; trie_init(&colors->ext_trie); trie_init(&colors->iext_trie); diff --git a/tests/bfs/color_deep.out b/tests/bfs/color_deep.out new file mode 100644 index 0000000..fb990d5 --- /dev/null +++ b/tests/bfs/color_deep.out @@ -0,0 +1,16 @@ +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/bfs/color_deep.sh b/tests/bfs/color_deep.sh new file mode 100644 index 0000000..a83ee0e --- /dev/null +++ b/tests/bfs/color_deep.sh @@ -0,0 +1,7 @@ +name="0123456789ABCDEF" +name="${name}${name}${name}${name}" +name="${name}${name}${name}${name}" +name="${name:0:255}" +export LS_COLORS="*${name}=01:" + +bfs_diff deep -color -type f -printf '%f\n' -- cgit v1.2.3 From c5a2bfd924445dfccd943ea34374a5d1266f5cb9 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 13 Jul 2023 15:22:42 -0400 Subject: color: TTY-escape filenames --- src/color.c | 10 +++++++++- src/color.h | 1 + src/printf.c | 4 ++-- tests/bfs/color.out | 2 ++ tests/bfs/color_L.out | 2 ++ tests/bfs/color_L_ln_target.out | 2 ++ tests/bfs/color_L_no_stat.out | 2 ++ tests/bfs/color_cd0_no.out | 2 ++ tests/bfs/color_escapes.out | 2 ++ tests/bfs/color_ext.out | 2 ++ tests/bfs/color_ext0.out | 2 ++ tests/bfs/color_ext_case.out | 2 ++ tests/bfs/color_ext_override.out | 2 ++ tests/bfs/color_ext_underride.out | 2 ++ tests/bfs/color_fi0_no.out | 2 ++ tests/bfs/color_fi_no.out | 2 ++ tests/bfs/color_ln_target.out | 2 ++ tests/bfs/color_mh.out | 2 ++ tests/bfs/color_mh0.out | 2 ++ tests/bfs/color_mi.out | 2 ++ tests/bfs/color_missing_colon.out | 2 ++ tests/bfs/color_no.out | 2 ++ tests/bfs/color_no_stat.out | 2 ++ tests/bfs/color_nul.out | 2 ++ tests/bfs/color_or.out | 2 ++ tests/bfs/color_or0_mi.out | 2 ++ tests/bfs/color_or0_mi0.out | 2 ++ tests/bfs/color_or_mi.out | 2 ++ tests/bfs/color_or_mi0.out | 2 ++ tests/bfs/color_rs_lc_rc_ec.out | 2 ++ tests/bfs/color_st0_tw0_ow.out | 2 ++ tests/bfs/color_st0_tw0_ow0.out | 2 ++ tests/bfs/color_st0_tw_ow.out | 2 ++ tests/bfs/color_st0_tw_ow0.out | 2 ++ tests/bfs/color_st_tw0_ow.out | 2 ++ tests/bfs/color_st_tw0_ow0.out | 2 ++ tests/bfs/color_st_tw_ow0.out | 2 ++ tests/bfs/color_su0_sg.out | 2 ++ tests/bfs/color_su0_sg0.out | 2 ++ tests/bfs/color_su_sg0.out | 2 ++ tests/bfs/nocolor.out | 2 ++ tests/gnu/empty_special.out | 1 + tests/tests.sh | 1 + 43 files changed, 90 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/src/color.c b/src/color.c index 00d7920..6593f5b 100644 --- a/src/color.c +++ b/src/color.c @@ -922,9 +922,12 @@ static int print_colored(CFILE *cfile, const struct esc_seq *esc, const char *st if (print_esc(cfile, esc) != 0) { return -1; } - if (dstrxcat(&cfile->buffer, str, len) != 0) { + + // Don't let the string itself interfere with the colors + if (print_wordesc(cfile, str, len, WESC_TTY) != 0) { return -1; } + if (print_reset(cfile) != 0) { return -1; } @@ -1242,6 +1245,11 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { return -1; } break; + case 'Q': + if (print_wordesc(cfile, va_arg(args, const char *), SIZE_MAX, WESC_TTY) != 0) { + return -1; + } + break; case 'F': if (print_name(cfile, va_arg(args, const struct BFTW *)) != 0) { diff --git a/src/color.h b/src/color.h index 932d551..0d46c33 100644 --- a/src/color.h +++ b/src/color.h @@ -87,6 +87,7 @@ int cfclose(CFILE *cfile); * %zu: A size_t * %m: strerror(errno) * %pq: A shell-escaped string, like bash's printf %q + * %pQ: A TTY-escaped string. * %pF: A colored file name, from a const struct BFTW * argument * %pP: A colored file path, from a const struct BFTW * argument * %pL: A colored link target, from a const struct BFTW * argument diff --git a/src/printf.c b/src/printf.c index 6520d2d..f0910fa 100644 --- a/src/printf.c +++ b/src/printf.c @@ -298,7 +298,7 @@ static int bfs_printf_h(CFILE *cfile, const struct bfs_printf *directive, const int ret; if (should_color(cfile, directive)) { - ret = cfprintf(cfile, "${di}%s${rs}", buf); + ret = cfprintf(cfile, "${di}%pQ${rs}", buf); } else { ret = dyn_fprintf(cfile->file, directive, buf); } @@ -313,7 +313,7 @@ static int bfs_printf_H(CFILE *cfile, const struct bfs_printf *directive, const if (ftwbuf->depth == 0) { return cfprintf(cfile, "%pP", ftwbuf); } else { - return cfprintf(cfile, "${di}%s${rs}", ftwbuf->root); + return cfprintf(cfile, "${di}%pQ${rs}", ftwbuf->root); } } else { return dyn_fprintf(cfile->file, directive, ftwbuf->root); diff --git a/tests/bfs/color.out b/tests/bfs/color.out index 34c7153..5c6b43e 100644 --- a/tests/bfs/color.out +++ b/tests/bfs/color.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_L.out b/tests/bfs/color_L.out index 89f9410..6904e39 100644 --- a/tests/bfs/color_L.out +++ b/tests/bfs/color_L.out @@ -1,6 +1,8 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh rainbow/chardev_link +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/sticky_ow diff --git a/tests/bfs/color_L_ln_target.out b/tests/bfs/color_L_ln_target.out index 2562c98..50105c3 100644 --- a/tests/bfs/color_L_ln_target.out +++ b/tests/bfs/color_L_ln_target.out @@ -1,7 +1,9 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh rainbow/chardev_link +rainbow/$'\e[1m' rainbow/socket rainbow/sticky_ow rainbow/sgid diff --git a/tests/bfs/color_L_no_stat.out b/tests/bfs/color_L_no_stat.out index 4fe99c0..beb538c 100644 --- a/tests/bfs/color_L_no_stat.out +++ b/tests/bfs/color_L_no_stat.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/chardev_link +rainbow/$'\e[1m' rainbow/ow rainbow/sticky rainbow/sticky_ow diff --git a/tests/bfs/color_cd0_no.out b/tests/bfs/color_cd0_no.out index 30ad97f..4d710fd 100644 --- a/tests/bfs/color_cd0_no.out +++ b/tests/bfs/color_cd0_no.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/file.dat diff --git a/tests/bfs/color_escapes.out b/tests/bfs/color_escapes.out index 808585e..746a9ea 100644 --- a/tests/bfs/color_escapes.out +++ b/tests/bfs/color_escapes.out @@ -1,5 +1,7 @@ +:$'rainbow/\e[1m/'$'\e[0m' :rainbow :rainbow/:exec.sh +:rainbow/:$'\e[1m' :rainbow/:socket :rainbow/:broken :rainbow/:chardev_link diff --git a/tests/bfs/color_ext.out b/tests/bfs/color_ext.out index be22b82..a35ca0b 100644 --- a/tests/bfs/color_ext.out +++ b/tests/bfs/color_ext.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext0.out b/tests/bfs/color_ext0.out index d151319..37301cc 100644 --- a/tests/bfs/color_ext0.out +++ b/tests/bfs/color_ext0.out @@ -1,6 +1,8 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/file.txt rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext_case.out b/tests/bfs/color_ext_case.out index 4e7258d..9388343 100644 --- a/tests/bfs/color_ext_case.out +++ b/tests/bfs/color_ext_case.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/lower.gz rainbow/lower.tar.gz @@ -6,6 +7,7 @@ rainbow/upper.TAR.GZ rainbow/lower.tar rainbow/upper.TAR +rainbow/$'\e[1m' rainbow/ul.TAR.gz rainbow/lu.tar.GZ rainbow/socket diff --git a/tests/bfs/color_ext_override.out b/tests/bfs/color_ext_override.out index 3a09cd2..1e0146c 100644 --- a/tests/bfs/color_ext_override.out +++ b/tests/bfs/color_ext_override.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh rainbow/lower.tar @@ -8,6 +9,7 @@ rainbow/ul.TAR.gz rainbow/upper.GZ rainbow/upper.TAR.GZ +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext_underride.out b/tests/bfs/color_ext_underride.out index 073d176..1ed66da 100644 --- a/tests/bfs/color_ext_underride.out +++ b/tests/bfs/color_ext_underride.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/lower.tar.gz rainbow/lu.tar.GZ @@ -8,6 +9,7 @@ rainbow/upper.TAR rainbow/lower.gz rainbow/upper.GZ +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_fi0_no.out b/tests/bfs/color_fi0_no.out index 34c7153..5c6b43e 100644 --- a/tests/bfs/color_fi0_no.out +++ b/tests/bfs/color_fi0_no.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_fi_no.out b/tests/bfs/color_fi_no.out index 8df9355..e64684d 100644 --- a/tests/bfs/color_fi_no.out +++ b/tests/bfs/color_fi_no.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ln_target.out b/tests/bfs/color_ln_target.out index 2562c98..50105c3 100644 --- a/tests/bfs/color_ln_target.out +++ b/tests/bfs/color_ln_target.out @@ -1,7 +1,9 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh rainbow/chardev_link +rainbow/$'\e[1m' rainbow/socket rainbow/sticky_ow rainbow/sgid diff --git a/tests/bfs/color_mh.out b/tests/bfs/color_mh.out index 7521b31..93bfde1 100644 --- a/tests/bfs/color_mh.out +++ b/tests/bfs/color_mh.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_mh0.out b/tests/bfs/color_mh0.out index 34c7153..5c6b43e 100644 --- a/tests/bfs/color_mh0.out +++ b/tests/bfs/color_mh0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_mi.out b/tests/bfs/color_mi.out index 34c7153..5c6b43e 100644 --- a/tests/bfs/color_mi.out +++ b/tests/bfs/color_mi.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_missing_colon.out b/tests/bfs/color_missing_colon.out index be22b82..a35ca0b 100644 --- a/tests/bfs/color_missing_colon.out +++ b/tests/bfs/color_missing_colon.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_no.out b/tests/bfs/color_no.out index b70e47d..c61d246 100644 --- a/tests/bfs/color_no.out +++ b/tests/bfs/color_no.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_no_stat.out b/tests/bfs/color_no_stat.out index 7d7d767..e57e11a 100644 --- a/tests/bfs/color_no_stat.out +++ b/tests/bfs/color_no_stat.out @@ -1,4 +1,6 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow +rainbow/$'\e[1m' rainbow/ow rainbow/sticky rainbow/sticky_ow diff --git a/tests/bfs/color_nul.out b/tests/bfs/color_nul.out index 4ea4d85..7140e32 100644 --- a/tests/bfs/color_nul.out +++ b/tests/bfs/color_nul.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/lower.gz rainbow/lower.tar.gz @@ -6,6 +7,7 @@ rainbow/upper.GZ rainbow/upper.TAR.GZ rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_or.out b/tests/bfs/color_or.out index 98efb63..07916da 100644 --- a/tests/bfs/color_or.out +++ b/tests/bfs/color_or.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/chardev_link rainbow/link.txt diff --git a/tests/bfs/color_or0_mi.out b/tests/bfs/color_or0_mi.out index 34c7153..5c6b43e 100644 --- a/tests/bfs/color_or0_mi.out +++ b/tests/bfs/color_or0_mi.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_or0_mi0.out b/tests/bfs/color_or0_mi0.out index 34c7153..5c6b43e 100644 --- a/tests/bfs/color_or0_mi0.out +++ b/tests/bfs/color_or0_mi0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_or_mi.out b/tests/bfs/color_or_mi.out index a9dc229..20bc61d 100644 --- a/tests/bfs/color_or_mi.out +++ b/tests/bfs/color_or_mi.out @@ -1,6 +1,8 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/chardev_link rainbow/link.txt diff --git a/tests/bfs/color_or_mi0.out b/tests/bfs/color_or_mi0.out index a9dc229..20bc61d 100644 --- a/tests/bfs/color_or_mi0.out +++ b/tests/bfs/color_or_mi0.out @@ -1,6 +1,8 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/chardev_link rainbow/link.txt diff --git a/tests/bfs/color_rs_lc_rc_ec.out b/tests/bfs/color_rs_lc_rc_ec.out index 82d94ec..f78b41f 100644 --- a/tests/bfs/color_rs_lc_rc_ec.out +++ b/tests/bfs/color_rs_lc_rc_ec.out @@ -1,4 +1,6 @@ +LC01;34RC$'rainbow/\e[1m/'EC$'\e[0m' LC01;34RCrainbow/ECLC01;32RCexec.shEC +LC01;34RCrainbow/ECLC01;34RC$'\e[1m'EC LC01;34RCrainbow/ECLC01;35RCsocketEC LC01;34RCrainbow/ECLC01;36RCbrokenEC LC01;34RCrainbow/ECLC01;36RCchardev_linkEC diff --git a/tests/bfs/color_st0_tw0_ow.out b/tests/bfs/color_st0_tw0_ow.out index bdc5942..d1fec74 100644 --- a/tests/bfs/color_st0_tw0_ow.out +++ b/tests/bfs/color_st0_tw0_ow.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/sticky rainbow/socket rainbow/broken diff --git a/tests/bfs/color_st0_tw0_ow0.out b/tests/bfs/color_st0_tw0_ow0.out index f13b7f3..cb80cb8 100644 --- a/tests/bfs/color_st0_tw0_ow0.out +++ b/tests/bfs/color_st0_tw0_ow0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/ow rainbow/sticky rainbow/sticky_ow diff --git a/tests/bfs/color_st0_tw_ow.out b/tests/bfs/color_st0_tw_ow.out index 2d7f682..a183932 100644 --- a/tests/bfs/color_st0_tw_ow.out +++ b/tests/bfs/color_st0_tw_ow.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/sticky rainbow/socket rainbow/broken diff --git a/tests/bfs/color_st0_tw_ow0.out b/tests/bfs/color_st0_tw_ow0.out index 7e343b8..cdc5cdd 100644 --- a/tests/bfs/color_st0_tw_ow0.out +++ b/tests/bfs/color_st0_tw_ow0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/ow rainbow/sticky rainbow/socket diff --git a/tests/bfs/color_st_tw0_ow.out b/tests/bfs/color_st_tw0_ow.out index c61a327..f45c75c 100644 --- a/tests/bfs/color_st_tw0_ow.out +++ b/tests/bfs/color_st_tw0_ow.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_st_tw0_ow0.out b/tests/bfs/color_st_tw0_ow0.out index 929a993..d95d12a 100644 --- a/tests/bfs/color_st_tw0_ow0.out +++ b/tests/bfs/color_st_tw0_ow0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/ow rainbow/socket rainbow/broken diff --git a/tests/bfs/color_st_tw_ow0.out b/tests/bfs/color_st_tw_ow0.out index 7092f5a..f1059dd 100644 --- a/tests/bfs/color_st_tw_ow0.out +++ b/tests/bfs/color_st_tw_ow0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/ow rainbow/socket rainbow/broken diff --git a/tests/bfs/color_su0_sg.out b/tests/bfs/color_su0_sg.out index 2bce534..31d5108 100644 --- a/tests/bfs/color_su0_sg.out +++ b/tests/bfs/color_su0_sg.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_su0_sg0.out b/tests/bfs/color_su0_sg0.out index 0c3d757..4804030 100644 --- a/tests/bfs/color_su0_sg0.out +++ b/tests/bfs/color_su0_sg0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_su_sg0.out b/tests/bfs/color_su_sg0.out index 7a70598..3dbc7fe 100644 --- a/tests/bfs/color_su_sg0.out +++ b/tests/bfs/color_su_sg0.out @@ -1,5 +1,7 @@ +$'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh +rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/nocolor.out b/tests/bfs/nocolor.out index 29e7de8..d51d24d 100644 --- a/tests/bfs/nocolor.out +++ b/tests/bfs/nocolor.out @@ -1,4 +1,6 @@ rainbow +rainbow/ +rainbow// rainbow/broken rainbow/chardev_link rainbow/exec.sh diff --git a/tests/gnu/empty_special.out b/tests/gnu/empty_special.out index 3aa57d2..fa35478 100644 --- a/tests/gnu/empty_special.out +++ b/tests/gnu/empty_special.out @@ -1,3 +1,4 @@ +rainbow// rainbow/exec.sh rainbow/file.dat rainbow/file.txt diff --git a/tests/tests.sh b/tests/tests.sh index 46e3e33..66a79a8 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -432,6 +432,7 @@ function make_rainbow() { chmod +t "$1"/sticky* "$XTOUCH" -p "$1"/exec.sh chmod +x "$1"/exec.sh + "$XTOUCH" -p "$1/"$'\e[1m/\e[0m' } make_rainbow "$TMP/rainbow" -- cgit v1.2.3 From 0e4dffeb46f3d8bfe30d60a0cfe1e1fa5a082b13 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 13 Jul 2023 15:45:08 -0400 Subject: color: Don't break up leading and trailing dirs --- src/color.c | 59 +++++++++++++++++++++------------------ tests/bfs/color.out | 2 +- tests/bfs/color_L.out | 2 +- tests/bfs/color_L_ln_target.out | 2 +- tests/bfs/color_L_no_stat.out | 8 +++--- tests/bfs/color_cd0_no.out | 2 +- tests/bfs/color_escapes.out | 2 +- tests/bfs/color_ext.out | 2 +- tests/bfs/color_ext0.out | 2 +- tests/bfs/color_ext_case.out | 2 +- tests/bfs/color_ext_override.out | 2 +- tests/bfs/color_ext_underride.out | 2 +- tests/bfs/color_fi0_no.out | 2 +- tests/bfs/color_fi_no.out | 2 +- tests/bfs/color_ln_target.out | 2 +- tests/bfs/color_ls.out | 16 +++++------ tests/bfs/color_mh.out | 2 +- tests/bfs/color_mh0.out | 2 +- tests/bfs/color_mi.out | 2 +- tests/bfs/color_missing_colon.out | 2 +- tests/bfs/color_no.out | 2 +- tests/bfs/color_no_stat.out | 8 +++--- tests/bfs/color_nul.out | 2 +- tests/bfs/color_or.out | 2 +- tests/bfs/color_or0_mi.out | 2 +- tests/bfs/color_or0_mi0.out | 2 +- tests/bfs/color_or_mi.out | 2 +- tests/bfs/color_or_mi0.out | 2 +- tests/bfs/color_rs_lc_rc_ec.out | 2 +- tests/bfs/color_st0_tw0_ow.out | 4 +-- tests/bfs/color_st0_tw0_ow0.out | 8 +++--- tests/bfs/color_st0_tw_ow.out | 4 +-- tests/bfs/color_st0_tw_ow0.out | 6 ++-- tests/bfs/color_st_tw0_ow.out | 2 +- tests/bfs/color_st_tw0_ow0.out | 4 +-- tests/bfs/color_st_tw_ow0.out | 4 +-- tests/bfs/color_su0_sg.out | 2 +- tests/bfs/color_su0_sg0.out | 2 +- tests/bfs/color_su_sg0.out | 2 +- tests/bfs/printf_color.out | 4 ++- 40 files changed, 95 insertions(+), 88 deletions(-) (limited to 'tests') diff --git a/src/color.c b/src/color.c index 6593f5b..7d98978 100644 --- a/src/color.c +++ b/src/color.c @@ -995,27 +995,48 @@ out: return ret; } -/** Print the directories leading up to a file. */ -static int print_dirs_colored(CFILE *cfile, const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags, size_t nameoff) { - const struct colors *colors = cfile->colors; +/** Print a path with colors. */ +static int print_path_colored(CFILE *cfile, const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { + size_t nameoff; + if (path == ftwbuf->path) { + nameoff = ftwbuf->nameoff; + } else { + nameoff = xbaseoff(path); + } + + const char *name = path + nameoff; + size_t pathlen = nameoff + strlen(name); ssize_t broken = first_broken_offset(path, ftwbuf, flags, nameoff); if (broken < 0) { return -1; } + size_t split = broken; - if (broken > 0) { - if (print_colored(cfile, colors->directory, path, broken) != 0) { - return -1; + const struct colors *colors = cfile->colors; + const struct esc_seq *dirs_color = colors->directory; + const struct esc_seq *name_color; + + if (split < nameoff) { + name_color = colors->missing; + if (!name_color) { + name_color = colors->orphan; + } + } else { + name_color = file_color(cfile->colors, path + nameoff, ftwbuf, flags); + if (name_color == dirs_color) { + split = pathlen; } } - if ((size_t)broken < nameoff) { - const struct esc_seq *color = colors->missing; - if (!color) { - color = colors->orphan; + if (split > 0) { + if (print_colored(cfile, dirs_color, path, split) != 0) { + return -1; } - if (print_colored(cfile, color, path + broken, nameoff - broken) != 0) { + } + + if (split < pathlen) { + if (print_colored(cfile, name_color, path + split, pathlen - split) != 0) { return -1; } } @@ -1029,22 +1050,6 @@ static int print_name_colored(CFILE *cfile, const char *name, const struct BFTW return print_colored(cfile, esc, name, strlen(name)); } -/** Print a path with colors. */ -static int print_path_colored(CFILE *cfile, const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { - size_t nameoff; - if (path == ftwbuf->path) { - nameoff = ftwbuf->nameoff; - } else { - nameoff = xbaseoff(path); - } - - if (print_dirs_colored(cfile, path, ftwbuf, flags, nameoff) != 0) { - return -1; - } - - return print_name_colored(cfile, path + nameoff, ftwbuf, flags); -} - /** Print the name of a file with the appropriate colors. */ static int print_name(CFILE *cfile, const struct BFTW *ftwbuf) { const char *name = ftwbuf->path + ftwbuf->nameoff; diff --git a/tests/bfs/color.out b/tests/bfs/color.out index 5c6b43e..a439814 100644 --- a/tests/bfs/color.out +++ b/tests/bfs/color.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_L.out b/tests/bfs/color_L.out index 6904e39..85923db 100644 --- a/tests/bfs/color_L.out +++ b/tests/bfs/color_L.out @@ -1,8 +1,8 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh rainbow/chardev_link -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/sticky_ow diff --git a/tests/bfs/color_L_ln_target.out b/tests/bfs/color_L_ln_target.out index 50105c3..23fe8d7 100644 --- a/tests/bfs/color_L_ln_target.out +++ b/tests/bfs/color_L_ln_target.out @@ -1,9 +1,9 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh rainbow/chardev_link -rainbow/$'\e[1m' rainbow/socket rainbow/sticky_ow rainbow/sgid diff --git a/tests/bfs/color_L_no_stat.out b/tests/bfs/color_L_no_stat.out index beb538c..72e0319 100644 --- a/tests/bfs/color_L_no_stat.out +++ b/tests/bfs/color_L_no_stat.out @@ -1,10 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/chardev_link -rainbow/$'\e[1m' -rainbow/ow -rainbow/sticky -rainbow/sticky_ow rainbow/socket rainbow/broken rainbow/file.txt @@ -25,3 +22,6 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/ow +rainbow/sticky +rainbow/sticky_ow diff --git a/tests/bfs/color_cd0_no.out b/tests/bfs/color_cd0_no.out index 4d710fd..37b3fbc 100644 --- a/tests/bfs/color_cd0_no.out +++ b/tests/bfs/color_cd0_no.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/file.dat diff --git a/tests/bfs/color_escapes.out b/tests/bfs/color_escapes.out index 746a9ea..0bf9fbb 100644 --- a/tests/bfs/color_escapes.out +++ b/tests/bfs/color_escapes.out @@ -1,7 +1,7 @@ +:$'rainbow/\e[1m' :$'rainbow/\e[1m/'$'\e[0m' :rainbow :rainbow/:exec.sh -:rainbow/:$'\e[1m' :rainbow/:socket :rainbow/:broken :rainbow/:chardev_link diff --git a/tests/bfs/color_ext.out b/tests/bfs/color_ext.out index a35ca0b..218100f 100644 --- a/tests/bfs/color_ext.out +++ b/tests/bfs/color_ext.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext0.out b/tests/bfs/color_ext0.out index 37301cc..d2a7fd5 100644 --- a/tests/bfs/color_ext0.out +++ b/tests/bfs/color_ext0.out @@ -1,8 +1,8 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/file.txt rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext_case.out b/tests/bfs/color_ext_case.out index 9388343..93dc8f6 100644 --- a/tests/bfs/color_ext_case.out +++ b/tests/bfs/color_ext_case.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/lower.gz @@ -7,7 +8,6 @@ rainbow/upper.TAR.GZ rainbow/lower.tar rainbow/upper.TAR -rainbow/$'\e[1m' rainbow/ul.TAR.gz rainbow/lu.tar.GZ rainbow/socket diff --git a/tests/bfs/color_ext_override.out b/tests/bfs/color_ext_override.out index 1e0146c..0acfcbc 100644 --- a/tests/bfs/color_ext_override.out +++ b/tests/bfs/color_ext_override.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh @@ -9,7 +10,6 @@ rainbow/ul.TAR.gz rainbow/upper.GZ rainbow/upper.TAR.GZ -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ext_underride.out b/tests/bfs/color_ext_underride.out index 1ed66da..5c98341 100644 --- a/tests/bfs/color_ext_underride.out +++ b/tests/bfs/color_ext_underride.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/lower.tar.gz @@ -9,7 +10,6 @@ rainbow/upper.TAR rainbow/lower.gz rainbow/upper.GZ -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_fi0_no.out b/tests/bfs/color_fi0_no.out index 5c6b43e..a439814 100644 --- a/tests/bfs/color_fi0_no.out +++ b/tests/bfs/color_fi0_no.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_fi_no.out b/tests/bfs/color_fi_no.out index e64684d..1c1ad8e 100644 --- a/tests/bfs/color_fi_no.out +++ b/tests/bfs/color_fi_no.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_ln_target.out b/tests/bfs/color_ln_target.out index 50105c3..23fe8d7 100644 --- a/tests/bfs/color_ln_target.out +++ b/tests/bfs/color_ln_target.out @@ -1,9 +1,9 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh rainbow/chardev_link -rainbow/$'\e[1m' rainbow/socket rainbow/sticky_ow rainbow/sgid diff --git a/tests/bfs/color_ls.out b/tests/bfs/color_ls.out index b08d894..b5216c4 100644 --- a/tests/bfs/color_ls.out +++ b/tests/bfs/color_ls.out @@ -1,12 +1,12 @@ -scratch/foo/bar -scratch/foo/bar -/__bfs__/nowhere -/__bfs__/nowhere -foo/bar/baz/qux -foo/bar/baz/qux +scratch/foo/bar +scratch/foo/bar +/__bfs__/nowhere +/__bfs__/nowhere +foo/bar/baz/qux +foo/bar/baz/qux foo/bar/nowhere foo/bar/nowhere -foo/bar/nowhere/nothing -foo/bar/nowhere/nothing +foo/bar/nowhere/nothing +foo/bar/nowhere/nothing foo/bar/baz foo/bar/baz diff --git a/tests/bfs/color_mh.out b/tests/bfs/color_mh.out index 93bfde1..c658082 100644 --- a/tests/bfs/color_mh.out +++ b/tests/bfs/color_mh.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_mh0.out b/tests/bfs/color_mh0.out index 5c6b43e..a439814 100644 --- a/tests/bfs/color_mh0.out +++ b/tests/bfs/color_mh0.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_mi.out b/tests/bfs/color_mi.out index 5c6b43e..a439814 100644 --- a/tests/bfs/color_mi.out +++ b/tests/bfs/color_mi.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_missing_colon.out b/tests/bfs/color_missing_colon.out index a35ca0b..218100f 100644 --- a/tests/bfs/color_missing_colon.out +++ b/tests/bfs/color_missing_colon.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_no.out b/tests/bfs/color_no.out index c61d246..67e1eee 100644 --- a/tests/bfs/color_no.out +++ b/tests/bfs/color_no.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_no_stat.out b/tests/bfs/color_no_stat.out index e57e11a..e3031b2 100644 --- a/tests/bfs/color_no_stat.out +++ b/tests/bfs/color_no_stat.out @@ -1,9 +1,6 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow -rainbow/$'\e[1m' -rainbow/ow -rainbow/sticky -rainbow/sticky_ow rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +22,6 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/ow +rainbow/sticky +rainbow/sticky_ow diff --git a/tests/bfs/color_nul.out b/tests/bfs/color_nul.out index 7140e32..8ccd9a7 100644 --- a/tests/bfs/color_nul.out +++ b/tests/bfs/color_nul.out @@ -1,3 +1,4 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/lower.gz @@ -7,7 +8,6 @@ rainbow/upper.GZ rainbow/upper.TAR.GZ rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_or.out b/tests/bfs/color_or.out index 07916da..0bd2570 100644 --- a/tests/bfs/color_or.out +++ b/tests/bfs/color_or.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/chardev_link rainbow/link.txt diff --git a/tests/bfs/color_or0_mi.out b/tests/bfs/color_or0_mi.out index 5c6b43e..a439814 100644 --- a/tests/bfs/color_or0_mi.out +++ b/tests/bfs/color_or0_mi.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_or0_mi0.out b/tests/bfs/color_or0_mi0.out index 5c6b43e..a439814 100644 --- a/tests/bfs/color_or0_mi0.out +++ b/tests/bfs/color_or0_mi0.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_or_mi.out b/tests/bfs/color_or_mi.out index 20bc61d..fb67e58 100644 --- a/tests/bfs/color_or_mi.out +++ b/tests/bfs/color_or_mi.out @@ -1,8 +1,8 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/chardev_link rainbow/link.txt diff --git a/tests/bfs/color_or_mi0.out b/tests/bfs/color_or_mi0.out index 20bc61d..fb67e58 100644 --- a/tests/bfs/color_or_mi0.out +++ b/tests/bfs/color_or_mi0.out @@ -1,8 +1,8 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/broken rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/chardev_link rainbow/link.txt diff --git a/tests/bfs/color_rs_lc_rc_ec.out b/tests/bfs/color_rs_lc_rc_ec.out index f78b41f..077ef8d 100644 --- a/tests/bfs/color_rs_lc_rc_ec.out +++ b/tests/bfs/color_rs_lc_rc_ec.out @@ -1,6 +1,6 @@ +LC01;34RC$'rainbow/\e[1m'EC LC01;34RC$'rainbow/\e[1m/'EC$'\e[0m' LC01;34RCrainbow/ECLC01;32RCexec.shEC -LC01;34RCrainbow/ECLC01;34RC$'\e[1m'EC LC01;34RCrainbow/ECLC01;35RCsocketEC LC01;34RCrainbow/ECLC01;36RCbrokenEC LC01;34RCrainbow/ECLC01;36RCchardev_linkEC diff --git a/tests/bfs/color_st0_tw0_ow.out b/tests/bfs/color_st0_tw0_ow.out index d1fec74..a82762b 100644 --- a/tests/bfs/color_st0_tw0_ow.out +++ b/tests/bfs/color_st0_tw0_ow.out @@ -1,8 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' -rainbow/sticky rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +24,4 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/sticky diff --git a/tests/bfs/color_st0_tw0_ow0.out b/tests/bfs/color_st0_tw0_ow0.out index cb80cb8..041f1d4 100644 --- a/tests/bfs/color_st0_tw0_ow0.out +++ b/tests/bfs/color_st0_tw0_ow0.out @@ -1,10 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' -rainbow/ow -rainbow/sticky -rainbow/sticky_ow rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +22,6 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/ow +rainbow/sticky +rainbow/sticky_ow diff --git a/tests/bfs/color_st0_tw_ow.out b/tests/bfs/color_st0_tw_ow.out index a183932..4dcb2f2 100644 --- a/tests/bfs/color_st0_tw_ow.out +++ b/tests/bfs/color_st0_tw_ow.out @@ -1,8 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' -rainbow/sticky rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +24,4 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/sticky diff --git a/tests/bfs/color_st0_tw_ow0.out b/tests/bfs/color_st0_tw_ow0.out index cdc5cdd..954ce9c 100644 --- a/tests/bfs/color_st0_tw_ow0.out +++ b/tests/bfs/color_st0_tw_ow0.out @@ -1,9 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' -rainbow/ow -rainbow/sticky rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +23,5 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/ow +rainbow/sticky diff --git a/tests/bfs/color_st_tw0_ow.out b/tests/bfs/color_st_tw0_ow.out index f45c75c..a6e9a16 100644 --- a/tests/bfs/color_st_tw0_ow.out +++ b/tests/bfs/color_st_tw0_ow.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_st_tw0_ow0.out b/tests/bfs/color_st_tw0_ow0.out index d95d12a..756dafb 100644 --- a/tests/bfs/color_st_tw0_ow0.out +++ b/tests/bfs/color_st_tw0_ow0.out @@ -1,8 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' -rainbow/ow rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +24,4 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/ow diff --git a/tests/bfs/color_st_tw_ow0.out b/tests/bfs/color_st_tw_ow0.out index f1059dd..6e4a260 100644 --- a/tests/bfs/color_st_tw_ow0.out +++ b/tests/bfs/color_st_tw_ow0.out @@ -1,8 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' -rainbow/ow rainbow/socket rainbow/broken rainbow/chardev_link @@ -25,3 +24,4 @@ rainbow/upper.GZ rainbow/upper.TAR rainbow/upper.TAR.GZ +rainbow/ow diff --git a/tests/bfs/color_su0_sg.out b/tests/bfs/color_su0_sg.out index 31d5108..d13b6b6 100644 --- a/tests/bfs/color_su0_sg.out +++ b/tests/bfs/color_su0_sg.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_su0_sg0.out b/tests/bfs/color_su0_sg0.out index 4804030..77fba58 100644 --- a/tests/bfs/color_su0_sg0.out +++ b/tests/bfs/color_su0_sg0.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/color_su_sg0.out b/tests/bfs/color_su_sg0.out index 3dbc7fe..8fab046 100644 --- a/tests/bfs/color_su_sg0.out +++ b/tests/bfs/color_su_sg0.out @@ -1,7 +1,7 @@ +$'rainbow/\e[1m' $'rainbow/\e[1m/'$'\e[0m' rainbow rainbow/exec.sh -rainbow/$'\e[1m' rainbow/socket rainbow/broken rainbow/chardev_link diff --git a/tests/bfs/printf_color.out b/tests/bfs/printf_color.out index 0468f7d..6641e9a 100644 --- a/tests/bfs/printf_color.out +++ b/tests/bfs/printf_color.out @@ -1,5 +1,7 @@ -. . rainbow ./rainbow rainbow +. $'./rainbow/\e[1m' $'\e[0m' $'./rainbow/\e[1m/'$'\e[0m' $'rainbow/\e[1m/'$'\e[0m' +. . rainbow ./rainbow rainbow . ./rainbow exec.sh ./rainbow/exec.sh rainbow/exec.sh +. ./rainbow $'\e[1m' $'./rainbow/\e[1m' $'rainbow/\e[1m' . ./rainbow socket ./rainbow/socket rainbow/socket . ./rainbow broken ./rainbow/broken rainbow/broken nowhere . ./rainbow chardev_link ./rainbow/chardev_link rainbow/chardev_link /dev/null -- cgit v1.2.3 From b4c3201ccceb9c73dd7751d7f9937b4afe78966f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 13 Jul 2023 16:00:02 -0400 Subject: color: Only highlight the trailing slash on ENOTDIR --- src/color.c | 6 ++++-- tests/bfs/color_ls.out | 4 ++-- tests/bfs/color_ls.sh | 6 +++--- 3 files changed, 9 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/src/color.c b/src/color.c index 7d98978..f7a5d86 100644 --- a/src/color.c +++ b/src/color.c @@ -982,8 +982,10 @@ static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf, while (ret && at_path[len - 1] == '/') { --len, --ret; } - while (ret && at_path[len - 1] != '/') { - --len, --ret; + if (errno != ENOTDIR) { + while (ret && at_path[len - 1] != '/') { + --len, --ret; + } } dstresize(&at_path, len); diff --git a/tests/bfs/color_ls.out b/tests/bfs/color_ls.out index b5216c4..f69eb9c 100644 --- a/tests/bfs/color_ls.out +++ b/tests/bfs/color_ls.out @@ -2,11 +2,11 @@ scratch/foo/bar /__bfs__/nowhere /__bfs__/nowhere -foo/bar/baz/qux -foo/bar/baz/qux foo/bar/nowhere foo/bar/nowhere foo/bar/nowhere/nothing foo/bar/nowhere/nothing foo/bar/baz foo/bar/baz +foo/bar/baz//qux +foo/bar/baz//qux diff --git a/tests/bfs/color_ls.sh b/tests/bfs/color_ls.sh index 37d088f..f2d3c72 100644 --- a/tests/bfs/color_ls.sh +++ b/tests/bfs/color_ls.sh @@ -3,13 +3,13 @@ clean_scratch ln -s foo/bar/baz scratch/link ln -s foo/bar/nowhere scratch/broken ln -s foo/bar/nowhere/nothing scratch/nested -ln -s foo/bar/baz/qux scratch/notdir +ln -s foo/bar/baz//qux scratch/notdir ln -s scratch/foo/bar scratch/relative mkdir scratch/__bfs__ ln -s /__bfs__/nowhere scratch/absolute -LS_COLORS="or=01;31:" invoke_bfs scratch/{,link,broken,nested,notdir,relative,absolute} -color -type l -ls \ +export LS_COLORS="or=01;31:" +invoke_bfs scratch/{,link,broken,nested,notdir,relative,absolute} -color -type l -ls \ | sed 's/.* -> //' \ | sort >"$OUT" - diff_output -- cgit v1.2.3 From 92f0c513ba4e9119c5b6977366ce997151a44977 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 14 Aug 2023 15:59:04 -0400 Subject: parse: Allow -files0-from an empty set of paths This follows a behaviour change in GNU findutils 4.9.0. --- src/parse.c | 13 +++---------- tests/gnu/files0_from_none.sh | 2 +- 2 files changed, 4 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 6b1eaa0..37940de 100644 --- a/src/parse.c +++ b/src/parse.c @@ -194,8 +194,6 @@ struct parser_state { char **mount_arg; /** An "-xdev" argument, if any. */ char **xdev_arg; - /** A "-files0-from" argument, if any. */ - char **files0_arg; /** A "-files0-from -" argument, if any. */ char **files0_stdin_arg; /** An "-ok"-type expression, if any. */ @@ -1318,7 +1316,6 @@ static struct bfs_expr *parse_files0_from(struct parser_state *state, int arg1, return NULL; } - state->files0_arg = expr->argv; const char *from = expr->argv[1]; FILE *file; @@ -1350,7 +1347,7 @@ static struct bfs_expr *parse_files0_from(struct parser_state *state, int arg1, } if (file == stdin) { - state->files0_stdin_arg = state->files0_arg; + state->files0_stdin_arg = expr->argv; } else { fclose(file); } @@ -3720,7 +3717,6 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { .prune_arg = NULL, .mount_arg = NULL, .xdev_arg = NULL, - .files0_arg = NULL, .files0_stdin_arg = NULL, .ok_expr = NULL, .now = ctx->now, @@ -3748,11 +3744,8 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { goto fail; } - if (darray_length(ctx->paths) == 0) { - if (!state.implicit_root) { - parse_argv_error(&state, state.files0_arg, 2, "No root paths specified.\n"); - goto fail; - } else if (parse_root(&state, ".") != 0) { + if (darray_length(ctx->paths) == 0 && state.implicit_root) { + if (parse_root(&state, ".") != 0) { goto fail; } } diff --git a/tests/gnu/files0_from_none.sh b/tests/gnu/files0_from_none.sh index 090fce0..1633163 100644 --- a/tests/gnu/files0_from_none.sh +++ b/tests/gnu/files0_from_none.sh @@ -1 +1 @@ -! printf "" | invoke_bfs -files0-from - +printf "" | bfs_diff -files0-from - -- cgit v1.2.3 From b327e3813162e44e53805c307672dc8dc69eeb06 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 14 Aug 2023 16:05:35 -0400 Subject: tests: Add missing .out file --- tests/gnu/files0_from_none.out | 0 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 tests/gnu/files0_from_none.out (limited to 'tests') diff --git a/tests/gnu/files0_from_none.out b/tests/gnu/files0_from_none.out new file mode 100644 index 0000000..e69de29 -- cgit v1.2.3 From f34baf1a2f42259a4873f6d9733078003184d4e4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 23 Aug 2023 23:19:55 -0400 Subject: Work around https://github.com/llvm/llvm-project/issues/64946 --- src/parse.c | 3 +++ tests/xtouch.c | 3 +++ 2 files changed, 6 insertions(+) (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 37940de..3861610 100644 --- a/src/parse.c +++ b/src/parse.c @@ -582,6 +582,9 @@ static const char *parse_int(const struct parser_state *state, char **arg, const } } + // https://github.com/llvm/llvm-project/issues/64946 + sanitize_init(&endptr); + if (endptr == str) { goto bad; } diff --git a/tests/xtouch.c b/tests/xtouch.c index 50416ba..4a02bf3 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -3,6 +3,7 @@ #include "../src/bfstd.h" #include "../src/config.h" +#include "../src/sanity.h" #include "../src/xtime.h" #include #include @@ -172,6 +173,8 @@ int main(int argc, char *argv[]) { if (marg) { char *end; long mode = strtol(marg, &end, 8); + // https://github.com/llvm/llvm-project/issues/64946 + sanitize_init(&end); if (*marg && !*end && mode >= 0 && mode < 01000) { args.fmode = args.dmode = mode; } else { -- cgit v1.2.3 From 377709664480a30fa5acdd11c7ca8c16669678ce Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 6 Sep 2023 14:59:59 -0400 Subject: bfstd: Fix an OOB string index in xmbrtowc() This bug could be reproduced with something like $ bfs -samefile $'\xFA\xFA' bfs: error: bfs: dstrnescat@src/dstring.c:252: wordesc() result truncated or worse, with -DNDEBUG, $ bfs -samefile $'.....................\xFA\xFA' bfs: error: bfs -samefile $'.....................\xFA\xFA\x00\x55\x53\x45\x52\x3D\x74\x61\x76\x69\x61\x6E\x61\x74\x6F\x72 bfs: error: ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ bfs: error: No such file or directory. which prints the memory after the end of the string (in this case, the environment variable USER=tavianator). The bug was caused by the line `*i += len`, which was intended to be `*i = len`. But actually, the right behaviour seems to be `*i += 1`. Fixes: 19c96abe0a1ee56cf206fd5e87defb1fd3e0daa5 --- src/bfstd.c | 8 ++------ tests/bfstd.c | 18 ++++++++++++++++++ tests/common/samefile_wordesc.sh | 4 ++++ 3 files changed, 24 insertions(+), 6 deletions(-) create mode 100644 tests/common/samefile_wordesc.sh (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index e546a47..2d9f60a 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -546,15 +546,11 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * static int xmbrtowc(wchar_t *wc, size_t *i, const char *str, size_t len, mbstate_t *mb) { size_t mblen = mbrtowc(wc, str + *i, len - *i, mb); switch (mblen) { - case -1: - // Invalid byte sequence + case -1: // Invalid byte sequence + case -2: // Incomplete byte sequence *i += 1; memset(mb, 0, sizeof(*mb)); return -1; - case -2: - // Incomplete byte sequence - *i += len; - return -1; default: *i += mblen; return 0; diff --git a/tests/bfstd.c b/tests/bfstd.c index fa854a8..2db084a 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -23,6 +23,15 @@ static void check_base_dir(const char *path, const char *dir, const char *base) free(xbase); } +/** Check the result of wordesc(). */ +static void check_wordesc(const char *str, const char *exp, enum wesc_flags flags) { + char buf[256]; + char *end = buf + sizeof(buf); + char *ret = wordesc(buf, end, str, flags); + bfs_verify(ret != end); + bfs_verify(strcmp(buf, exp) == 0, "wordesc(%s) == %s (!= %s)", str, buf, exp); +} + int main(void) { // From man 3p basename check_base_dir("usr", ".", "usr"); @@ -36,5 +45,14 @@ int main(void) { check_base_dir("//usr//lib//", "//usr", "lib"); check_base_dir("/home//dwc//test", "/home//dwc", "test"); + check_wordesc("", "\"\"", WESC_SHELL); + check_wordesc("word", "word", WESC_SHELL); + check_wordesc("two words", "\"two words\"", WESC_SHELL); + check_wordesc("word's", "\"word's\"", WESC_SHELL); + check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL); + check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); + check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); + check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); + return EXIT_SUCCESS; } diff --git a/tests/common/samefile_wordesc.sh b/tests/common/samefile_wordesc.sh new file mode 100644 index 0000000..b5d158f --- /dev/null +++ b/tests/common/samefile_wordesc.sh @@ -0,0 +1,4 @@ +# Regression test: don't abort on incomplete UTF-8 sequences +export LC_ALL=$(locale -a | grep -Ei 'utf-?8$' | head -n1) +test -n "$LC_ALL" || skip +! invoke_bfs -samefile $'\xFA\xFA' -- cgit v1.2.3 From 90f9205b40b2f2049df46d819d14d67bfcb055be Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 6 Sep 2023 16:23:10 -0400 Subject: bfstd: Fix printable_len() off-by-one If xmbrtowc() fails, or if xiswprint() is false, then we shouldn't include that wide char in the printable length. Fixes: 19c96abe0a1ee56cf206fd5e87defb1fd3e0daa5 --- src/bfstd.c | 4 ++-- tests/bfstd.c | 15 +++++++++++++++ 2 files changed, 17 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index 2d9f60a..61d2fee 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -666,9 +666,9 @@ static size_t printable_len(const char *str, size_t len, enum wesc_flags flags) multibyte: memset(&mb, 0, sizeof(mb)); - while (i < len) { + for (size_t j = i; i < len; i = j) { wchar_t wc; - if (xmbrtowc(&wc, &i, str, len, &mb) != 0) { + if (xmbrtowc(&wc, &j, str, len, &mb) != 0) { break; } if (!xiswprint(wc, flags)) { diff --git a/tests/bfstd.c b/tests/bfstd.c index 2db084a..83964e5 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -5,6 +5,8 @@ #include "../src/config.h" #include "../src/diag.h" #include +#include +#include #include #include #include @@ -33,6 +35,11 @@ static void check_wordesc(const char *str, const char *exp, enum wesc_flags flag } int main(void) { + // Try to set a UTF-8 locale + if (!setlocale(LC_ALL, "C.UTF-8")) { + setlocale(LC_ALL, ""); + } + // From man 3p basename check_base_dir("usr", ".", "usr"); check_base_dir("usr/", ".", "usr"); @@ -54,5 +61,13 @@ int main(void) { check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); + const char *charmap = nl_langinfo(CODESET); + if (strcmp(charmap, "UTF-8") == 0) { + check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY); + check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); + check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); + check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); + } + return EXIT_SUCCESS; } -- cgit v1.2.3 From 76253d272c50ba08002a47395e47a4f9ce4fb53e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 25 Sep 2023 13:58:19 -0400 Subject: Use the new list macros --- src/bftw.c | 24 +++++++++++------------- src/color.c | 2 +- src/ctx.c | 4 ++-- src/trie.h | 7 +++---- src/xspawn.c | 11 +++++------ tests/trie.c | 4 ++-- 6 files changed, 24 insertions(+), 28 deletions(-) (limited to 'tests') diff --git a/src/bftw.c b/src/bftw.c index e6b8cd5..f3060ce 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -318,8 +318,7 @@ static size_t bftw_child_nameoff(const struct bftw_file *parent) { /** Destroy a cache. */ static void bftw_cache_destroy(struct bftw_cache *cache) { - bfs_assert(!cache->head); - bfs_assert(!cache->tail); + bfs_assert(LIST_EMPTY(cache)); bfs_assert(!cache->target); varena_destroy(&cache->files); @@ -347,9 +346,9 @@ static struct bftw_file *bftw_file_new(struct bftw_cache *cache, struct bftw_fil file->nameoff = 0; } - file->next = NULL; - file->to_read.next = NULL; - file->lru.prev = file->lru.next = NULL; + SLIST_ITEM_INIT(file); + SLIST_ITEM_INIT(file, to_read); + LIST_ITEM_INIT(file, lru); file->refcount = 1; file->pincount = 0; @@ -833,12 +832,11 @@ static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) { SLIST_APPEND(&state->to_read, file, to_read); } - while (state->to_open.head) { - if (bftw_ioq_opendir(state, state->to_open.head) == 0) { - SLIST_POP(&state->to_open); - } else { + for_slist (struct bftw_file, dir, &state->to_open) { + if (bftw_ioq_opendir(state, dir) != 0) { break; } + SLIST_POP(&state->to_open); } } @@ -847,7 +845,7 @@ static bool bftw_pop_dir(struct bftw_state *state) { bfs_assert(!state->file); struct bftw_cache *cache = &state->cache; - bool have_files = state->to_visit.head; + bool have_files = !SLIST_EMPTY(&state->to_visit); if (state->flags & BFTW_SORT) { // Keep strict breadth-first order when sorting @@ -855,9 +853,9 @@ static bool bftw_pop_dir(struct bftw_state *state) { return false; } } else { - while (!state->to_read.head) { + while (SLIST_EMPTY(&state->to_read)) { // Block if we have no other files/dirs to visit, or no room in the cache - bool have_dirs = state->to_open.head; + bool have_dirs = !SLIST_EMPTY(&state->to_open); bool have_room = cache->capacity > 0 && cache->dirlimit > 0; bool block = !(have_dirs || have_files) || !have_room; @@ -1303,7 +1301,7 @@ static void bftw_list_sort(struct bftw_list *list) { bftw_list_sort(&right); // Merge - while (left.head && right.head) { + while (!SLIST_EMPTY(&left) && !SLIST_EMPTY(&right)) { struct bftw_file *lf = left.head; struct bftw_file *rf = right.head; diff --git a/src/color.c b/src/color.c index b9a788b..5e78c6c 100644 --- a/src/color.c +++ b/src/color.c @@ -328,7 +328,7 @@ fail: static int build_iext_trie(struct colors *colors) { trie_clear(&colors->iext_trie); - TRIE_FOR_EACH(&colors->ext_trie, leaf) { + for_trie (leaf, &colors->ext_trie) { size_t len = leaf->length - 1; if (colors->ext_len < len) { colors->ext_len = len; diff --git a/src/ctx.c b/src/ctx.c index a940bed..3a44e68 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -152,7 +152,7 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) { // - the user sees everything relevant before an -ok[dir] prompt // - output from commands is interleaved consistently with bfs // - executed commands can rely on I/O from other bfs actions - TRIE_FOR_EACH(&ctx->files, leaf) { + for_trie (leaf, &ctx->files) { struct bfs_ctx_file *ctx_file = leaf->value; CFILE *cfile = ctx_file->cfile; if (fflush(cfile->file) == 0) { @@ -239,7 +239,7 @@ int bfs_ctx_free(struct bfs_ctx *ctx) { bfs_groups_free(ctx->groups); bfs_users_free(ctx->users); - TRIE_FOR_EACH(&ctx->files, leaf) { + for_trie (leaf, &ctx->files) { struct bfs_ctx_file *ctx_file = leaf->value; if (ctx_file->error) { diff --git a/src/trie.h b/src/trie.h index dfaae15..2f51db5 100644 --- a/src/trie.h +++ b/src/trie.h @@ -6,6 +6,7 @@ #include "config.h" #include "alloc.h" +#include "list.h" #include #include @@ -141,9 +142,7 @@ void trie_destroy(struct trie *trie); /** * Iterate over the leaves of a trie. */ -#define TRIE_FOR_EACH(trie, leaf) \ - for (struct trie_leaf *leaf = (trie)->head, *_next; \ - leaf && (_next = leaf->next, true); \ - leaf = _next) +#define for_trie(leaf, trie) \ + for_list(struct trie_leaf, leaf, trie) #endif // BFS_TRIE_H diff --git a/src/xspawn.c b/src/xspawn.c index 2cabdcc..80bafef 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -49,8 +49,8 @@ int bfs_spawn_init(struct bfs_spawn *ctx) { } int bfs_spawn_destroy(struct bfs_spawn *ctx) { - while (ctx->head) { - free(SLIST_POP(ctx)); + for_slist (struct bfs_spawn_action, action, ctx) { + free(action); } return 0; @@ -68,7 +68,7 @@ static struct bfs_spawn_action *bfs_spawn_add(struct bfs_spawn *ctx, enum bfs_sp return NULL; } - action->next = NULL; + SLIST_ITEM_INIT(action); action->op = op; action->in_fd = -1; action->out_fd = -1; @@ -138,7 +138,7 @@ int bfs_spawn_addsetrlimit(struct bfs_spawn *ctx, int resource, const struct rli static void bfs_spawn_exec(const char *exe, const struct bfs_spawn *ctx, char **argv, char **envp, int pipefd[2]) { xclose(pipefd[0]); - for (const struct bfs_spawn_action *action = ctx ? ctx->head : NULL; action; action = action->next) { + for_slist (const struct bfs_spawn_action, action, ctx) { // Move the error-reporting pipe out of the way if necessary... if (action->out_fd == pipefd[1]) { int fd = dup_cloexec(pipefd[1]); @@ -199,9 +199,8 @@ pid_t bfs_spawn(const char *exe, const struct bfs_spawn *ctx, char **argv, char envp = environ; } - enum bfs_spawn_flags flags = ctx ? ctx->flags : 0; char *resolved = NULL; - if (flags & BFS_SPAWN_USEPATH) { + if (ctx->flags & BFS_SPAWN_USEPATH) { exe = resolved = bfs_spawn_resolve(exe); if (!resolved) { return -1; diff --git a/tests/trie.c b/tests/trie.c index e687f96..6ea94a2 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -70,7 +70,7 @@ int main(void) { { size_t i = 0; - TRIE_FOR_EACH(&trie, leaf) { + for_trie (leaf, &trie) { bfs_verify(leaf == trie_find_str(&trie, keys[i])); bfs_verify(!leaf->prev || leaf->prev->next == leaf); bfs_verify(!leaf->next || leaf->next->prev == leaf); @@ -107,7 +107,7 @@ int main(void) { } } - TRIE_FOR_EACH(&trie, leaf) { + for_trie (leaf, &trie) { bfs_verify(false); } -- cgit v1.2.3 From 52de184ba28551734e1cb13233588504ab5f62ec Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Sep 2023 12:11:15 -0400 Subject: Formatting fixes --- src/color.c | 6 +++--- src/ctx.c | 1 - src/darray.c | 4 ++-- src/diag.c | 8 ++++---- src/diag.h | 2 +- src/dstring.c | 4 ++-- src/eval.c | 15 +++++++-------- src/exec.c | 16 ++++++++-------- src/fsade.c | 6 +++--- src/ioq.c | 2 +- src/opt.c | 11 +++++------ src/opt.h | 1 - src/parse.c | 45 ++++++++++++++++++++++----------------------- src/printf.c | 46 +++++++++++++++++++++++----------------------- src/pwcache.c | 1 - src/stat.c | 2 +- src/trie.c | 2 +- src/trie.h | 4 ++-- src/xregex.c | 2 +- src/xspawn.c | 2 +- src/xtime.c | 12 ++++++------ tests/bfstd.c | 2 +- tests/mksock.c | 2 +- tests/trie.c | 4 ++-- tests/xtimegm.c | 6 +++--- 25 files changed, 100 insertions(+), 106 deletions(-) (limited to 'tests') diff --git a/src/color.c b/src/color.c index 8d0b995..788d35d 100644 --- a/src/color.c +++ b/src/color.c @@ -1143,11 +1143,11 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose) { if (verbose) { double rate = 0.0, time = 0.0; if (expr->evaluations) { - rate = 100.0*expr->successes/expr->evaluations; - time = (1.0e9*expr->elapsed.tv_sec + expr->elapsed.tv_nsec)/expr->evaluations; + rate = 100.0 * expr->successes / expr->evaluations; + time = (1.0e9 * expr->elapsed.tv_sec + expr->elapsed.tv_nsec) / expr->evaluations; } if (cbuff(cfile, " [${ylw}%zu${rs}/${ylw}%zu${rs}=${ylw}%g%%${rs}; ${ylw}%gns${rs}]", - expr->successes, expr->evaluations, rate, time)) { + expr->successes, expr->evaluations, rate, time)) { return -1; } } diff --git a/src/ctx.c b/src/ctx.c index 3a44e68..9a24a33 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -168,7 +168,6 @@ void bfs_ctx_flush(const struct bfs_ctx *ctx) { } else if (cfile == ctx->cout) { bfs_error(ctx, "(standard output): %m.\n"); } - } // Flush the user/group caches, in case the executed command edits the diff --git a/src/darray.c b/src/darray.c index 42b8397..3e66a55 100644 --- a/src/darray.c +++ b/src/darray.c @@ -55,7 +55,7 @@ void *darray_push(void *da, const void *item, size_t size) { size_t i = header->length++; if (i >= capacity) { capacity *= 2; - header = realloc(header, sizeof(*header) + capacity*size); + header = realloc(header, sizeof(*header) + capacity * size); if (!header) { // This failure will be detected by darray_check() return da; @@ -64,7 +64,7 @@ void *darray_push(void *da, const void *item, size_t size) { } char *data = darray_data(header); - memcpy(data + i*size, item, size); + memcpy(data + i * size, item, size); return data; } diff --git a/src/diag.c b/src/diag.c index fa9db39..fa66525 100644 --- a/src/diag.c +++ b/src/diag.c @@ -4,9 +4,9 @@ #include "diag.h" #include "alloc.h" #include "bfstd.h" -#include "ctx.h" #include "color.h" #include "config.h" +#include "ctx.h" #include "dstring.h" #include "expr.h" #include @@ -31,14 +31,14 @@ void bfs_perror(const struct bfs_ctx *ctx, const char *str) { bfs_error(ctx, "%s: %m.\n", str); } -void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { +void bfs_error(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); bfs_verror(ctx, format, args); va_end(args); } -bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { +bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { va_list args; va_start(args, format); bool ret = bfs_vwarning(ctx, format, args); @@ -46,7 +46,7 @@ bool bfs_warning(const struct bfs_ctx *ctx, const char *format, ...) { return ret; } -bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...) { +bool bfs_debug(const struct bfs_ctx *ctx, enum debug_flags flag, const char *format, ...) { va_list args; va_start(args, format); bool ret = bfs_vdebug(ctx, flag, format, args); diff --git a/src/diag.h b/src/diag.h index e019db0..fea8847 100644 --- a/src/diag.h +++ b/src/diag.h @@ -8,8 +8,8 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "ctx.h" #include "config.h" +#include "ctx.h" #include /** diff --git a/src/dstring.c b/src/dstring.c index ef4e733..f947741 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -169,7 +169,7 @@ char *dstrprintf(const char *format, ...) { char *dstrvprintf(const char *format, va_list args) { // Guess a capacity to try to avoid reallocating - dchar *str = dstralloc(2*strlen(format)); + dchar *str = dstralloc(2 * strlen(format)); if (!str) { return NULL; } @@ -195,7 +195,7 @@ int dstrcatf(dchar **str, const char *format, ...) { int dstrvcatf(dchar **str, const char *format, va_list args) { // Guess a capacity to try to avoid calling vsnprintf() twice size_t len = dstrlen(*str); - dstreserve(str, len + 2*strlen(format)); + dstreserve(str, len + 2 * strlen(format)); size_t cap = dstrheader(*str)->capacity; va_list copy; diff --git a/src/eval.c b/src/eval.c index 3550751..9f4896a 100644 --- a/src/eval.c +++ b/src/eval.c @@ -239,7 +239,7 @@ bool eval_time(const struct bfs_expr *expr, struct bfs_eval *state) { time_t diff = timespec_diff(&expr->reftime, time); switch (expr->time_unit) { case BFS_DAYS: - diff /= 60*24; + diff /= 60 * 24; fallthru; case BFS_MINUTES: diff /= 60; @@ -271,7 +271,7 @@ bool eval_used(const struct bfs_expr *expr, struct bfs_eval *state) { return false; } - long long day_seconds = 60*60*24; + long long day_seconds = 60 * 60 * 24; diff = (diff + day_seconds - 1) / day_seconds; return bfs_expr_cmp(expr, diff); } @@ -685,7 +685,7 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { uintmax_t ino = statbuf->ino; uintmax_t block_size = ctx->posixly_correct ? 512 : 1024; - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + block_size - 1)/block_size; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + block_size - 1) / block_size; char mode[11]; xstrmode(statbuf->mode, mode); char acl = bfs_check_acl(ftwbuf) > 0 ? '+' : ' '; @@ -721,8 +721,8 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { time_t time = statbuf->mtime.tv_sec; time_t now = ctx->now.tv_sec; - time_t six_months_ago = now - 6*30*24*60*60; - time_t tomorrow = now + 24*60*60; + time_t six_months_ago = now - 6 * 30 * 24 * 60 * 60; + time_t tomorrow = now + 24 * 60 * 60; struct tm tm; if (xlocaltime(&time, &tm) != 0) { goto error; @@ -823,7 +823,6 @@ bool eval_fprintx(const struct bfs_expr *expr, struct bfs_eval *state) { ++path; } - if (fputc('\n', file) == EOF) { goto error; } @@ -905,7 +904,7 @@ bool eval_size(const struct bfs_expr *expr, struct bfs_eval *state) { }; off_t scale = scales[expr->size_unit]; - off_t size = (statbuf->size + scale - 1)/scale; // Round up + off_t size = (statbuf->size + scale - 1) / scale; // Round up return bfs_expr_cmp(expr, size); } @@ -918,7 +917,7 @@ bool eval_sparse(const struct bfs_expr *expr, struct bfs_eval *state) { return false; } - blkcnt_t expected = (statbuf->size + BFS_STAT_BLKSIZE - 1)/BFS_STAT_BLKSIZE; + blkcnt_t expected = (statbuf->size + BFS_STAT_BLKSIZE - 1) / BFS_STAT_BLKSIZE; return statbuf->blocks < expected; } diff --git a/src/exec.c b/src/exec.c index 7b55522..0e0d585 100644 --- a/src/exec.c +++ b/src/exec.c @@ -5,9 +5,9 @@ #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "ctx.h" #include "color.h" #include "config.h" +#include "ctx.h" #include "diag.h" #include "dstring.h" #include "xspawn.h" @@ -348,7 +348,7 @@ static int bfs_exec_spawn(const struct bfs_exec *execbuf) { if (execbuf->flags & BFS_EXEC_MULTI) { bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments] (size %zu)\n", - execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size); + execbuf->argv[0], execbuf->argc - 1, execbuf->arg_size); } else { bfs_exec_debug(execbuf, "Executing '%s' ... [%zu arguments]\n", execbuf->argv[0], execbuf->argc - 1); } @@ -471,7 +471,7 @@ static bool bfs_exec_args_remain(const struct bfs_exec *execbuf) { static size_t bfs_exec_estimate_max(const struct bfs_exec *execbuf) { size_t min = execbuf->arg_min; size_t max = execbuf->arg_max; - return min + (max - min)/2; + return min + (max - min) / 2; } /** Update the ARG_MAX lower bound from a successful execution. */ @@ -486,7 +486,7 @@ static void bfs_exec_update_min(struct bfs_exec *execbuf) { size_t estimate = bfs_exec_estimate_max(execbuf); bfs_exec_debug(execbuf, "ARG_MAX between [%zu, %zu], trying %zu\n", - execbuf->arg_min, execbuf->arg_max, estimate); + execbuf->arg_min, execbuf->arg_max, estimate); } } @@ -502,7 +502,7 @@ static size_t bfs_exec_update_max(struct bfs_exec *execbuf) { // Trim a fraction off the max size to avoid repeated failures near the // top end of the working range - size -= size/16; + size -= size / 16; if (size < execbuf->arg_max) { execbuf->arg_max = size; @@ -515,7 +515,7 @@ static size_t bfs_exec_update_max(struct bfs_exec *execbuf) { // Binary search for a more precise bound size_t estimate = bfs_exec_estimate_max(execbuf); bfs_exec_debug(execbuf, "ARG_MAX between [%zu, %zu], trying %zu\n", - execbuf->arg_min, execbuf->arg_max, estimate); + execbuf->arg_min, execbuf->arg_max, estimate); return estimate; } @@ -589,7 +589,7 @@ static bool bfs_exec_would_overflow(const struct bfs_exec *execbuf, const char * size_t next_size = execbuf->arg_size + bfs_exec_arg_size(arg); if (next_size > arg_max) { bfs_exec_debug(execbuf, "Command size (%zu) would exceed maximum (%zu), executing buffered command\n", - next_size, arg_max); + next_size, arg_max); return true; } @@ -601,7 +601,7 @@ static int bfs_exec_push(struct bfs_exec *execbuf, char *arg) { execbuf->argv[execbuf->argc] = arg; if (execbuf->argc + 1 >= execbuf->argv_cap) { - size_t cap = 2*execbuf->argv_cap; + size_t cap = 2 * execbuf->argv_cap; char **argv = realloc(execbuf->argv, sizeof_array(char *, cap)); if (!argv) { return -1; diff --git a/src/fsade.c b/src/fsade.c index 8dec5a8..cbff47b 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -3,9 +3,9 @@ #include "fsade.h" #include "atomic.h" -#include "config.h" #include "bfstd.h" #include "bftw.h" +#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" @@ -292,7 +292,7 @@ int bfs_check_xattrs(const struct BFTW *ftwbuf) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_list)(const char *, int, void*, size_t) = + ssize_t (*extattr_list)(const char *, int, void *, size_t) = ftwbuf->type == BFS_LNK ? extattr_list_link : extattr_list_file; len = extattr_list(path, EXTATTR_NAMESPACE_SYSTEM, NULL, 0); @@ -331,7 +331,7 @@ int bfs_check_xattr_named(const struct BFTW *ftwbuf, const char *name) { ssize_t len; #if BFS_USE_SYS_EXTATTR_H - ssize_t (*extattr_get)(const char *, int, const char *, void*, size_t) = + ssize_t (*extattr_get)(const char *, int, const char *, void *, size_t) = ftwbuf->type == BFS_LNK ? extattr_get_link : extattr_get_file; len = extattr_get(path, EXTATTR_NAMESPACE_SYSTEM, name, NULL, 0); diff --git a/src/ioq.c b/src/ioq.c index f7ca8c6..d3ba2de 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -9,8 +9,8 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "thread.h" #include "sanity.h" +#include "thread.h" #include #include #include diff --git a/src/opt.c b/src/opt.c index 14de081..77c2798 100644 --- a/src/opt.c +++ b/src/opt.c @@ -535,8 +535,8 @@ static struct bfs_expr *optimize_and_expr(const struct opt_state *state, struct expr->pure = lhs->pure && rhs->pure; expr->always_true = lhs->always_true && rhs->always_true; expr->always_false = lhs->always_false || rhs->always_false; - expr->cost = lhs->cost + lhs->probability*rhs->cost; - expr->probability = lhs->probability*rhs->probability; + expr->cost = lhs->cost + lhs->probability * rhs->cost; + expr->probability = lhs->probability * rhs->probability; return expr; } @@ -606,8 +606,8 @@ static struct bfs_expr *optimize_or_expr(const struct opt_state *state, struct b expr->pure = lhs->pure && rhs->pure; expr->always_true = lhs->always_true || rhs->always_true; expr->always_false = lhs->always_false && rhs->always_false; - expr->cost = lhs->cost + (1 - lhs->probability)*rhs->cost; - expr->probability = lhs->probability + rhs->probability - lhs->probability*rhs->probability; + expr->cost = lhs->cost + (1 - lhs->probability) * rhs->cost; + expr->probability = lhs->probability + rhs->probability - lhs->probability * rhs->probability; return expr; } @@ -1078,7 +1078,6 @@ static const struct { {eval_xattrname, 0.01}, }; - /** * Table of simple predicates. */ @@ -1331,7 +1330,7 @@ static bool reorder_expr_recursive(const struct opt_state *state, struct bfs_exp if (expr->eval_fn == eval_and || expr->eval_fn == eval_or) { if (lhs->pure && rhs->pure) { float rhs_prob = expr->eval_fn == eval_and ? rhs->probability : 1.0 - rhs->probability; - float swapped_cost = rhs->cost + rhs_prob*lhs->cost; + float swapped_cost = rhs->cost + rhs_prob * lhs->cost; ret |= reorder_expr(state, expr, swapped_cost); } } diff --git a/src/opt.h b/src/opt.h index 28cadb9..4aac129 100644 --- a/src/opt.h +++ b/src/opt.h @@ -21,4 +21,3 @@ struct bfs_ctx; int bfs_optimize(struct bfs_ctx *ctx); #endif // BFS_OPT_H - diff --git a/src/parse.c b/src/parse.c index 3416d9e..7766a7b 100644 --- a/src/parse.c +++ b/src/parse.c @@ -41,8 +41,8 @@ #include #include #include -#include #include +#include #include #include #include @@ -1797,10 +1797,10 @@ static int parse_reftime(const struct parser_state *state, struct bfs_expr *expr #else int gmtoff = -timezone; #endif - int tz_hour = gmtoff/3600; - int tz_min = (labs(gmtoff)/60)%60; + int tz_hour = gmtoff / 3600; + int tz_min = (labs(gmtoff) / 60) % 60; fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n", - year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); + year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); if (xgmtime(&state->now.tv_sec, &tm) != 0) { parse_perror(state, "xgmtime()"); @@ -1832,8 +1832,8 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int expr->stat_field = parse_newerxy_field(arg[6]); if (!expr->stat_field) { parse_expr_error(state, expr, - "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", - arg[6]); + "For ${blu}-newer${bld}XY${rs}, ${bld}X${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, or ${bld}B${rs}, not ${err}%c${rs}.\n", + arg[6]); goto fail; } @@ -1845,8 +1845,8 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int enum bfs_stat_field field = parse_newerxy_field(arg[7]); if (!field) { parse_expr_error(state, expr, - "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", - arg[7]); + "For ${blu}-newer${bld}XY${rs}, ${bld}Y${rs} should be ${bld}a${rs}, ${bld}c${rs}, ${bld}m${rs}, ${bld}B${rs}, or ${bld}t${rs}, not ${err}%c${rs}.\n", + arg[7]); goto fail; } @@ -1855,7 +1855,6 @@ static struct bfs_expr *parse_newerxy(struct parser_state *state, int arg1, int goto fail; } - const struct timespec *reftime = bfs_stat_time(&sb, field); if (!reftime) { parse_expr_error(state, expr, "Couldn't get file %s.\n", bfs_stat_field_name(field)); @@ -1905,7 +1904,7 @@ static struct bfs_expr *parse_nohidden(struct parser_state *state, int arg1, int */ static struct bfs_expr *parse_noleaf(struct parser_state *state, int arg1, int arg2) { parse_warning(state, "${ex}%s${rs} does not apply the optimization that ${blu}%s${rs} inhibits.\n\n", - BFS_COMMAND, state->argv[0]); + BFS_COMMAND, state->argv[0]); return parse_nullary_option(state); } @@ -2730,12 +2729,12 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg } cfprintf(cout, "Usage: ${ex}%s${rs} [${cyn}flags${rs}...] [${mag}paths${rs}...] [${blu}expression${rs}...]\n\n", - state->command); + state->command); cfprintf(cout, "${ex}%s${rs} is compatible with ${ex}find${rs}, with some extensions. " - "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n" - "and ${blu}expressions${rs} may be freely mixed in any order.\n\n", - BFS_COMMAND); + "${cyn}Flags${rs} (${cyn}-H${rs}/${cyn}-L${rs}/${cyn}-P${rs} etc.), ${mag}paths${rs},\n" + "and ${blu}expressions${rs} may be freely mixed in any order.\n\n", + BFS_COMMAND); cfprintf(cout, "${bld}Flags:${rs}\n\n"); @@ -2807,7 +2806,7 @@ static struct bfs_expr *parse_help(struct parser_state *state, int arg1, int arg cfprintf(cout, " ${blu}-ignore_readdir_race${rs}\n"); cfprintf(cout, " ${blu}-noignore_readdir_race${rs}\n"); cfprintf(cout, " Whether to report an error if ${ex}%s${rs} detects that the file tree is modified\n", - BFS_COMMAND); + BFS_COMMAND); cfprintf(cout, " during the search (default: ${blu}-noignore_readdir_race${rs})\n"); cfprintf(cout, " ${blu}-maxdepth${rs} ${bld}N${rs}\n"); cfprintf(cout, " ${blu}-mindepth${rs} ${bld}N${rs}\n"); @@ -3462,14 +3461,14 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { if (state->mount_arg && state->xdev_arg) { parse_conflict_warning(state, state->mount_arg, 1, state->xdev_arg, 1, - "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", - state->xdev_arg[0], state->mount_arg[0]); + "${blu}%s${rs} is redundant in the presence of ${blu}%s${rs}.\n\n", + state->xdev_arg[0], state->mount_arg[0]); } if (state->ctx->warn && state->depth_arg && state->prune_arg) { parse_conflict_warning(state, state->depth_arg, 1, state->prune_arg, 1, - "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", - state->prune_arg[0], state->depth_arg[0]); + "${blu}%s${rs} does not work in the presence of ${blu}%s${rs}.\n", + state->prune_arg[0], state->depth_arg[0]); if (state->interactive) { bfs_warning(state->ctx, "Do you want to continue? "); @@ -3483,8 +3482,8 @@ static struct bfs_expr *parse_whole_expr(struct parser_state *state) { if (state->ok_expr && state->files0_stdin_arg) { parse_conflict_error(state, state->ok_expr->argv, state->ok_expr->argc, state->files0_stdin_arg, 2, - "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", - state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]); + "${blu}%s${rs} conflicts with ${blu}%s${rs} ${bld}%s${rs}.\n", + state->ok_expr->argv[0], state->files0_stdin_arg[0], state->files0_stdin_arg[1]); goto fail; } @@ -3644,7 +3643,7 @@ void bfs_ctx_dump(const struct bfs_ctx *ctx, enum debug_flags flag) { static void dump_costs(const struct bfs_ctx *ctx) { const struct bfs_expr *expr = ctx->expr; bfs_debug(ctx, DEBUG_COST, " Cost: ~${ylw}%g${rs}\n", expr->cost); - bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0*expr->probability); + bfs_debug(ctx, DEBUG_COST, "Probability: ~${ylw}%g%%${rs}\n", 100.0 * expr->probability); } struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { @@ -3654,7 +3653,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { goto fail; } - static char* default_argv[] = {BFS_COMMAND, NULL}; + static char *default_argv[] = {BFS_COMMAND, NULL}; if (argc < 1) { argc = 1; argv = default_argv; diff --git a/src/printf.c b/src/printf.c index 5de5a28..98bcb0f 100644 --- a/src/printf.c +++ b/src/printf.c @@ -113,14 +113,14 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_printf *directive, co } BFS_PRINTF_BUF(buf, "%s %s %2d %.2d:%.2d:%.2d.%09ld0 %4d", - days[tm.tm_wday], - months[tm.tm_mon], - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec, - 1900 + tm.tm_year); + days[tm.tm_wday], + months[tm.tm_mon], + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec, + 1900 + tm.tm_year); return dyn_fprintf(cfile->file, directive, buf); } @@ -152,19 +152,19 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; case '+': ret = snprintf(buf, sizeof(buf), "%4d-%.2d-%.2d+%.2d:%.2d:%.2d.%09ld0", - 1900 + tm.tm_year, - tm.tm_mon + 1, - tm.tm_mday, - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec); + 1900 + tm.tm_year, + tm.tm_mon + 1, + tm.tm_mday, + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec); break; case 'k': ret = snprintf(buf, sizeof(buf), "%2d", tm.tm_hour); break; case 'l': - ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11)%12 + 1); + ret = snprintf(buf, sizeof(buf), "%2d", (tm.tm_hour + 11) % 12 + 1); break; case 's': ret = snprintf(buf, sizeof(buf), "%lld", (long long)ts->tv_sec); @@ -174,10 +174,10 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_printf *directive, break; case 'T': ret = snprintf(buf, sizeof(buf), "%.2d:%.2d:%.2d.%09ld0", - tm.tm_hour, - tm.tm_min, - tm.tm_sec, - (long)ts->tv_nsec); + tm.tm_hour, + tm.tm_min, + tm.tm_sec, + (long)ts->tv_nsec); break; // POSIX strftime() features @@ -202,7 +202,7 @@ static int bfs_printf_b(CFILE *cfile, const struct bfs_printf *directive, const return -1; } - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 511)/512; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 511) / 512; BFS_PRINTF_BUF(buf, "%ju", blocks); return dyn_fprintf(cfile->file, directive, buf); } @@ -338,7 +338,7 @@ static int bfs_printf_k(CFILE *cfile, const struct bfs_printf *directive, const return -1; } - uintmax_t blocks = ((uintmax_t)statbuf->blocks*BFS_STAT_BLKSIZE + 1023)/1024; + uintmax_t blocks = ((uintmax_t)statbuf->blocks * BFS_STAT_BLKSIZE + 1023) / 1024; BFS_PRINTF_BUF(buf, "%ju", blocks); return dyn_fprintf(cfile->file, directive, buf); } @@ -452,7 +452,7 @@ static int bfs_printf_S(CFILE *cfile, const struct bfs_printf *directive, const if (statbuf->size == 0 && statbuf->blocks == 0) { sparsity = 1.0; } else { - sparsity = (double)BFS_STAT_BLKSIZE*statbuf->blocks/statbuf->size; + sparsity = (double)BFS_STAT_BLKSIZE * statbuf->blocks / statbuf->size; } return dyn_fprintf(cfile->file, directive, sparsity); } diff --git a/src/pwcache.c b/src/pwcache.c index 0e2f5c1..c728ba9 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -4,7 +4,6 @@ #include "pwcache.h" #include "alloc.h" #include "config.h" -#include "darray.h" #include "trie.h" #include #include diff --git a/src/stat.c b/src/stat.c index e8f48ee..d7387c6 100644 --- a/src/stat.c +++ b/src/stat.c @@ -10,8 +10,8 @@ #include #include #include -#include #include +#include #if defined(STATX_BASIC_STATS) && (!__ANDROID__ || __ANDROID_API__ >= 30) # define BFS_HAS_LIBC_STATX true diff --git a/src/trie.c b/src/trie.c index 77aa2d0..55544e6 100644 --- a/src/trie.c +++ b/src/trie.c @@ -700,7 +700,7 @@ static void trie_remove_impl(struct trie *trie, struct trie_leaf *leaf) { } if (child_index < parent_size) { - memmove(child, child + 1, (parent_size - child_index)*sizeof(*child)); + memmove(child, child + 1, (parent_size - child_index) * sizeof(*child)); } if (has_single_bit(parent_size)) { diff --git a/src/trie.h b/src/trie.h index 2f51db5..02088f1 100644 --- a/src/trie.h +++ b/src/trie.h @@ -4,8 +4,8 @@ #ifndef BFS_TRIE_H #define BFS_TRIE_H -#include "config.h" #include "alloc.h" +#include "config.h" #include "list.h" #include #include @@ -143,6 +143,6 @@ void trie_destroy(struct trie *trie); * Iterate over the leaves of a trie. */ #define for_trie(leaf, trie) \ - for_list(struct trie_leaf, leaf, trie) + for_list (struct trie_leaf, leaf, trie) #endif // BFS_TRIE_H diff --git a/src/xregex.c b/src/xregex.c index beb6676..b9c04bf 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -5,8 +5,8 @@ #include "alloc.h" #include "config.h" #include "diag.h" -#include "thread.h" #include "sanity.h" +#include "thread.h" #include #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 80bafef..7fb63e0 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -187,7 +187,7 @@ fail: // In case of a write error, the parent will still see that we exited // unsuccessfully, but won't know why - (void) xwrite(pipefd[1], &error, sizeof(error)); + (void)xwrite(pipefd[1], &error, sizeof(error)); xclose(pipefd[1]); _Exit(127); diff --git a/src/xtime.c b/src/xtime.c index 79dafad..e90bdb1 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -81,7 +81,7 @@ static int safe_add(int *value, int delta) { static int floor_div(int n, int d) { int a = n < 0; - return (n + a)/d - a; + return (n + a) / d - a; } static int wrap(int *value, int max, int *next) { @@ -93,7 +93,7 @@ static int wrap(int *value, int max, int *next) { static int month_length(int year, int month) { static const int month_lengths[] = {31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31}; int ret = month_lengths[month]; - if (month == 1 && year%4 == 0 && (year%100 != 0 || (year + 300)%400 == 0)) { + if (month == 1 && year % 4 == 0 && (year % 100 != 0 || (year + 300) % 400 == 0)) { ++ret; } return ret; @@ -156,13 +156,13 @@ int xtimegm(struct tm *tm, time_t *timep) { leap_days = floor_div(tm->tm_year + 3, 4) - floor_div(tm->tm_year + 99, 100) + floor_div(tm->tm_year + 299, 400) - 17; } - long long epoch_days = 365LL*(tm->tm_year - 70) + leap_days + tm->tm_yday; - tm->tm_wday = (epoch_days + 4)%7; + long long epoch_days = 365LL * (tm->tm_year - 70) + leap_days + tm->tm_yday; + tm->tm_wday = (epoch_days + 4) % 7; if (tm->tm_wday < 0) { tm->tm_wday += 7; } - long long epoch_time = tm->tm_sec + 60*(tm->tm_min + 60*(tm->tm_hour + 24*epoch_days)); + long long epoch_time = tm->tm_sec + 60 * (tm->tm_min + 60 * (tm->tm_hour + 24 * epoch_days)); *timep = (time_t)epoch_time; if ((long long)*timep != epoch_time) { goto overflow; @@ -296,7 +296,7 @@ end: goto error; } - int offset = 60*tz_hour + tz_min; + int offset = 60 * tz_hour + tz_min; if (tz_negative) { result->tv_sec -= offset; } else { diff --git a/tests/bfstd.c b/tests/bfstd.c index 83964e5..33b3792 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,8 +7,8 @@ #include #include #include -#include #include +#include #include #include diff --git a/tests/mksock.c b/tests/mksock.c index 42ef322..7023b4f 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -9,8 +9,8 @@ #include "../src/bfstd.h" #include #include -#include #include +#include #include #include #include diff --git a/tests/trie.c b/tests/trie.c index 6ea94a2..656fd85 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -120,11 +120,11 @@ int main(void) { bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr + longsize/2, 0xAB, longsize/2); + memset(longstr + longsize / 2, 0xAB, longsize / 2); bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); - memset(longstr, 0xAA, longsize/2); + memset(longstr, 0xAA, longsize / 2); bfs_verify(!trie_find_mem(&trie, longstr, longsize)); bfs_verify(trie_insert_mem(&trie, longstr, longsize)); diff --git a/tests/xtimegm.c b/tests/xtimegm.c index b2479b7..973b2eb 100644 --- a/tests/xtimegm.c +++ b/tests/xtimegm.c @@ -42,9 +42,9 @@ static bool tm_equal(const struct tm *tma, const struct tm *tmb) { static void tm_print(FILE *file, const struct tm *tm) { fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_wday, tm->tm_yday, tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); } -- cgit v1.2.3 From 25660f5c062cf1e0d0982457614c9aa4584fa565 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 29 Sep 2023 15:13:41 -0400 Subject: tests/xtouch: Try creating the immediate parent first --- tests/xtouch.c | 39 ++++++++++++++++++++++++--------------- 1 file changed, 24 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/tests/xtouch.c b/tests/xtouch.c index 4a02bf3..80fad8d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -48,31 +48,40 @@ static int at_flags(const struct args *args) { /** Create any parent directories of the given path. */ static int mkdirs(const char *path, mode_t mode) { - char *copy = strdup(path); - if (!copy) { - return -1; + int ret = -1; + char *dir = xdirname(path); + if (!dir) { + goto err; } - int ret = -1; - char *cur = copy + strspn(copy, "/"); - while (true) { - cur += strcspn(cur, "/"); + if (strcmp(dir, ".") == 0) { + goto done; + } + + // Optimistically try the immediate parent first + if (mkdir(dir, mode) == 0 || errno == EEXIST) { + goto done; + } + // Create the parents one-at-a-time + char *cur = dir + strspn(dir, "/"); + while (*cur) { + cur += strcspn(cur, "/"); char *next = cur + strspn(cur, "/"); - if (!*next) { - ret = 0; - break; - } + char c = *cur; *cur = '\0'; - if (mkdir(copy, mode) != 0 && errno != EEXIST) { - break; + if (mkdir(dir, mode) != 0 && errno != EEXIST) { + goto err; } - *cur = '/'; + *cur = c; cur = next; } - free(copy); +done: + ret = 0; +err: + free(dir); return ret; } -- cgit v1.2.3 From 9907004a1bb047e0644bf6d180aeb8b6ac23b1bb Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 2 Oct 2023 12:57:26 -0400 Subject: tests: Don't cd into paths longer than PATH_MAX This fixes a warning on at least Alpine Linux that looks like cd: error retrieving current directory: getcwd: cannot access parent directories: Filename too long --- tests/tests.sh | 20 ++++++++------------ 1 file changed, 8 insertions(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 66a79a8..121cd85 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -393,18 +393,14 @@ function make_deep() { local names="$name/$name/$name/$name" for i in {0..9} A B C D E F; do - ( - mkdir "$1/$i" - cd "$1/$i" - - # 4 * 1024 == 4096 == PATH_MAX - for _ in {1..4}; do - mkdir -p "$names" - cd "$names" - done - - "$XTOUCH" "$name" - ) + "$XTOUCH" -p "$1/$i/$name" + + # 4 * 1024 == 4096 == PATH_MAX + for _ in {1..4}; do + mv "$1/$i/$name" "$1/" + mkdir -p "$1/$i/$names" + mv "$1/$name" "$1/$i/$names/" + done done } make_deep "$TMP/deep" -- cgit v1.2.3 From c1103834065bbd1182a90b9723dbd89a4a908731 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 2 Oct 2023 14:13:49 -0400 Subject: tests: Fix make_deep() on FreeBSD I keep forgetting that PATH_MAX is only 1024 there. --- tests/tests.sh | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 121cd85..663f5aa 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -389,18 +389,19 @@ function make_deep() { name="${name}${name}${name}${name}" name="${name:0:255}" - # 4 * 256 - 1 == 1023 - local names="$name/$name/$name/$name" - for i in {0..9} A B C D E F; do "$XTOUCH" -p "$1/$i/$name" - # 4 * 1024 == 4096 == PATH_MAX - for _ in {1..4}; do - mv "$1/$i/$name" "$1/" - mkdir -p "$1/$i/$names" - mv "$1/$name" "$1/$i/$names/" - done + ( + cd "$1/$i" + + # 8 * 512 == 4096 >= PATH_MAX + for _ in {1..8}; do + mv "$name" .. + mkdir -p "$name/$name" + mv "../$name" "$name/$name/" + done + ) done } make_deep "$TMP/deep" -- cgit v1.2.3 From 428cf9c206beee3407ea3c5480b00f4cfbea95f5 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 5 Oct 2023 12:56:36 -0400 Subject: bfstd: Add a thread-safe wrapper for strerror() --- src/bfstd.c | 43 +++++++++++++++++++++++++++++++++++++++++++ src/bfstd.h | 11 +++++++++++ src/color.c | 2 +- src/eval.c | 2 +- src/parse.c | 4 ++-- src/printf.c | 2 +- src/thread.h | 3 ++- src/xregex.c | 3 ++- tests/bfstd.c | 4 ++-- tests/mksock.c | 2 +- tests/xtouch.c | 6 +++--- 11 files changed, 69 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index fcf4a6d..e9214d4 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -12,6 +12,7 @@ #include #include #include +#include #include #include #include @@ -276,6 +277,48 @@ char *xstpencpy(char *dest, char *end, const char *src, size_t n) { } } +const char *xstrerror(int errnum) { + int saved = errno; + const char *ret = NULL; + static thread_local char buf[256]; + +#if __APPLE__ + // No strerror_l() on macOS + if (strerror_r(errnum, buf, sizeof(buf)) == 0) { + ret = buf; + } +#else +# if __NetBSD__ + // NetBSD has no thread-specific locales + locale_t loc = LC_GLOBAL_LOCALE; +# else + locale_t loc = uselocale((locale_t)0); +# endif + + locale_t copy = loc; + if (copy == LC_GLOBAL_LOCALE) { + copy = duplocale(copy); + } + + if (copy != (locale_t)0) { + ret = strerror_l(errnum, loc); + } + + if (loc == LC_GLOBAL_LOCALE) { + freelocale(copy); + } +#endif + + if (!ret) { + // Fallback for strerror_[lr]() or duplocale() failures + snprintf(buf, sizeof(buf), "Unknown error %d", errnum); + ret = buf; + } + + errno = saved; + return ret; +} + void xstrmode(mode_t mode, char str[11]) { strcpy(str, "----------"); diff --git a/src/bfstd.h b/src/bfstd.h index fb77399..abde24e 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -166,6 +166,17 @@ char *xstpecpy(char *dest, char *end, const char *src); */ char *xstpencpy(char *dest, char *end, const char *src, size_t n); +/** + * Thread-safe strerror(). + * + * @param errnum + * An error number. + * @return + * A string describing that error, which remains valid until the next + * xstrerror() call in the same thread. + */ +const char *xstrerror(int errnum); + /** * Format a mode like ls -l (e.g. -rw-r--r--). * diff --git a/src/color.c b/src/color.c index 788d35d..fbb5edf 100644 --- a/src/color.c +++ b/src/color.c @@ -1239,7 +1239,7 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { break; case 'm': - if (dstrcat(&cfile->buffer, strerror(error)) != 0) { + if (dstrcat(&cfile->buffer, xstrerror(error)) != 0) { return -1; } break; diff --git a/src/eval.c b/src/eval.c index 9f4896a..e0dd97b 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1376,7 +1376,7 @@ static enum bftw_action eval_callback(const struct BFTW *ftwbuf, void *ptr) { if (ftwbuf->type == BFS_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { - eval_error(&state, "%s.\n", strerror(ftwbuf->error)); + eval_error(&state, "%s.\n", xstrerror(ftwbuf->error)); } state.action = BFTW_PRUNE; goto done; diff --git a/src/parse.c b/src/parse.c index 7766a7b..976f7cb 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1127,7 +1127,7 @@ static struct bfs_expr *parse_color(struct parser_state *state, int color, int a if (color) { if (!colors) { - parse_expr_error(state, expr, "Error parsing $$LS_COLORS: %s.\n", strerror(ctx->colors_error)); + parse_expr_error(state, expr, "Error parsing $$LS_COLORS: %s.\n", xstrerror(ctx->colors_error)); bfs_expr_free(expr); return NULL; } @@ -3741,7 +3741,7 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } if (state.use_color == COLOR_AUTO && !ctx->colors) { - bfs_warning(ctx, "Error parsing $$LS_COLORS: %s.\n\n", strerror(ctx->colors_error)); + bfs_warning(ctx, "Error parsing $$LS_COLORS: %s.\n\n", xstrerror(ctx->colors_error)); } if (bfs_optimize(ctx) != 0) { diff --git a/src/printf.c b/src/printf.c index 98bcb0f..704e26d 100644 --- a/src/printf.c +++ b/src/printf.c @@ -744,7 +744,7 @@ int bfs_printf_parse(const struct bfs_ctx *ctx, struct bfs_expr *expr, const cha if (!directive.ptr) { int error = errno; bfs_expr_error(ctx, expr); - bfs_error(ctx, "Couldn't parse the mount table: %s.\n", strerror(error)); + bfs_error(ctx, "Couldn't parse the mount table: %s.\n", xstrerror(error)); goto directive_error; } break; diff --git a/src/thread.h b/src/thread.h index ab95a79..a59033c 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,6 +8,7 @@ #ifndef BFS_THREAD_H #define BFS_THREAD_H +#include "bfstd.h" #include "config.h" #include "diag.h" #include @@ -23,7 +24,7 @@ #endif #define thread_verify(expr, cond) \ - bfs_verify((errno = (expr), (cond)), "%s: %s", #expr, strerror(errno)) + bfs_verify((errno = (expr), (cond)), "%s: %s", #expr, xstrerror(errno)) /** * Wrapper for pthread_create(). diff --git a/src/xregex.c b/src/xregex.c index 87b692e..3df27f0 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -3,6 +3,7 @@ #include "xregex.h" #include "alloc.h" +#include "bfstd.h" #include "config.h" #include "diag.h" #include "sanity.h" @@ -274,7 +275,7 @@ void bfs_regfree(struct bfs_regex *regex) { char *bfs_regerror(const struct bfs_regex *regex) { if (!regex) { - return strdup(strerror(ENOMEM)); + return strdup(xstrerror(ENOMEM)); } #if BFS_USE_ONIGURUMA diff --git a/tests/bfstd.c b/tests/bfstd.c index 33b3792..c386279 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -15,12 +15,12 @@ /** Check the result of xdirname()/xbasename(). */ static void check_base_dir(const char *path, const char *dir, const char *base) { char *xdir = xdirname(path); - bfs_verify(xdir, "xdirname(): %s", strerror(errno)); + bfs_verify(xdir, "xdirname(): %s", xstrerror(errno)); bfs_verify(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); free(xdir); char *xbase = xbasename(path); - bfs_verify(xbase, "xbasename(): %s", strerror(errno)); + bfs_verify(xbase, "xbasename(): %s", xstrerror(errno)); bfs_verify(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); free(xbase); } diff --git a/tests/mksock.c b/tests/mksock.c index 7023b4f..f3b61da 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -19,7 +19,7 @@ * Print an error message. */ static void errmsg(const char *cmd, const char *path) { - fprintf(stderr, "%s: '%s': %s.\n", cmd, path, strerror(errno)); + fprintf(stderr, "%s: '%s': %s.\n", cmd, path, xstrerror(errno)); } /** diff --git a/tests/xtouch.c b/tests/xtouch.c index 80fad8d..a4c4d40 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -197,14 +197,14 @@ int main(int argc, char *argv[]) { if (rarg) { struct stat buf; if (fstatat(AT_FDCWD, rarg, &buf, at_flags(&args)) != 0) { - fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, strerror(errno)); + fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, xstrerror(errno)); return EXIT_FAILURE; } times[0] = buf.st_atim; times[1] = buf.st_mtim; } else if (darg) { if (xgetdate(darg, ×[0]) != 0) { - fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, strerror(errno)); + fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, xstrerror(errno)); return EXIT_FAILURE; } times[1] = times[0]; @@ -237,7 +237,7 @@ int main(int argc, char *argv[]) { for (; optind < argc; ++optind) { const char *path = argv[optind]; if (xtouch(&args, path) != 0) { - fprintf(stderr, "%s: '%s': %s\n", cmd, path, strerror(errno)); + fprintf(stderr, "%s: '%s': %s\n", cmd, path, xstrerror(errno)); ret = EXIT_FAILURE; } } -- cgit v1.2.3 From 8ddd85627f537131e3c8fdd3957fd263dac3dffe Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 5 Oct 2023 16:25:21 -0400 Subject: tests: Respect $NO_COLOR --- tests/tests.sh | 24 +++++++++++++++--------- 1 file changed, 15 insertions(+), 9 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 663f5aa..61f17b4 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -19,7 +19,11 @@ export UBSAN_OPTIONS="$SAN_OPTIONS" export LS_COLORS="" unset BFS_COLORS -if [ -t 1 ]; then +function color_fd() { + [ -z "${NO_COLOR:-}" ] && [ -t "$1" ] +} + +if color_fd 1; then BLD=$'\033[01m' RED=$'\033[01;31m' GRN=$'\033[01;32m' @@ -443,7 +447,7 @@ fi function bfs_verbose() { if [ "$VERBOSE_COMMANDS" ]; then - if [ -t 3 ]; then + if color_fd 3; then printf "${GRN}%q${RST} " "${BFS[@]}" >&3 local expr_started= @@ -493,7 +497,7 @@ function check_exit() { } # Detect colored diff support -if [ -t 2 ] && diff --color=always /dev/null /dev/null 2>/dev/null; then +if color_fd 2 && diff --color=always /dev/null /dev/null 2>/dev/null; then DIFF="diff --color=always" else DIFF="diff" @@ -634,7 +638,7 @@ function update_eol() { if [ "$VERBOSE_TESTS" ]; then BOL='' -elif [ -t 1 ]; then +elif color_fd 1; then BOL='\r\033[K' # Workaround for bash 4: checkwinsize is off by default. We can turn it on, @@ -651,12 +655,14 @@ passed=0 failed=0 skipped=0 +if color_fd 1 || [ "$VERBOSE_TESTS" ]; then + TEST_FMT="${BOL}${YLW}%s${RST}${EOL}" +else + TEST_FMT="." +fi + for TEST in "${TEST_CASES[@]}"; do - if [[ -t 1 || "$VERBOSE_TESTS" ]]; then - printf "${BOL}${YLW}%s${RST}${EOL}" "$TEST" - else - printf "." - fi + printf "$TEST_FMT" "$TEST" OUT="$TMP/$TEST.out" mkdir -p "${OUT%/*}" -- cgit v1.2.3 From 5a16a1e2b33733f0e6dff4ddfaed2662c3ba7ccb Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 5 Oct 2023 16:25:56 -0400 Subject: tests/bfs: Add tests for color autodetection --- tests/bfs/color_auto.out | 27 +++++++++++++++++++++++++++ tests/bfs/color_auto.sh | 6 ++++++ tests/bfs/nocolor_env.out | 27 +++++++++++++++++++++++++++ tests/bfs/nocolor_env.sh | 5 +++++ 4 files changed, 65 insertions(+) create mode 100644 tests/bfs/color_auto.out create mode 100644 tests/bfs/color_auto.sh create mode 100644 tests/bfs/nocolor_env.out create mode 100644 tests/bfs/nocolor_env.sh (limited to 'tests') diff --git a/tests/bfs/color_auto.out b/tests/bfs/color_auto.out new file mode 100644 index 0000000..a439814 --- /dev/null +++ b/tests/bfs/color_auto.out @@ -0,0 +1,27 @@ +$'rainbow/\e[1m' +$'rainbow/\e[1m/'$'\e[0m' +rainbow +rainbow/exec.sh +rainbow/socket +rainbow/broken +rainbow/chardev_link +rainbow/link.txt +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky +rainbow/file.dat +rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/mh1 +rainbow/mh2 +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/color_auto.sh b/tests/bfs/color_auto.sh new file mode 100644 index 0000000..aa2eb02 --- /dev/null +++ b/tests/bfs/color_auto.sh @@ -0,0 +1,6 @@ +command -v unbuffer &>/dev/null || skip + +unset NO_COLOR +unbuffer "${BFS[@]}" rainbow >"$OUT" +sort_output +diff_output diff --git a/tests/bfs/nocolor_env.out b/tests/bfs/nocolor_env.out new file mode 100644 index 0000000..d51d24d --- /dev/null +++ b/tests/bfs/nocolor_env.out @@ -0,0 +1,27 @@ +rainbow +rainbow/ +rainbow// +rainbow/broken +rainbow/chardev_link +rainbow/exec.sh +rainbow/file.dat +rainbow/file.txt +rainbow/link.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/mh1 +rainbow/mh2 +rainbow/ow +rainbow/pipe +rainbow/sgid +rainbow/socket +rainbow/sticky +rainbow/sticky_ow +rainbow/sugid +rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/nocolor_env.sh b/tests/bfs/nocolor_env.sh new file mode 100644 index 0000000..399bdb0 --- /dev/null +++ b/tests/bfs/nocolor_env.sh @@ -0,0 +1,5 @@ +command -v unbuffer &>/dev/null || skip + +NO_COLOR=1 unbuffer "${BFS[@]}" rainbow >"$OUT" +sort_output +diff_output -- cgit v1.2.3 From 6e12ff030a380d0f13c505b5285ceb589761b68d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 5 Oct 2023 17:18:40 -0400 Subject: tests/bfs: Add a test that runs -status --- tests/bfs/status.sh | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 tests/bfs/status.sh (limited to 'tests') diff --git a/tests/bfs/status.sh b/tests/bfs/status.sh new file mode 100644 index 0000000..30bdfa7 --- /dev/null +++ b/tests/bfs/status.sh @@ -0,0 +1,3 @@ +command -v unbuffer &>/dev/null || skip + +unbuffer "${BFS[@]}" basic -status >"$OUT" -- cgit v1.2.3 From 5e7b5eeb59f9f46ce916aaf968dd42570570580a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 6 Oct 2023 11:42:06 -0400 Subject: tests: New bfs_pty wrapper for unbuffer --- tests/bfs/color_auto.sh | 4 +--- tests/bfs/nocolor_env.sh | 2 +- tests/bfs/status.sh | 4 +--- tests/tests.sh | 14 ++++++++++++++ 4 files changed, 17 insertions(+), 7 deletions(-) (limited to 'tests') diff --git a/tests/bfs/color_auto.sh b/tests/bfs/color_auto.sh index aa2eb02..7e875cc 100644 --- a/tests/bfs/color_auto.sh +++ b/tests/bfs/color_auto.sh @@ -1,6 +1,4 @@ -command -v unbuffer &>/dev/null || skip - unset NO_COLOR -unbuffer "${BFS[@]}" rainbow >"$OUT" +bfs_pty rainbow >"$OUT" sort_output diff_output diff --git a/tests/bfs/nocolor_env.sh b/tests/bfs/nocolor_env.sh index 399bdb0..0a17fb2 100644 --- a/tests/bfs/nocolor_env.sh +++ b/tests/bfs/nocolor_env.sh @@ -1,5 +1,5 @@ command -v unbuffer &>/dev/null || skip -NO_COLOR=1 unbuffer "${BFS[@]}" rainbow >"$OUT" +NO_COLOR=1 bfs_pty rainbow >"$OUT" sort_output diff_output diff --git a/tests/bfs/status.sh b/tests/bfs/status.sh index 30bdfa7..d3859e2 100644 --- a/tests/bfs/status.sh +++ b/tests/bfs/status.sh @@ -1,3 +1 @@ -command -v unbuffer &>/dev/null || skip - -unbuffer "${BFS[@]}" basic -status >"$OUT" +bfs_pty basic -status >"$OUT" diff --git a/tests/tests.sh b/tests/tests.sh index 61f17b4..5297446 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -488,6 +488,20 @@ function invoke_bfs() { fi } +function bfs_pty() { + command -v unbuffer &>/dev/null || skip + + bfs_verbose "$@" + unbuffer bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" + local status="$?" + + if ((status > 125)); then + exit "$status" + else + return "$status" + fi +} + function check_exit() { local expected="$1" local actual="0" -- cgit v1.2.3 From 14c610d1e2849c1d162adf6f43b72e023780ffde Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 6 Oct 2023 11:56:31 -0400 Subject: tests: Check for expect_unbuffer too That's where FreeBSD installs it. --- tests/tests.sh | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 5297446..662efd9 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -488,11 +488,17 @@ function invoke_bfs() { fi } +if command -v unbuffer &>/dev/null; then + UNBUFFER=unbuffer +elif command -v expect_unbuffer &>/dev/null; then + UNBUFFER=expect_unbuffer +fi + function bfs_pty() { - command -v unbuffer &>/dev/null || skip + test -n "$UNBUFFER" || skip bfs_verbose "$@" - unbuffer bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" + "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" local status="$?" if ((status > 125)); then -- cgit v1.2.3 From bcfe9c4e846bc97f97967c7df95e6b0a08a9a0ad Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 6 Oct 2023 12:00:02 -0400 Subject: tests/bfs/status: Try to test SIGWINCH --- tests/bfs/status.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/bfs/status.sh b/tests/bfs/status.sh index d3859e2..83e12d3 100644 --- a/tests/bfs/status.sh +++ b/tests/bfs/status.sh @@ -1 +1 @@ -bfs_pty basic -status >"$OUT" +bfs_pty basic -status -print -depth 0 -exec stty cols 123 rows 14 \; >"$OUT" -- cgit v1.2.3 From 054ef7f719ce6fd2167f1c1b4433feaa438bebfc Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 6 Oct 2023 12:37:01 -0400 Subject: alloc: Test allocation size overflows --- src/alloc.h | 4 ++-- tests/alloc.c | 12 ++++++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/src/alloc.h b/src/alloc.h index 5f0c423..fd3e5f0 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -132,11 +132,11 @@ void *zalloc(size_t align, size_t size); /** Allocate memory for an array. */ #define ALLOC_ARRAY(type, count) \ - (type *)alloc(alignof(type), sizeof_array(type, count)); + (type *)alloc(alignof(type), sizeof_array(type, count)) /** Allocate zeroed memory for an array. */ #define ZALLOC_ARRAY(type, count) \ - (type *)zalloc(alignof(type), sizeof_array(type, count)); + (type *)zalloc(alignof(type), sizeof_array(type, count)) /** Allocate memory for a flexible struct. */ #define ALLOC_FLEX(type, member, count) \ diff --git a/tests/alloc.c b/tests/alloc.c index 9e6e892..382131f 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "../src/alloc.h" #include "../src/diag.h" +#include #include int main(void) { @@ -13,13 +14,20 @@ int main(void) { }; bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); bfs_verify(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); - bfs_verify(sizeof_flex(struct flexible, bar, SIZE_MAX / sizeof(int) + 1) - == align_floor(alignof(struct flexible), SIZE_MAX)); + + size_t too_many = SIZE_MAX / sizeof(int) + 1; + bfs_verify(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) // Doesn't happen in typical ABIs bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + // Make sure we detect allocation size overflows + bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + bfs_verify(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + // varena tests struct varena varena; VARENA_INIT(&varena, struct flexible, bar); -- cgit v1.2.3 From da5c9dd34f65989c842cfb831b8592157dd8ed34 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 12 Oct 2023 13:09:11 -0400 Subject: diag: Move enum debug_flags out of ctx.h --- src/ctx.c | 25 ------------------------- src/ctx.h | 28 +--------------------------- src/diag.c | 25 +++++++++++++++++++++++++ src/diag.h | 29 ++++++++++++++++++++++++++++- tests/alloc.c | 1 + 5 files changed, 55 insertions(+), 53 deletions(-) (limited to 'tests') diff --git a/src/ctx.c b/src/ctx.c index 9a24a33..561ecae 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -17,31 +17,6 @@ #include #include -const char *debug_flag_name(enum debug_flags flag) { - switch (flag) { - case DEBUG_COST: - return "cost"; - case DEBUG_EXEC: - return "exec"; - case DEBUG_OPT: - return "opt"; - case DEBUG_RATES: - return "rates"; - case DEBUG_SEARCH: - return "search"; - case DEBUG_STAT: - return "stat"; - case DEBUG_TREE: - return "tree"; - - case DEBUG_ALL: - break; - } - - bfs_bug("Unrecognized debug flag"); - return "???"; -} - struct bfs_ctx *bfs_ctx_new(void) { struct bfs_ctx *ctx = ZALLOC(struct bfs_ctx); if (!ctx) { diff --git a/src/ctx.h b/src/ctx.h index 2b8e8cb..96406bd 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -10,38 +10,12 @@ #include "bftw.h" #include "config.h" +#include "diag.h" #include "trie.h" #include #include #include -/** - * Various debugging flags. - */ -enum debug_flags { - /** Print cost estimates. */ - DEBUG_COST = 1 << 0, - /** Print executed command details. */ - DEBUG_EXEC = 1 << 1, - /** Print optimization details. */ - DEBUG_OPT = 1 << 2, - /** Print rate information. */ - DEBUG_RATES = 1 << 3, - /** Trace the filesystem traversal. */ - DEBUG_SEARCH = 1 << 4, - /** Trace all stat() calls. */ - DEBUG_STAT = 1 << 5, - /** Print the parse tree. */ - DEBUG_TREE = 1 << 6, - /** All debug flags. */ - DEBUG_ALL = (1 << 7) - 1, -}; - -/** - * Convert a debug flag to a string. - */ -const char *debug_flag_name(enum debug_flags flag); - /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index fa66525..bf2343d 100644 --- a/src/diag.c +++ b/src/diag.c @@ -27,6 +27,31 @@ noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...) { abort(); } +const char *debug_flag_name(enum debug_flags flag) { + switch (flag) { + case DEBUG_COST: + return "cost"; + case DEBUG_EXEC: + return "exec"; + case DEBUG_OPT: + return "opt"; + case DEBUG_RATES: + return "rates"; + case DEBUG_SEARCH: + return "search"; + case DEBUG_STAT: + return "stat"; + case DEBUG_TREE: + return "tree"; + + case DEBUG_ALL: + break; + } + + bfs_bug("Unrecognized debug flag"); + return "???"; +} + void bfs_perror(const struct bfs_ctx *ctx, const char *str) { bfs_error(ctx, "%s: %m.\n", str); } diff --git a/src/diag.h b/src/diag.h index fea8847..838a794 100644 --- a/src/diag.h +++ b/src/diag.h @@ -9,7 +9,6 @@ #define BFS_DIAG_H #include "config.h" -#include "ctx.h" #include /** @@ -84,8 +83,36 @@ noreturn void bfs_abortf(const struct bfs_loc *loc, const char *format, ...); # define bfs_assert bfs_verify #endif +struct bfs_ctx; struct bfs_expr; +/** + * Various debugging flags. + */ +enum debug_flags { + /** Print cost estimates. */ + DEBUG_COST = 1 << 0, + /** Print executed command details. */ + DEBUG_EXEC = 1 << 1, + /** Print optimization details. */ + DEBUG_OPT = 1 << 2, + /** Print rate information. */ + DEBUG_RATES = 1 << 3, + /** Trace the filesystem traversal. */ + DEBUG_SEARCH = 1 << 4, + /** Trace all stat() calls. */ + DEBUG_STAT = 1 << 5, + /** Print the parse tree. */ + DEBUG_TREE = 1 << 6, + /** All debug flags. */ + DEBUG_ALL = (1 << 7) - 1, +}; + +/** + * Convert a debug flag to a string. + */ +const char *debug_flag_name(enum debug_flags flag); + /** * Like perror(), but decorated like bfs_error(). */ diff --git a/tests/alloc.c b/tests/alloc.c index 382131f..2334241 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -5,6 +5,7 @@ #include "../src/diag.h" #include #include +#include int main(void) { // Check sizeof_flex() -- cgit v1.2.3 From 5dbab7e7815800ea6a110519154f05485457b194 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 12 Oct 2023 23:47:04 -0400 Subject: tests: Don't crash if unbuffer isn't installed --- tests/tests.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 662efd9..f233f74 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -495,7 +495,7 @@ elif command -v expect_unbuffer &>/dev/null; then fi function bfs_pty() { - test -n "$UNBUFFER" || skip + test -n "${UNBUFFER:-}" || skip bfs_verbose "$@" "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" -- cgit v1.2.3 From 3780856f7503096e01a64f19eb6051b765db252e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 13 Oct 2023 13:52:01 -0400 Subject: tests: Always create the scratch directory Otherwise, some tests that try to check for flag support like invoke_bfs scratch -quit -xattr || skip can be accidentally skipped if ./scratch doesn't exist yet: $ ./tests/tests.sh bsd/xattr tests skipped: 1 After this patch, we get the expected $ ./tests/tests.sh bsd/xattr tests passed: 1 --- tests/tests.sh | 2 ++ 1 file changed, 2 insertions(+) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index f233f74..61e4aba 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -437,6 +437,8 @@ function make_rainbow() { } make_rainbow "$TMP/rainbow" +mkdir "$TMP/scratch" + # Close stdin so bfs doesn't think we're interactive exec Date: Sat, 14 Oct 2023 08:55:57 -0400 Subject: tests: Move closed_std* tests out of the POSIX group POSIX actually says > If the utility would be executed with file descriptor 0, 1, or 2 > closed, implementations may execute the utility with the file > descriptor open to an unspecified file. So we're not guaranteed to be able to detect the situation in the first place. Add a best-effort check for these platforms and skip the test. Link: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_09_01_01 --- tests/bfs/closed_stderr.sh | 4 ++++ tests/bfs/closed_stdin.out | 19 +++++++++++++++++++ tests/bfs/closed_stdin.sh | 1 + tests/bfs/closed_stdout.sh | 4 ++++ tests/posix/closed_stderr.sh | 1 - tests/posix/closed_stdin.out | 19 ------------------- tests/posix/closed_stdin.sh | 1 - tests/posix/closed_stdout.sh | 1 - 8 files changed, 28 insertions(+), 22 deletions(-) create mode 100644 tests/bfs/closed_stderr.sh create mode 100644 tests/bfs/closed_stdin.out create mode 100644 tests/bfs/closed_stdin.sh create mode 100644 tests/bfs/closed_stdout.sh delete mode 100644 tests/posix/closed_stderr.sh delete mode 100644 tests/posix/closed_stdin.out delete mode 100644 tests/posix/closed_stdin.sh delete mode 100644 tests/posix/closed_stdout.sh (limited to 'tests') diff --git a/tests/bfs/closed_stderr.sh b/tests/bfs/closed_stderr.sh new file mode 100644 index 0000000..26abd85 --- /dev/null +++ b/tests/bfs/closed_stderr.sh @@ -0,0 +1,4 @@ +# Check if the platform automatically re-opens stderr before we can +(bash -c 'echo >&2' 2>&-) && skip + +! invoke_bfs basic >&- 2>&- diff --git a/tests/bfs/closed_stdin.out b/tests/bfs/closed_stdin.out new file mode 100644 index 0000000..a7ccfe4 --- /dev/null +++ b/tests/bfs/closed_stdin.out @@ -0,0 +1,19 @@ +basic +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/closed_stdin.sh b/tests/bfs/closed_stdin.sh new file mode 100644 index 0000000..6932be8 --- /dev/null +++ b/tests/bfs/closed_stdin.sh @@ -0,0 +1 @@ +bfs_diff basic <&- diff --git a/tests/bfs/closed_stdout.sh b/tests/bfs/closed_stdout.sh new file mode 100644 index 0000000..5b6f7c3 --- /dev/null +++ b/tests/bfs/closed_stdout.sh @@ -0,0 +1,4 @@ +# Check if the platform automatically re-opens stdout before we can +(bash -c echo >&-) && skip + +! invoke_bfs basic >&- diff --git a/tests/posix/closed_stderr.sh b/tests/posix/closed_stderr.sh deleted file mode 100644 index 570d5bb..0000000 --- a/tests/posix/closed_stderr.sh +++ /dev/null @@ -1 +0,0 @@ -! invoke_bfs basic >&- 2>&- diff --git a/tests/posix/closed_stdin.out b/tests/posix/closed_stdin.out deleted file mode 100644 index a7ccfe4..0000000 --- a/tests/posix/closed_stdin.out +++ /dev/null @@ -1,19 +0,0 @@ -basic -basic/a -basic/b -basic/c -basic/c/d -basic/e -basic/e/f -basic/g -basic/g/h -basic/i -basic/j -basic/j/foo -basic/k -basic/k/foo -basic/k/foo/bar -basic/l -basic/l/foo -basic/l/foo/bar -basic/l/foo/bar/baz diff --git a/tests/posix/closed_stdin.sh b/tests/posix/closed_stdin.sh deleted file mode 100644 index 6932be8..0000000 --- a/tests/posix/closed_stdin.sh +++ /dev/null @@ -1 +0,0 @@ -bfs_diff basic <&- diff --git a/tests/posix/closed_stdout.sh b/tests/posix/closed_stdout.sh deleted file mode 100644 index 25c060d..0000000 --- a/tests/posix/closed_stdout.sh +++ /dev/null @@ -1 +0,0 @@ -! invoke_bfs basic >&- -- cgit v1.2.3 From 3ac3bee7b0d9c9be693415206efa664bf4a7d4a7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 17 Oct 2023 11:35:41 -0400 Subject: dir: Fix FreeBSD union mounts --- src/dir.c | 54 ++++++++++++++++++++++++++++++++++++++++------- tests/posix/overlayfs.out | 5 +++++ tests/posix/overlayfs.sh | 7 ++++++ tests/posix/unionfs.out | 10 +++++++++ tests/posix/unionfs.sh | 6 ++++++ 5 files changed, 74 insertions(+), 8 deletions(-) create mode 100644 tests/posix/overlayfs.out create mode 100644 tests/posix/overlayfs.sh create mode 100644 tests/posix/unionfs.out create mode 100644 tests/posix/unionfs.sh (limited to 'tests') diff --git a/src/dir.c b/src/dir.c index dee02e5..371696f 100644 --- a/src/dir.c +++ b/src/dir.c @@ -7,6 +7,7 @@ #include "config.h" #include "diag.h" #include "sanity.h" +#include "trie.h" #include #include #include @@ -101,7 +102,9 @@ enum bfs_type bfs_mode_to_type(mode_t mode) { */ enum { /** We've reached the end of the directory. */ - BFS_DIR_EOF = BFS_DIR_PRIVATE << 0, + BFS_DIR_EOF = BFS_DIR_PRIVATE << 0, + /** This directory is a union mount we need to dedup manually. */ + BFS_DIR_UNION = BFS_DIR_PRIVATE << 1, }; struct bfs_dir { @@ -111,6 +114,9 @@ struct bfs_dir { int fd; unsigned short pos; unsigned short size; +# if __FreeBSD__ + struct trie trie; +# endif alignas(sys_dirent) char buf[]; #else DIR *dir; @@ -153,7 +159,14 @@ int bfs_opendir(struct bfs_dir *dir, int at_fd, const char *at_path, enum bfs_di dir->fd = fd; dir->pos = 0; dir->size = 0; -#else + +# if __FreeBSD__ && defined(F_ISUNIONSTACK) + if (fcntl(fd, F_ISUNIONSTACK) > 0) { + dir->flags |= BFS_DIR_UNION; + trie_init(&dir->trie); + } +# endif +#else // !BFS_USE_GETDENTS dir->dir = fdopendir(fd); if (!dir->dir) { if (at_path) { @@ -245,11 +258,23 @@ static int bfs_getdent(struct bfs_dir *dir, const sys_dirent **de) { } /** Skip ".", "..", and deleted/empty dirents. */ -static bool skip_dirent(const sys_dirent *de) { -#if __FreeBSD__ +static int bfs_skipdent(struct bfs_dir *dir, const sys_dirent *de) { +#if BFS_USE_GETDENTS && __FreeBSD__ + // Union mounts on FreeBSD have to be de-duplicated in userspace + if (dir->flags & BFS_DIR_UNION) { + struct trie_leaf *leaf = trie_insert_str(&dir->trie, de->d_name); + if (!leaf) { + return -1; + } else if (leaf->value) { + return 1; + } else { + leaf->value = leaf; + } + } + // NFS mounts on FreeBSD can return empty dirents with inode number 0 if (de->d_ino == 0) { - return true; + return 1; } #endif @@ -274,7 +299,10 @@ int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de) { return ret; } - if (skip_dirent(sysde)) { + int skip = bfs_skipdent(dir, sysde); + if (skip < 0) { + return skip; + } else if (skip) { continue; } @@ -287,6 +315,16 @@ int bfs_readdir(struct bfs_dir *dir, struct bfs_dirent *de) { } } +static void bfs_destroydir(struct bfs_dir *dir) { +#if BFS_USE_GETDENTS && __FreeBSD__ + if (dir->flags & BFS_DIR_UNION) { + trie_destroy(&dir->trie); + } +#endif + + sanitize_uninit(dir, DIR_SIZE); +} + int bfs_closedir(struct bfs_dir *dir) { #if BFS_USE_GETDENTS int ret = xclose(dir->fd); @@ -297,7 +335,7 @@ int bfs_closedir(struct bfs_dir *dir) { } #endif - sanitize_uninit(dir, DIR_SIZE); + bfs_destroydir(dir); return ret; } @@ -309,7 +347,7 @@ int bfs_unwrapdir(struct bfs_dir *dir) { int ret = fdclosedir(dir->dir); #endif - sanitize_uninit(dir, DIR_SIZE); + bfs_destroydir(dir); return ret; } #endif diff --git a/tests/posix/overlayfs.out b/tests/posix/overlayfs.out new file mode 100644 index 0000000..754d01d --- /dev/null +++ b/tests/posix/overlayfs.out @@ -0,0 +1,5 @@ +scratch/merged +scratch/merged/bar +scratch/merged/baz +scratch/merged/baz/qux +scratch/merged/foo diff --git a/tests/posix/overlayfs.sh b/tests/posix/overlayfs.sh new file mode 100644 index 0000000..a56b5b3 --- /dev/null +++ b/tests/posix/overlayfs.sh @@ -0,0 +1,7 @@ +test "$UNAME" = "Linux" || skip +clean_scratch +"$XTOUCH" -p scratch/{lower/{foo,bar,baz},upper/{bar,baz/qux}} +mkdir -p scratch/{work,merged} +bfs_sudo mount -t overlay overlay -olowerdir=scratch/lower,upperdir=scratch/upper,workdir=scratch/work scratch/merged || skip +trap "bfs_sudo umount scratch/merged; bfs_sudo rm -rf scratch/work" EXIT +bfs_diff scratch/merged diff --git a/tests/posix/unionfs.out b/tests/posix/unionfs.out new file mode 100644 index 0000000..6d0fa3c --- /dev/null +++ b/tests/posix/unionfs.out @@ -0,0 +1,10 @@ +scratch +scratch/lower +scratch/lower/bar +scratch/lower/baz +scratch/lower/foo +scratch/upper +scratch/upper/bar +scratch/upper/baz +scratch/upper/baz/qux +scratch/upper/foo diff --git a/tests/posix/unionfs.sh b/tests/posix/unionfs.sh new file mode 100644 index 0000000..88a549f --- /dev/null +++ b/tests/posix/unionfs.sh @@ -0,0 +1,6 @@ +[[ "$UNAME" == *BSD* ]] || skip +clean_scratch +"$XTOUCH" -p scratch/{lower/{foo,bar,baz},upper/{bar,baz/qux}} +bfs_sudo mount -t unionfs -o below scratch/{lower,upper} || skip +trap "bfs_sudo umount scratch/upper" EXIT +bfs_diff scratch -- cgit v1.2.3 From dd1c8c2f08d232d5745dc1f5fe483ec4072fe454 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 17 Oct 2023 11:55:31 -0400 Subject: parse: Set BFTW_WHITEOUTS when parsing -type w --- src/parse.c | 3 +++ tests/bsd/type_w.out | 38 +++++++++++++++++++++++++++++++ tests/bsd/type_w.sh | 64 ++++++++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 105 insertions(+) create mode 100644 tests/bsd/type_w.out create mode 100644 tests/bsd/type_w.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 976f7cb..fafd787 100644 --- a/src/parse.c +++ b/src/parse.c @@ -2514,6 +2514,8 @@ static struct bfs_expr *parse_status(struct parser_state *state, int arg1, int a * Parse -x?type [bcdpflsD]. */ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) { + struct bfs_ctx *ctx = state->ctx; + bfs_eval_fn *eval = x ? eval_xtype : eval_type; struct bfs_expr *expr = parse_unary_test(state, eval); if (!expr) { @@ -2551,6 +2553,7 @@ static struct bfs_expr *parse_type(struct parser_state *state, int x, int arg2) break; case 'w': expr->num |= 1 << BFS_WHT; + ctx->flags |= BFTW_WHITEOUTS; break; case '\0': diff --git a/tests/bsd/type_w.out b/tests/bsd/type_w.out new file mode 100644 index 0000000..d383f69 --- /dev/null +++ b/tests/bsd/type_w.out @@ -0,0 +1,38 @@ +1: -rw-r--r-- scratch/mnt/lower/bar +1: -rw-r--r-- scratch/mnt/lower/baz +1: -rw-r--r-- scratch/mnt/lower/foo +1: -rw-r--r-- scratch/mnt/upper/baz/qux +1: -rw-r--r-- scratch/mnt/upper/foo +1: drwxr-xr-x scratch/mnt +1: drwxr-xr-x scratch/mnt/lower +1: drwxr-xr-x scratch/mnt/upper +1: drwxr-xr-x scratch/mnt/upper/baz +2: w--------- scratch/mnt/upper/bar +3: -rw-r--r-- scratch/mnt/lower/bar +3: -rw-r--r-- scratch/mnt/lower/baz +3: -rw-r--r-- scratch/mnt/lower/foo +3: -rw-r--r-- scratch/mnt/upper/baz/qux +3: -rw-r--r-- scratch/mnt/upper/foo +3: drwxr-xr-x scratch/mnt +3: drwxr-xr-x scratch/mnt/lower +3: drwxr-xr-x scratch/mnt/upper +3: drwxr-xr-x scratch/mnt/upper/baz +3: w--------- scratch/mnt/upper/bar +4: -rw-r--r-- scratch/mnt/lower/bar +4: -rw-r--r-- scratch/mnt/lower/baz +4: -rw-r--r-- scratch/mnt/lower/foo +4: -rw-r--r-- scratch/mnt/upper/baz/qux +4: drwxr-xr-x scratch/mnt +4: drwxr-xr-x scratch/mnt/lower +4: drwxr-xr-x scratch/mnt/upper +4: drwxr-xr-x scratch/mnt/upper/baz +5: w--------- scratch/mnt/upper/bar +6: -rw-r--r-- scratch/mnt/lower/bar +6: -rw-r--r-- scratch/mnt/lower/baz +6: -rw-r--r-- scratch/mnt/lower/foo +6: -rw-r--r-- scratch/mnt/upper/baz/qux +6: drwxr-xr-x scratch/mnt +6: drwxr-xr-x scratch/mnt/lower +6: drwxr-xr-x scratch/mnt/upper +6: drwxr-xr-x scratch/mnt/upper/baz +6: w--------- scratch/mnt/upper/bar diff --git a/tests/bsd/type_w.sh b/tests/bsd/type_w.sh new file mode 100644 index 0000000..d0cb58c --- /dev/null +++ b/tests/bsd/type_w.sh @@ -0,0 +1,64 @@ +# Only ffs supports whiteouts on FreeBSD +command -v mdconfig &>/dev/null || skip +command -v newfs &>/dev/null || skip + +cleanup=() +do_cleanup() { + # Run cleanup hooks in reverse order + while ((${#cleanup[@]} > 0)); do + cmd="${cleanup[-1]}" + unset 'cleanup[-1]' + eval "bfs_sudo $cmd" + done +} +trap do_cleanup EXIT + +clean_scratch + +# Create a ramdisk +truncate -s1M scratch/img +md=$(bfs_sudo mdconfig scratch/img) || skip +cleanup+=("mdconfig -du $md") + +# Make an ffs filesystem +bfs_sudo newfs -n "/dev/$md" >&2 || skip +mkdir scratch/mnt + +# Mount it +bfs_sudo mount "/dev/$md" scratch/mnt || skip +cleanup+=("umount scratch/mnt") + +# Make it owned by us +bfs_sudo chown "$(id -u):$(id -g)" scratch/mnt +"$XTOUCH" -p scratch/mnt/{lower/{foo,bar,baz},upper/{bar,baz/qux}} + +# Mount a union filesystem within it +bfs_sudo mount -t unionfs -o below scratch/mnt/{lower,upper} +cleanup+=("umount scratch/mnt/upper") + +# Create a whiteout +rm scratch/mnt/upper/bar + +# FreeBSD find doesn't have -printf, so munge -ls output +munge_ls() { + sed -En 's|.*([-drwx]{10}).*(scratch/.*)|'"$1"': \1 \2|p' +} + +# Do a few tests in one +{ + # Normally, we shouldn't see the whiteouts + invoke_bfs scratch/mnt -ls | munge_ls 1 + # -type w adds whiteouts to the output + invoke_bfs scratch/mnt -type w -ls | munge_ls 2 + # So this is not the same as test 1 + invoke_bfs scratch/mnt \( -type w -or -not -type w \) -ls | munge_ls 3 + # Unmount the unionfs + bfs_sudo umount scratch/mnt/upper + unset 'cleanup[-1]' + # Now repeat the same tests + invoke_bfs scratch/mnt -ls | munge_ls 4 + invoke_bfs scratch/mnt -type w -ls | munge_ls 5 + invoke_bfs scratch/mnt \( -type w -or -not -type w \) -ls | munge_ls 6 +} >"$OUT" +sort_output +diff_output -- cgit v1.2.3 From b1c6199ad82de2081f18eba931a787a25eb27ea7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 08:04:30 -0400 Subject: tests: Rename --noclean -> --no-clean --- tests/tests.sh | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 61e4aba..282a428 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -94,7 +94,7 @@ function usage() { local pad=$(printf "%*s" ${#0} "") cat < Date: Thu, 19 Oct 2023 09:31:33 -0400 Subject: tests: New defer function --- tests/bsd/type_w.sh | 20 ++++---------------- tests/common/L_mount.sh | 2 +- tests/common/inum_bind_mount.sh | 2 +- tests/common/inum_mount.sh | 2 +- tests/common/mount.sh | 2 +- tests/gnu/fstype_stacked.sh | 4 ++-- tests/gnu/fstype_umount.sh | 2 +- tests/gnu/inum_automount.sh | 2 +- tests/gnu/printf_Y_error.sh | 2 +- tests/gnu/xtype_bind_mount.sh | 2 +- tests/posix/L_xdev.sh | 2 +- tests/posix/depth_error.sh | 2 +- tests/posix/overlayfs.sh | 6 +++++- tests/posix/readdir_error.sh | 2 +- tests/posix/type_bind_mount.sh | 2 +- tests/posix/unionfs.sh | 2 +- tests/posix/xdev.sh | 2 +- tests/tests.sh | 30 +++++++++++++++++++++++++++++- 18 files changed, 54 insertions(+), 34 deletions(-) (limited to 'tests') diff --git a/tests/bsd/type_w.sh b/tests/bsd/type_w.sh index d0cb58c..15b4e68 100644 --- a/tests/bsd/type_w.sh +++ b/tests/bsd/type_w.sh @@ -2,23 +2,12 @@ command -v mdconfig &>/dev/null || skip command -v newfs &>/dev/null || skip -cleanup=() -do_cleanup() { - # Run cleanup hooks in reverse order - while ((${#cleanup[@]} > 0)); do - cmd="${cleanup[-1]}" - unset 'cleanup[-1]' - eval "bfs_sudo $cmd" - done -} -trap do_cleanup EXIT - clean_scratch # Create a ramdisk truncate -s1M scratch/img md=$(bfs_sudo mdconfig scratch/img) || skip -cleanup+=("mdconfig -du $md") +defer bfs_sudo mdconfig -du "$md" # Make an ffs filesystem bfs_sudo newfs -n "/dev/$md" >&2 || skip @@ -26,7 +15,7 @@ mkdir scratch/mnt # Mount it bfs_sudo mount "/dev/$md" scratch/mnt || skip -cleanup+=("umount scratch/mnt") +defer bfs_sudo umount scratch/mnt # Make it owned by us bfs_sudo chown "$(id -u):$(id -g)" scratch/mnt @@ -34,7 +23,7 @@ bfs_sudo chown "$(id -u):$(id -g)" scratch/mnt # Mount a union filesystem within it bfs_sudo mount -t unionfs -o below scratch/mnt/{lower,upper} -cleanup+=("umount scratch/mnt/upper") +defer bfs_sudo umount scratch/mnt/upper # Create a whiteout rm scratch/mnt/upper/bar @@ -53,8 +42,7 @@ munge_ls() { # So this is not the same as test 1 invoke_bfs scratch/mnt \( -type w -or -not -type w \) -ls | munge_ls 3 # Unmount the unionfs - bfs_sudo umount scratch/mnt/upper - unset 'cleanup[-1]' + pop_defer # Now repeat the same tests invoke_bfs scratch/mnt -ls | munge_ls 4 invoke_bfs scratch/mnt -type w -ls | munge_ls 5 diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh index b04acd0..aaf9069 100644 --- a/tests/common/L_mount.sh +++ b/tests/common/L_mount.sh @@ -4,7 +4,7 @@ clean_scratch mkdir scratch/{foo,mnt} bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -trap "bfs_sudo umount scratch/mnt" EXIT +defer bfs_sudo umount scratch/mnt ln -s ../mnt scratch/foo/bar "$XTOUCH" scratch/mnt/baz diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh index ecb4ec3..47f7c36 100644 --- a/tests/common/inum_bind_mount.sh +++ b/tests/common/inum_bind_mount.sh @@ -4,6 +4,6 @@ clean_scratch "$XTOUCH" scratch/{foo,bar} bfs_sudo mount --bind scratch/{foo,bar} || skip -trap "bfs_sudo umount scratch/bar" EXIT +defer bfs_sudo umount scratch/bar bfs_diff scratch -inum "$(inum scratch/bar)" diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh index a4832e4..1bf2d86 100644 --- a/tests/common/inum_mount.sh +++ b/tests/common/inum_mount.sh @@ -4,6 +4,6 @@ clean_scratch mkdir scratch/{foo,mnt} bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -trap "bfs_sudo umount scratch/mnt" EXIT +defer bfs_sudo umount scratch/mnt bfs_diff scratch -inum "$(inum scratch/mnt)" diff --git a/tests/common/mount.sh b/tests/common/mount.sh index b13b43c..db8b801 100644 --- a/tests/common/mount.sh +++ b/tests/common/mount.sh @@ -4,7 +4,7 @@ clean_scratch mkdir scratch/{foo,mnt} bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -trap "bfs_sudo umount scratch/mnt" EXIT +defer bfs_sudo umount scratch/mnt "$XTOUCH" scratch/foo/bar scratch/mnt/baz diff --git a/tests/gnu/fstype_stacked.sh b/tests/gnu/fstype_stacked.sh index 16f428f..a4b067a 100644 --- a/tests/gnu/fstype_stacked.sh +++ b/tests/gnu/fstype_stacked.sh @@ -4,9 +4,9 @@ clean_scratch mkdir scratch/mnt bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -trap "bfs_sudo umount scratch/mnt" EXIT +defer bfs_sudo umount scratch/mnt bfs_sudo mount -t ramfs ramfs scratch/mnt || skip -trap "bfs_sudo umount scratch/mnt; bfs_sudo umount scratch/mnt" EXIT +defer bfs_sudo umount scratch/mnt bfs_diff scratch/mnt -fstype ramfs -print -o -printf '%p: %F\n' diff --git a/tests/gnu/fstype_umount.sh b/tests/gnu/fstype_umount.sh index e817831..b6da7a3 100644 --- a/tests/gnu/fstype_umount.sh +++ b/tests/gnu/fstype_umount.sh @@ -4,7 +4,7 @@ clean_scratch mkdir scratch/tmp bfs_sudo mount -t tmpfs tmpfs scratch/tmp || skip -trap "bfs_sudo umount -R scratch/tmp" EXIT +defer bfs_sudo umount -R scratch/tmp mkdir scratch/tmp/ram bfs_sudo mount -t ramfs ramfs scratch/tmp/ram || skip diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh index c4450ef..261a4be 100644 --- a/tests/gnu/inum_automount.sh +++ b/tests/gnu/inum_automount.sh @@ -6,7 +6,7 @@ clean_scratch mkdir scratch/{foo,automnt} bfs_sudo systemd-mount -A -o bind basic scratch/automnt || skip -trap "bfs_sudo systemd-umount scratch/automnt" EXIT +defer bfs_sudo systemd-umount scratch/automnt before=$(inum scratch/automnt) bfs_diff scratch -inum "$before" -prune diff --git a/tests/gnu/printf_Y_error.sh b/tests/gnu/printf_Y_error.sh index 3aa816e..13d52e3 100644 --- a/tests/gnu/printf_Y_error.sh +++ b/tests/gnu/printf_Y_error.sh @@ -3,6 +3,6 @@ mkdir scratch/foo ln -s foo/bar scratch/bar chmod -x scratch/foo -trap "chmod +x scratch/foo" EXIT +defer chmod +x scratch/foo ! bfs_diff scratch -printf '(%p) (%l) %y %Y\n' diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh index 99a11ab..1cc20ec 100644 --- a/tests/gnu/xtype_bind_mount.sh +++ b/tests/gnu/xtype_bind_mount.sh @@ -5,6 +5,6 @@ clean_scratch ln -s /dev/null scratch/link bfs_sudo mount --bind /dev/null scratch/null || skip -trap "bfs_sudo umount scratch/null" EXIT +defer bfs_sudo umount scratch/null bfs_diff -L scratch -type c diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh index 172ea23..d16c211 100644 --- a/tests/posix/L_xdev.sh +++ b/tests/posix/L_xdev.sh @@ -4,7 +4,7 @@ clean_scratch mkdir scratch/{foo,mnt} bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -trap "bfs_sudo umount scratch/mnt" EXIT +defer bfs_sudo umount scratch/mnt ln -s ../mnt scratch/foo/bar "$XTOUCH" scratch/mnt/baz diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh index e91fbf6..a6429d1 100644 --- a/tests/posix/depth_error.sh +++ b/tests/posix/depth_error.sh @@ -2,6 +2,6 @@ clean_scratch "$XTOUCH" -p scratch/foo/bar chmod a-r scratch/foo -trap "chmod +r scratch/foo" EXIT +defer chmod +r scratch/foo ! bfs_diff scratch -depth diff --git a/tests/posix/overlayfs.sh b/tests/posix/overlayfs.sh index a56b5b3..4cccebf 100644 --- a/tests/posix/overlayfs.sh +++ b/tests/posix/overlayfs.sh @@ -1,7 +1,11 @@ test "$UNAME" = "Linux" || skip + clean_scratch "$XTOUCH" -p scratch/{lower/{foo,bar,baz},upper/{bar,baz/qux}} + mkdir -p scratch/{work,merged} bfs_sudo mount -t overlay overlay -olowerdir=scratch/lower,upperdir=scratch/upper,workdir=scratch/work scratch/merged || skip -trap "bfs_sudo umount scratch/merged; bfs_sudo rm -rf scratch/work" EXIT +defer bfs_sudo rm -rf scratch/work +defer bfs_sudo umount scratch/merged + bfs_diff scratch/merged diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh index 9a002a1..fc48eb1 100644 --- a/tests/posix/readdir_error.sh +++ b/tests/posix/readdir_error.sh @@ -13,7 +13,7 @@ mkfifo scratch/{fever,pid,wait,running} ) & # Kill the parent cat on exit -trap "kill -9 %1" EXIT +defer kill -9 %1 # Read the child PID read -r pid 0)); do + pop_defer || ret=$? + done + + return $ret +} + function bfs_sudo() { if ((${#SUDO[@]})); then "${SUDO[@]}" "$@" @@ -304,7 +332,7 @@ function cleanup() { } if [ "$CLEAN" ]; then - trap cleanup EXIT + defer cleanup else echo "Test files saved to $TMP" fi -- cgit v1.2.3 From e527c1b288e15eadead25e01c29b5e8b074601a7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 09:34:58 -0400 Subject: tests: Fix uses of $? with set -e --- tests/tests.sh | 22 ++++++++++++---------- 1 file changed, 12 insertions(+), 10 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index b159ea2..83f5b68 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -507,14 +507,15 @@ function bfs_verbose() { function invoke_bfs() { bfs_verbose "$@" - "${BFS[@]}" "$@" - local status="$?" + + local ret=0 + "${BFS[@]}" "$@" || ret=$? # Allow bfs to fail, but not crash - if ((status > 125)); then - exit "$status" + if ((ret > 125)); then + exit "$ret" else - return "$status" + return "$ret" fi } @@ -528,13 +529,14 @@ function bfs_pty() { test -n "${UNBUFFER:-}" || skip bfs_verbose "$@" - "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" - local status="$?" - if ((status > 125)); then - exit "$status" + local ret=0 + "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" || ret=$? + + if ((ret > 125)); then + exit "$ret" else - return "$status" + return "$ret" fi } -- cgit v1.2.3 From ee11047140578029ff6d6bd27ac733b03c3e737c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 10:19:45 -0400 Subject: tests: Use the ERR trap to print the failing command --- tests/tests.sh | 50 ++++++++++++++++++++++++++++++++++++++++++++++---- 1 file changed, 46 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 83f5b68..c601f8a 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -260,6 +260,14 @@ if (( ${#TEST_CASES[@]} == 0 )); then exit 1 fi +function quote() { + printf '%q' "$1" + shift + if (($# > 0)); then + printf ' %q' "$@" + fi +} + DEFER=() # Run a command when this (sub)shell exits @@ -269,13 +277,21 @@ function defer() { DEFER=() trap pop_defers EXIT fi - DEFER+=("$(printf '%q ' "$@")") + DEFER+=("$(quote "$@")") } function pop_defer() { local cmd="${DEFER[-1]}" unset "DEFER[-1]" - eval "$cmd" + + local ret=0 + eval "$cmd" || ret=$? + + if ((ret != 0)); then + printf 'defer %s: error %d\n' "$cmd" $ret >&2 + fi + + return $ret } function pop_defers() { @@ -703,6 +719,32 @@ elif color_fd 1; then trap update_eol WINCH fi +function callers() { + local frame=0 + while caller $frame; do + ((++frame)) + done +} + +function debug_err() { + local ret=$? + local line func file + callers | while read -r line func file; do + if [ "$func" = source ]; then + local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : + file="${file/#*\/tests\//tests\/}" + printf '%s:%d: %s: error %d\n' "$file" "$line" "$cmd" "$ret" >&2 + break + fi + done +} + +function run_test() ( + set -eE + trap debug_err ERR + source "$@" +) + passed=0 failed=0 skipped=0 @@ -720,9 +762,9 @@ for TEST in "${TEST_CASES[@]}"; do mkdir -p "${OUT%/*}" if [ "$VERBOSE_ERRORS" ]; then - (set -e; . "$TESTS/$TEST.sh") + run_test "$TESTS/$TEST.sh" else - (set -e; . "$TESTS/$TEST.sh") 2>"$TMP/$TEST.err" + run_test "$TESTS/$TEST.sh" 2>"$TMP/$TEST.err" fi status=$? -- cgit v1.2.3 From 31442d6bdaf6a7490e1e31fd9a676525f8a05a6d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 11:31:31 -0400 Subject: tests: Clean up colorization --- tests/tests.sh | 178 +++++++++++++++++++++++++++++++++------------------------ 1 file changed, 102 insertions(+), 76 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index c601f8a..5c8fca8 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -19,29 +19,35 @@ export UBSAN_OPTIONS="$SAN_OPTIONS" export LS_COLORS="" unset BFS_COLORS +BLD=$'\e[01m' +RED=$'\e[01;31m' +GRN=$'\e[01;32m' +YLW=$'\e[01;33m' +BLU=$'\e[01;34m' +MAG=$'\e[01;35m' +CYN=$'\e[01;36m' +RST=$'\e[0m' + function color_fd() { [ -z "${NO_COLOR:-}" ] && [ -t "$1" ] } -if color_fd 1; then - BLD=$'\033[01m' - RED=$'\033[01;31m' - GRN=$'\033[01;32m' - YLW=$'\033[01;33m' - BLU=$'\033[01;34m' - MAG=$'\033[01;35m' - CYN=$'\033[01;36m' - RST=$'\033[0m' -else - BLD= - RED= - GRN= - YLW= - BLU= - MAG= - CYN= - RST= -fi +color_fd 1 && COLOR_STDOUT=1 || COLOR_STDOUT=0 +color_fd 2 && COLOR_STDERR=1 || COLOR_STDERR=0 + +# Filter out escape sequences if necessary +function color() { + if color_fd 1; then + cat + else + sed $'s/\e\\[[^m]*m//g' + fi +} + +# printf with auto-detected color support +function cprintf() { + printf "$@" | color +} UNAME=$(uname) @@ -59,14 +65,14 @@ fi if command -v capsh &>/dev/null; then if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then if [ -n "${BFS_TRIED_DROP:-}" ]; then - cat >&2 <&2 <&2 <&2 <&2 <&2 <&2 + cprintf "${RED}error:${RST} Unrecognized option '%s'.\n\n" "$arg" >&2 usage >&2 exit 1 ;; @@ -237,7 +243,7 @@ chown "$(id -u):$(id -g)" "$TMP" cd "$TESTS" -if (( ${#PATTERNS[@]} == 0 )); then +if ((${#PATTERNS[@]} == 0)); then PATTERNS=("*") fi @@ -252,10 +258,10 @@ for TEST in {posix,common,bsd,gnu,bfs}/*.sh; do done done -if (( ${#TEST_CASES[@]} == 0 )); then - printf "${RED}error:${RST} No tests matched" >&2 - printf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 - printf ".\n\n" >&2 +if ((${#TEST_CASES[@]} == 0)); then + cprintf "${RED}error:${RST} No tests matched" >&2 + cprintf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 + cprintf ".\n\n" >&2 usage >&2 exit 1 fi @@ -268,27 +274,48 @@ function quote() { fi } -DEFER=() - # Run a command when this (sub)shell exits function defer() { trap -- KILL if ! trap -p EXIT | grep -q pop_defers; then - DEFER=() + DEFER_CMDS=() + DEFER_LINES=() + DEFER_FILES=() trap pop_defers EXIT fi - DEFER+=("$(quote "$@")") + + DEFER_CMDS+=("$(quote "$@")") + + local line file + read -r line file < <(caller) + DEFER_LINES+=("$line") + DEFER_FILES+=("$file") +} + +function report_err() { + local file="${1/#*\/tests\//tests\/}" + set -- "$file" "${@:2}" + + if ((COLOR_STDERR)); then + printf "${BLD}%s:%d:${RST} ${RED}error %d:${RST}\n %s\n" "$@" >&2 + else + printf "%s:%d: error %d:\n %s\n" "$@" >&2 + fi } function pop_defer() { - local cmd="${DEFER[-1]}" - unset "DEFER[-1]" + local cmd="${DEFER_CMDS[-1]}" + local file="${DEFER_FILES[-1]}" + local line="${DEFER_LINES[-1]}" + unset "DEFER_CMDS[-1]" + unset "DEFER_FILES[-1]" + unset "DEFER_LINES[-1]" local ret=0 eval "$cmd" || ret=$? if ((ret != 0)); then - printf 'defer %s: error %d\n' "$cmd" $ret >&2 + report_err "$file" $line $ret "defer $cmd" fi return $ret @@ -297,7 +324,7 @@ function pop_defer() { function pop_defers() { local ret=0 - while ((${#DEFER[@]} > 0)); do + while ((${#DEFER_CMDS[@]} > 0)); do pop_defer || ret=$? done @@ -492,33 +519,34 @@ if [ "$VERBOSE_COMMANDS" ]; then fi function bfs_verbose() { - if [ "$VERBOSE_COMMANDS" ]; then - if color_fd 3; then - printf "${GRN}%q${RST} " "${BFS[@]}" >&3 - - local expr_started= - for arg; do - if [[ $arg == -[A-Z]* ]]; then - printf "${CYN}%q${RST} " "$arg" >&3 - elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then - expr_started=yes - printf "${RED}%q${RST} " "$arg" >&3 - elif [[ $expr_started && $arg == [\),] ]]; then - printf "${RED}%q${RST} " "$arg" >&3 - elif [[ $arg == -?* ]]; then - expr_started=yes - printf "${BLU}%q${RST} " "$arg" >&3 - elif [ "$expr_started" ]; then - printf "${BLD}%q${RST} " "$arg" >&3 - else - printf "${MAG}%q${RST} " "$arg" >&3 - fi - done - else - printf '%q ' "${BFS[@]}" "$@" >&3 - fi - printf '\n' >&3 + if ! [ "$VERBOSE_COMMANDS" ]; then + return fi + + { + printf "${GRN}%q${RST} " "${BFS[@]}" + + local expr_started= + for arg; do + if [[ $arg == -[A-Z]* ]]; then + printf "${CYN}%q${RST} " "$arg" + elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then + expr_started=yes + printf "${RED}%q${RST} " "$arg" + elif [[ $expr_started && $arg == [\),] ]]; then + printf "${RED}%q${RST} " "$arg" + elif [[ $arg == -?* ]]; then + expr_started=yes + printf "${BLU}%q${RST} " "$arg" + elif [ "$expr_started" ]; then + printf "${BLD}%q${RST} " "$arg" + else + printf "${MAG}%q${RST} " "$arg" + fi + done + + printf '\n' + } | color >&3 } function invoke_bfs() { @@ -565,7 +593,7 @@ function check_exit() { } # Detect colored diff support -if color_fd 2 && diff --color=always /dev/null /dev/null 2>/dev/null; then +if ((COLOR_STDERR)) && diff --color=always /dev/null /dev/null 2>/dev/null; then DIFF="diff --color=always" else DIFF="diff" @@ -608,10 +636,10 @@ function skip() { if [ "$VERBOSE_SKIPPED" ]; then caller | { read -r line file - printf "${BOL}${CYN}%s skipped!${RST} (%s)\n" "$TEST" "$(awk "NR == $line" "$file")" + cprintf "${BOL}${CYN}%s skipped!${RST} (%s)\n" "$TEST" "$(awk "NR == $line" "$file")" } elif [ "$VERBOSE_TESTS" ]; then - printf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" + cprintf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" fi exit $EX_SKIP @@ -706,7 +734,7 @@ function update_eol() { if [ "$VERBOSE_TESTS" ]; then BOL='' -elif color_fd 1; then +elif ((COLOR_STDOUT)); then BOL='\r\033[K' # Workaround for bash 4: checkwinsize is off by default. We can turn it on, @@ -727,13 +755,11 @@ function callers() { } function debug_err() { - local ret=$? - local line func file + local ret=$? line func file callers | while read -r line func file; do if [ "$func" = source ]; then local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : - file="${file/#*\/tests\//tests\/}" - printf '%s:%d: %s: error %d\n' "$file" "$line" "$cmd" "$ret" >&2 + report_err "$file" $line $ret "$cmd" break fi done @@ -749,7 +775,7 @@ passed=0 failed=0 skipped=0 -if color_fd 1 || [ "$VERBOSE_TESTS" ]; then +if ((COLOR_STDOUT)) || [ "$VERBOSE_TESTS" ]; then TEST_FMT="${BOL}${YLW}%s${RST}${EOL}" else TEST_FMT="." @@ -775,7 +801,7 @@ for TEST in "${TEST_CASES[@]}"; do else ((++failed)) [ "$VERBOSE_ERRORS" ] || cat "$TMP/$TEST.err" >&2 - printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" + cprintf "${BOL}${RED}%s failed!${RST}\n" "$TEST" [ "$STOP" ] && break fi done @@ -783,12 +809,12 @@ done printf "${BOL}" if ((passed > 0)); then - printf "${GRN}tests passed: %d${RST}\n" "$passed" + cprintf "${GRN}tests passed: %d${RST}\n" "$passed" fi if ((skipped > 0)); then - printf "${CYN}tests skipped: %s${RST}\n" "$skipped" + cprintf "${CYN}tests skipped: %s${RST}\n" "$skipped" fi if ((failed > 0)); then - printf "${RED}tests failed: %s${RST}\n" "$failed" + cprintf "${RED}tests failed: %s${RST}\n" "$failed" exit 1 fi -- cgit v1.2.3 From d484cba3424dcfca4851ba867d8877e3a9381a0e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 11:39:04 -0400 Subject: tests: Update bash coding style --- tests/tests.sh | 64 +++++++++++++++++++++++++++++----------------------------- 1 file changed, 32 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/tests/tests.sh b/tests/tests.sh index 5c8fca8..dcef28e 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -83,7 +83,7 @@ EOF --caps=cap_dac_override,cap_dac_read_search-eip \ -- "$0" "$@" fi -elif [ "$EUID" -eq 0 ]; then +elif ((EUID == 0)); then UNLESS= if [ "$UNAME" = "Linux" ]; then UNLESS=" unless ${GRN}capsh${RST} is installed" @@ -142,13 +142,13 @@ EOF PATTERNS=() SUDO=() -STOP= -CLEAN=yes -UPDATE= -VERBOSE_COMMANDS= -VERBOSE_ERRORS= -VERBOSE_SKIPPED= -VERBOSE_TESTS= +STOP=0 +CLEAN=1 +UPDATE=0 +VERBOSE_COMMANDS=0 +VERBOSE_ERRORS=0 +VERBOSE_SKIPPED=0 +VERBOSE_TESTS=0 for arg; do case "$arg" in @@ -174,31 +174,31 @@ for arg; do read -a SUDO <<<"${arg#*=}" ;; --stop) - STOP=yes + STOP=1 ;; --no-clean|--noclean) - CLEAN= + CLEAN=0 ;; --update) - UPDATE=yes + UPDATE=1 ;; --verbose=commands) - VERBOSE_COMMANDS=yes + VERBOSE_COMMANDS=1 ;; --verbose=errors) - VERBOSE_ERRORS=yes + VERBOSE_ERRORS=1 ;; --verbose=skipped) - VERBOSE_SKIPPED=yes + VERBOSE_SKIPPED=1 ;; --verbose=tests) - VERBOSE_TESTS=yes + VERBOSE_TESTS=1 ;; --verbose) - VERBOSE_COMMANDS=yes - VERBOSE_ERRORS=yes - VERBOSE_SKIPPED=yes - VERBOSE_TESTS=yes + VERBOSE_COMMANDS=1 + VERBOSE_ERRORS=1 + VERBOSE_SKIPPED=1 + VERBOSE_TESTS=1 ;; --help) usage @@ -374,7 +374,7 @@ function cleanup() { rm -rf "$TMP" } -if [ "$CLEAN" ]; then +if ((CLEAN)); then defer cleanup else echo "Test files saved to $TMP" @@ -513,13 +513,13 @@ mkdir "$TMP/scratch" # Close stdin so bfs doesn't think we're interactive exec &1 fi function bfs_verbose() { - if ! [ "$VERBOSE_COMMANDS" ]; then + if ((!VERBOSE_COMMANDS)); then return fi @@ -611,7 +611,7 @@ function sort_output() { function diff_output() { local GOLD="$TESTS/$TEST.out" - if [ "$UPDATE" ]; then + if ((UPDATE)); then cp "$OUT" "$GOLD" else $DIFF -u "$GOLD" "$OUT" >&2 @@ -633,12 +633,12 @@ function bfs_diff() ( ) function skip() { - if [ "$VERBOSE_SKIPPED" ]; then + if ((VERBOSE_SKIPPED)); then caller | { read -r line file cprintf "${BOL}${CYN}%s skipped!${RST} (%s)\n" "$TEST" "$(awk "NR == $line" "$file")" } - elif [ "$VERBOSE_TESTS" ]; then + elif ((VERBOSE_TESTS)); then cprintf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" fi @@ -658,7 +658,7 @@ function closefrom() { fi local fd="${fd##*/}" - if [ "$fd" -ge "$1" ]; then + if ((fd >= $1)); then eval "exec ${fd}<&-" fi done @@ -674,7 +674,7 @@ function set_acl() { chmod +a "$(id -un) allow read,write" "$1" ;; FreeBSD) - if [ "$(getconf ACL_NFS4 "$1")" -gt 0 ]; then + if (($(getconf ACL_NFS4 "$1") > 0)); then setfacl -m "u:$(id -un):rw::allow" "$1" else setfacl -m "u:$(id -un):rw" "$1" @@ -732,7 +732,7 @@ function update_eol() { EOL="\\033[${cols}G " } -if [ "$VERBOSE_TESTS" ]; then +if ((VERBOSE_TESTS)); then BOL='' elif ((COLOR_STDOUT)); then BOL='\r\033[K' @@ -775,7 +775,7 @@ passed=0 failed=0 skipped=0 -if ((COLOR_STDOUT)) || [ "$VERBOSE_TESTS" ]; then +if ((COLOR_STDOUT || VERBOSE_TESTS)); then TEST_FMT="${BOL}${YLW}%s${RST}${EOL}" else TEST_FMT="." @@ -787,7 +787,7 @@ for TEST in "${TEST_CASES[@]}"; do OUT="$TMP/$TEST.out" mkdir -p "${OUT%/*}" - if [ "$VERBOSE_ERRORS" ]; then + if ((VERBOSE_ERRORS)); then run_test "$TESTS/$TEST.sh" else run_test "$TESTS/$TEST.sh" 2>"$TMP/$TEST.err" @@ -800,9 +800,9 @@ for TEST in "${TEST_CASES[@]}"; do ((++skipped)) else ((++failed)) - [ "$VERBOSE_ERRORS" ] || cat "$TMP/$TEST.err" >&2 + ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 cprintf "${BOL}${RED}%s failed!${RST}\n" "$TEST" - [ "$STOP" ] && break + ((STOP)) && break fi done -- cgit v1.2.3 From 785a3f2d777627f39bed44f4ae7a0180d5184109 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 16:37:47 -0400 Subject: tests: Refactor implementation into separate files --- tests/bfs/deep_strict.sh | 2 - tests/color.sh | 43 +++ tests/common/execdir_ulimit.sh | 1 - tests/getopts.sh | 158 ++++++++ tests/gnu/printf_u_g_ulimit.sh | 1 - tests/ls-color.sh | 6 +- tests/posix/deep.sh | 2 - tests/posix/nogroup_ulimit.sh | 1 - tests/posix/nouser_ulimit.sh | 1 - tests/run.sh | 316 ++++++++++++++++ tests/stddirs.sh | 185 +++++++++ tests/tests.sh | 824 +---------------------------------------- tests/util.sh | 189 ++++++++++ 13 files changed, 906 insertions(+), 823 deletions(-) create mode 100644 tests/color.sh create mode 100644 tests/getopts.sh create mode 100644 tests/run.sh create mode 100644 tests/stddirs.sh create mode 100644 tests/util.sh (limited to 'tests') diff --git a/tests/bfs/deep_strict.sh b/tests/bfs/deep_strict.sh index e057310..50c8f05 100644 --- a/tests/bfs/deep_strict.sh +++ b/tests/bfs/deep_strict.sh @@ -1,5 +1,3 @@ -closefrom 4 - # Not even enough fds to keep the root open ulimit -n 7 bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \; diff --git a/tests/color.sh b/tests/color.sh new file mode 100644 index 0000000..0d6ef68 --- /dev/null +++ b/tests/color.sh @@ -0,0 +1,43 @@ +#!/hint/bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +## Colored output + +# Common escape sequences +BLD=$'\e[01m' +RED=$'\e[01;31m' +GRN=$'\e[01;32m' +YLW=$'\e[01;33m' +BLU=$'\e[01;34m' +MAG=$'\e[01;35m' +CYN=$'\e[01;36m' +RST=$'\e[0m' + +# Check if we should color output to the given fd +color_fd() { + [ -z "${NO_COLOR:-}" ] && [ -t "$1" ] +} + +# Cache the color status for std{out,err} +color_fd 1 && COLOR_STDOUT=1 || COLOR_STDOUT=0 +color_fd 2 && COLOR_STDERR=1 || COLOR_STDERR=0 + +# Save these in case the tests unset PATH +CAT=$(command -v cat) +SED=$(command -v sed) + +# Filter out escape sequences if necessary +color() { + if color_fd 1; then + "$CAT" + else + "$SED" $'s/\e\\[[^m]*m//g' + fi +} + +# printf with auto-detected color support +cprintf() { + printf "$@" | color +} diff --git a/tests/common/execdir_ulimit.sh b/tests/common/execdir_ulimit.sh index 8bd9edd..f7fc467 100644 --- a/tests/common/execdir_ulimit.sh +++ b/tests/common/execdir_ulimit.sh @@ -2,6 +2,5 @@ clean_scratch mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C -closefrom 4 ulimit -n 13 bfs_diff scratch -execdir echo {} \; diff --git a/tests/getopts.sh b/tests/getopts.sh new file mode 100644 index 0000000..6616a4a --- /dev/null +++ b/tests/getopts.sh @@ -0,0 +1,158 @@ +#!/hint/bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +## Argument parsing + +# Print usage information +usage() { + local pad=$(printf "%*s" ${#0} "") + color <&2 + usage >&2 + exit 1 + ;; + *) + PATTERNS+=("$arg") + ;; + esac + done + + # Try to resolve the path to $BFS before we cd, while also supporting + # --bfs="./bin/bfs -S ids" + read -a BFS <<<"${BFS:-$BIN/bfs}" + BFS[0]=$(_realpath "$(command -v "${BFS[0]}")") + + if ((${#PATTERNS[@]} == 0)); then + PATTERNS=("*") + fi + + TEST_CASES=() + ALL_TESTS=($(cd "$TESTS" && quote {posix,common,bsd,gnu,bfs}/*.sh)) + for TEST in "${ALL_TESTS[@]}"; do + TEST="${TEST%.sh}" + for PATTERN in "${PATTERNS[@]}"; do + if [[ $TEST == $PATTERN ]]; then + TEST_CASES+=("$TEST") + break + fi + done + done + + if ((${#TEST_CASES[@]} == 0)); then + cprintf "${RED}error:${RST} No tests matched" >&2 + cprintf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 + cprintf ".\n\n" >&2 + usage >&2 + exit 1 + fi +} diff --git a/tests/gnu/printf_u_g_ulimit.sh b/tests/gnu/printf_u_g_ulimit.sh index a84ee29..390ad48 100644 --- a/tests/gnu/printf_u_g_ulimit.sh +++ b/tests/gnu/printf_u_g_ulimit.sh @@ -1,3 +1,2 @@ -closefrom 4 ulimit -n 16 [ "$(invoke_bfs deep -printf '%u %g\n' | uniq)" = "$(id -un) $(id -gn)" ] diff --git a/tests/ls-color.sh b/tests/ls-color.sh index 9fdd59c..b9a0402 100755 --- a/tests/ls-color.sh +++ b/tests/ls-color.sh @@ -7,7 +7,7 @@ set -e -function parse_ls_colors() { +parse_ls_colors() { for key; do local -n var="$key" if [[ "$LS_COLORS" =~ (^|:)$key=(([^:]|\\:)*) ]]; then @@ -18,7 +18,7 @@ function parse_ls_colors() { done } -function re_escape() { +re_escape() { # https://stackoverflow.com/a/29613573/502399 sed 's/[^^]/[&]/g; s/\^/\\^/g' <<<"$1" } @@ -34,7 +34,7 @@ parse_ls_colors rs lc rc ec no strip="(($(re_escape "$lc$no$rc"))?($(re_escape "$ec")|$(re_escape "$lc$rc")))+" -function ls_color() { +ls_color() { # Strip the leading reset sequence from the ls output ls -1d --color "$@" | sed -E "s/^$strip([a-z].*)$strip/\4/; s/^$strip//" } diff --git a/tests/posix/deep.sh b/tests/posix/deep.sh index 3d1cd60..431705e 100644 --- a/tests/posix/deep.sh +++ b/tests/posix/deep.sh @@ -1,4 +1,2 @@ -closefrom 4 - ulimit -n 16 bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \; diff --git a/tests/posix/nogroup_ulimit.sh b/tests/posix/nogroup_ulimit.sh index 8f758c4..2186321 100644 --- a/tests/posix/nogroup_ulimit.sh +++ b/tests/posix/nogroup_ulimit.sh @@ -1,4 +1,3 @@ -closefrom 4 ulimit -n 16 # -mindepth 18, but POSIX diff --git a/tests/posix/nouser_ulimit.sh b/tests/posix/nouser_ulimit.sh index 2777589..be0a65f 100644 --- a/tests/posix/nouser_ulimit.sh +++ b/tests/posix/nouser_ulimit.sh @@ -1,4 +1,3 @@ -closefrom 4 ulimit -n 16 # -mindepth 18, but POSIX diff --git a/tests/run.sh b/tests/run.sh new file mode 100644 index 0000000..70c9cc2 --- /dev/null +++ b/tests/run.sh @@ -0,0 +1,316 @@ +#!/hint/bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +## Running test cases + +# Beginning/end of line escape sequences +BOL=$'\n' +EOL=$'\n' + +# Update $EOL for the terminal size +update_eol() { + # Bash gets $COLUMNS from stderr, so if it's redirected use tput instead + local cols="${COLUMNS-}" + if [ -z "$cols" ]; then + cols=$(tput cols) + fi + + # Put the cursor at the last column, then write a space so the next + # character will wrap + EOL=$'\e['"${cols}G " +} + +# ERR trap for tests +debug_err() { + local ret=$? line func file + callers | while read -r line func file; do + if [ "$func" = source ]; then + local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : + debug "$file" $line "${RED}error $ret${RST}" "$cmd" >&4 + break + fi + done +} + +# Run a single test +run_test() ( + set -eE + trap debug_err ERR + cd "$TMP" + source "$@" +) + +# Run all the tests +run_tests() { + if ((VERBOSE_TESTS)); then + BOL='' + elif ((COLOR_STDOUT)); then + # Carriage return + clear line + BOL=$'\r\e[K' + + # Workaround for bash 4: checkwinsize is off by default. We can turn it + # on, but we also have to explicitly trigger a foreground job to finish + # so that it will update the window size before we use $COLUMNS + shopt -s checkwinsize + (:) + + update_eol + trap update_eol WINCH + fi + + passed=0 + failed=0 + skipped=0 + + if ((COLOR_STDOUT || VERBOSE_TESTS)); then + TEST_FMT="${BOL}${YLW}%s${RST}${EOL}" + else + TEST_FMT="." + fi + + # Turn off set -e (but turn it back on in run_test) + set +e + + for TEST in "${TEST_CASES[@]}"; do + printf "$TEST_FMT" "$TEST" + + OUT="$TMP/$TEST.out" + mkdir -p "${OUT%/*}" + + if ((VERBOSE_ERRORS)); then + run_test "$TESTS/$TEST.sh" + else + run_test "$TESTS/$TEST.sh" 2>"$TMP/$TEST.err" + fi + status=$? + + if ((status == 0)); then + ((++passed)) + elif ((status == EX_SKIP)); then + ((++skipped)) + else + ((++failed)) + ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 + cprintf "${BOL}${RED}%s failed!${RST}\n" "$TEST" + ((STOP)) && break + fi + done + + printf "${BOL}" + + if ((passed > 0)); then + cprintf "${GRN}tests passed: %d${RST}\n" "$passed" + fi + if ((skipped > 0)); then + cprintf "${CYN}tests skipped: %s${RST}\n" "$skipped" + fi + if ((failed > 0)); then + cprintf "${RED}tests failed: %s${RST}\n" "$failed" + exit 1 + fi +} + +## Utilities for the tests themselves + +# Return value when a test is skipped +EX_SKIP=77 + +# Skip the current test +skip() { + if ((VERBOSE_SKIPPED)); then + caller | { + read -r line file + printf "${BOL}" + debug "$file" $line "${CYN}$TEST skipped!${RST}" "$(awk "NR == $line" "$file")" >&3 + } + elif ((VERBOSE_TESTS)); then + cprintf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" + fi + + exit $EX_SKIP +} + +# Run a command and check its exit status +check_exit() { + local expected="$1" + local actual="0" + shift + "$@" || actual="$?" + ((actual == expected)) +} + +# Run a command with sudo +bfs_sudo() { + if ((${#SUDO[@]})); then + "${SUDO[@]}" "$@" + else + return 1 + fi +} + +# Get the inode number of a file +inum() { + ls -id "$@" | awk '{ print $1 }' +} + +# Set an ACL on a file +set_acl() { + case "$UNAME" in + Darwin) + chmod +a "$(id -un) allow read,write" "$1" + ;; + FreeBSD) + if (($(getconf ACL_NFS4 "$1") > 0)); then + setfacl -m "u:$(id -un):rw::allow" "$1" + else + setfacl -m "u:$(id -un):rw" "$1" + fi + ;; + *) + setfacl -m "u:$(id -un):rw" "$1" + ;; + esac +} + +# Print a bfs invocation for --verbose=commands +bfs_verbose() ( + if ((!VERBOSE_COMMANDS)); then + return + fi + + # Free up an fd for the pipe + exec 4>&- + + { + printf "${GRN}%q${RST} " "${BFS[@]}" + + local expr_started= + for arg; do + if [[ $arg == -[A-Z]* ]]; then + printf "${CYN}%q${RST} " "$arg" + elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then + expr_started=yes + printf "${RED}%q${RST} " "$arg" + elif [[ $expr_started && $arg == [\),] ]]; then + printf "${RED}%q${RST} " "$arg" + elif [[ $arg == -?* ]]; then + expr_started=yes + printf "${BLU}%q${RST} " "$arg" + elif [ "$expr_started" ]; then + printf "${BLD}%q${RST} " "$arg" + else + printf "${MAG}%q${RST} " "$arg" + fi + done + + printf '\n' + } | color >&3 +) + +# Run the bfs we're testing +invoke_bfs() { + bfs_verbose "$@" + + local ret=0 + # Close the logging fds + "${BFS[@]}" "$@" 3>&- 4>&- || ret=$? + + # Allow bfs to fail, but not crash + if ((ret > 125)); then + exit "$ret" + else + return "$ret" + fi +} + +if command -v unbuffer &>/dev/null; then + UNBUFFER=unbuffer +elif command -v expect_unbuffer &>/dev/null; then + UNBUFFER=expect_unbuffer +fi + +# Run bfs with a pseudo-terminal attached +bfs_pty() { + test -n "${UNBUFFER:-}" || skip + + bfs_verbose "$@" + + local ret=0 + "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" || ret=$? + + if ((ret > 125)); then + exit "$ret" + else + return "$ret" + fi +} + +# Create a directory tree with xattrs in scratch +make_xattrs() { + clean_scratch + + "$XTOUCH" scratch/{normal,xattr,xattr_2} + ln -s xattr scratch/link + ln -s normal scratch/xattr_link + + case "$UNAME" in + Darwin) + xattr -w bfs_test true scratch/xattr \ + && xattr -w bfs_test_2 true scratch/xattr_2 \ + && xattr -s -w bfs_test true scratch/xattr_link + ;; + FreeBSD) + setextattr user bfs_test true scratch/xattr \ + && setextattr user bfs_test_2 true scratch/xattr_2 \ + && setextattr -h user bfs_test true scratch/xattr_link + ;; + *) + # Linux tmpfs doesn't support the user.* namespace, so we use the security.* + # namespace, which is writable by root and readable by others + bfs_sudo setfattr -n security.bfs_test scratch/xattr \ + && bfs_sudo setfattr -n security.bfs_test_2 scratch/xattr_2 \ + && bfs_sudo setfattr -h -n security.bfs_test scratch/xattr_link + ;; + esac +} + +## Snapshot testing + +# Return value when a difference is detected +EX_DIFF=20 + +# Detect colored diff support +if ((COLOR_STDERR)) && diff --color=always /dev/null /dev/null 2>/dev/null; then + DIFF="diff --color=always" +else + DIFF="diff" +fi + +# Sort the output file +sort_output() { + sort -o "$OUT" "$OUT" +} + +# Diff against the expected output +diff_output() { + local GOLD="$TESTS/$TEST.out" + + if ((UPDATE)); then + cp "$OUT" "$GOLD" + else + $DIFF -u "$GOLD" "$OUT" >&2 + fi +} + +# Run bfs, and diff it against the expected output +bfs_diff() { + local ret=0 + invoke_bfs "$@" >"$OUT" || ret=$? + + sort_output + diff_output || exit $EX_DIFF + + return $ret +} diff --git a/tests/stddirs.sh b/tests/stddirs.sh new file mode 100644 index 0000000..e7f7246 --- /dev/null +++ b/tests/stddirs.sh @@ -0,0 +1,185 @@ +#!/hint/bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +## Standard directory trees for tests + +# Creates a simple file+directory structure for tests +make_basic() { + "$XTOUCH" -p "$1"/{a,b,c/d,e/f,g/h/,i/} + "$XTOUCH" -p "$1"/{j/foo,k/foo/bar,l/foo/bar/baz} + echo baz >"$1/l/foo/bar/baz" +} + +# Creates a file+directory structure with various permissions for tests +make_perms() { + "$XTOUCH" -p -M000 "$1/0" + "$XTOUCH" -p -M444 "$1/r" + "$XTOUCH" -p -M222 "$1/w" + "$XTOUCH" -p -M644 "$1/rw" + "$XTOUCH" -p -M555 "$1/rx" + "$XTOUCH" -p -M311 "$1/wx" + "$XTOUCH" -p -M755 "$1/rwx" +} + +# Creates a file+directory structure with various symbolic and hard links +make_links() { + "$XTOUCH" -p "$1/file" + ln -s file "$1/symlink" + ln "$1/file" "$1/hardlink" + ln -s nowhere "$1/broken" + ln -s symlink/file "$1/notdir" + "$XTOUCH" -p "$1/deeply/nested"/{dir/,file} + ln -s file "$1/deeply/nested/link" + ln -s nowhere "$1/deeply/nested/broken" + ln -s deeply/nested "$1/skip" +} + +# Creates a file+directory structure with symbolic link loops +make_loops() { + "$XTOUCH" -p "$1/file" + ln -s file "$1/symlink" + ln -s nowhere "$1/broken" + ln -s symlink/file "$1/notdir" + ln -s loop "$1/loop" + mkdir -p "$1/deeply/nested/dir" + ln -s ../../deeply "$1/deeply/nested/loop" + ln -s deeply/nested/loop/nested "$1/skip" +} + +# Creates a file+directory structure with varying timestamps +make_times() { + "$XTOUCH" -p -t "1991-12-14 00:00" "$1/a" + "$XTOUCH" -p -t "1991-12-14 00:01" "$1/b" + "$XTOUCH" -p -t "1991-12-14 00:02" "$1/c" + ln -s a "$1/l" + "$XTOUCH" -p -h -t "1991-12-14 00:03" "$1/l" + "$XTOUCH" -p -t "1991-12-14 00:04" "$1" +} + +# Creates a file+directory structure with various weird file/directory names +make_weirdnames() { + "$XTOUCH" -p "$1/-/a" + "$XTOUCH" -p "$1/(/b" + "$XTOUCH" -p "$1/(-/c" + "$XTOUCH" -p "$1/!/d" + "$XTOUCH" -p "$1/!-/e" + "$XTOUCH" -p "$1/,/f" + "$XTOUCH" -p "$1/)/g" + "$XTOUCH" -p "$1/.../h" + "$XTOUCH" -p "$1/\\/i" + "$XTOUCH" -p "$1/ /j" + "$XTOUCH" -p "$1/[/k" +} + +# Creates a very deep directory structure for testing PATH_MAX handling +make_deep() { + mkdir -p "$1" + + # $name will be 255 characters, aka _XOPEN_NAME_MAX + local name="0123456789ABCDEF" + name="${name}${name}${name}${name}" + name="${name}${name}${name}${name}" + name="${name:0:255}" + + for i in {0..9} A B C D E F; do + "$XTOUCH" -p "$1/$i/$name" + + ( + cd "$1/$i" + + # 8 * 512 == 4096 >= PATH_MAX + for _ in {1..8}; do + mv "$name" .. + mkdir -p "$name/$name" + mv "../$name" "$name/$name/" + done + ) + done +} + +# Creates a directory structure with many different types, and therefore colors +make_rainbow() { + "$XTOUCH" -p "$1/file.txt" + "$XTOUCH" -p "$1/file.dat" + "$XTOUCH" -p "$1/lower".{gz,tar,tar.gz} + "$XTOUCH" -p "$1/upper".{GZ,TAR,TAR.GZ} + "$XTOUCH" -p "$1/lu.tar.GZ" "$1/ul.TAR.gz" + ln -s file.txt "$1/link.txt" + "$XTOUCH" -p "$1/mh1" + ln "$1/mh1" "$1/mh2" + mkfifo "$1/pipe" + # TODO: block + ln -s /dev/null "$1/chardev_link" + ln -s nowhere "$1/broken" + "$MKSOCK" "$1/socket" + "$XTOUCH" -p "$1"/s{u,g,ug}id + chmod u+s "$1"/su{,g}id + chmod g+s "$1"/s{u,}gid + mkdir "$1/ow" "$1"/sticky{,_ow} + chmod o+w "$1"/*ow + chmod +t "$1"/sticky* + "$XTOUCH" -p "$1"/exec.sh + chmod +x "$1"/exec.sh + "$XTOUCH" -p "$1/"$'\e[1m/\e[0m' +} + +# Create all standard directory trees +make_stddirs() { + TMP=$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX) + + if ((CLEAN)); then + defer clean_stddirs + else + printf "Test files saved to ${BLD}%s${RST}\n" "$TMP" + fi + + chown "$(id -u):$(id -g)" "$TMP" + + make_basic "$TMP/basic" + make_perms "$TMP/perms" + make_links "$TMP/links" + make_loops "$TMP/loops" + make_times "$TMP/times" + make_weirdnames "$TMP/weirdnames" + make_deep "$TMP/deep" + make_rainbow "$TMP/rainbow" + mkdir "$TMP/scratch" +} + +# Clean whatever was left in the scratch directory +clean_scratch() { + if [ -e "$TMP/scratch" ]; then + # Try to unmount anything left behind + if ((${#SUDO[@]})) && command -v mountpoint &>/dev/null; then + for path in "$TMP/scratch"/*; do + if mountpoint -q "$path"; then + sudo umount "$path" + fi + done + fi + + # Reset any modified permissions + chmod -R +rX "$TMP/scratch" + + rm -rf "$TMP/scratch" + fi + + mkdir "$TMP/scratch" +} + +# Clean up temporary directories on exit +clean_stddirs() { + # Don't force rm to deal with long paths + for dir in "$TMP"/deep/*/*; do + if [ -d "$dir" ]; then + (cd "$dir" && rm -rf *) + fi + done + + # In case a test left anything weird in scratch/ + clean_scratch + + rm -rf "$TMP" +} diff --git a/tests/tests.sh b/tests/tests.sh index dcef28e..3890243 100755 --- a/tests/tests.sh +++ b/tests/tests.sh @@ -6,815 +6,15 @@ set -euP umask 022 -export LC_ALL=C -export TZ=UTC0 - -SAN_OPTIONS="halt_on_error=1:log_to_syslog=0" -export ASAN_OPTIONS="$SAN_OPTIONS" -export LSAN_OPTIONS="$SAN_OPTIONS" -export MSAN_OPTIONS="$SAN_OPTIONS" -export TSAN_OPTIONS="$SAN_OPTIONS" -export UBSAN_OPTIONS="$SAN_OPTIONS" - -export LS_COLORS="" -unset BFS_COLORS - -BLD=$'\e[01m' -RED=$'\e[01;31m' -GRN=$'\e[01;32m' -YLW=$'\e[01;33m' -BLU=$'\e[01;34m' -MAG=$'\e[01;35m' -CYN=$'\e[01;36m' -RST=$'\e[0m' - -function color_fd() { - [ -z "${NO_COLOR:-}" ] && [ -t "$1" ] -} - -color_fd 1 && COLOR_STDOUT=1 || COLOR_STDOUT=0 -color_fd 2 && COLOR_STDERR=1 || COLOR_STDERR=0 - -# Filter out escape sequences if necessary -function color() { - if color_fd 1; then - cat - else - sed $'s/\e\\[[^m]*m//g' - fi -} - -# printf with auto-detected color support -function cprintf() { - printf "$@" | color -} - -UNAME=$(uname) - -if [ "$UNAME" = Darwin ]; then - # ASan on macOS likes to report - # - # malloc: nano zone abandoned due to inability to preallocate reserved vm space. - # - # to syslog, which as a side effect opens a socket which might take the - # place of one of the standard streams if the process is launched with it - # closed. This environment variable avoids the message. - export MallocNanoZone=0 -fi - -if command -v capsh &>/dev/null; then - if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then - if [ -n "${BFS_TRIED_DROP:-}" ]; then - color >&2 <&2 <&2 <&2 - usage >&2 - exit 1 - ;; - *) - PATTERNS+=("$arg") - ;; - esac -done - -function _realpath() { - ( - cd "$(dirname -- "$1")" - echo "$PWD/$(basename -- "$1")" - ) -} - -TESTS=$(_realpath "$(dirname -- "${BASH_SOURCE[0]}")") - -if [ "${BUILDDIR-}" ]; then - BIN=$(_realpath "$BUILDDIR/bin") -else - BIN=$(_realpath "$TESTS/../bin") -fi -MKSOCK="$BIN/tests/mksock" -XTOUCH="$BIN/tests/xtouch" - -# Try to resolve the path to $BFS before we cd, while also supporting -# --bfs="./bin/bfs -S ids" -read -a BFS <<<"${BFS:-$BIN/bfs}" -BFS[0]=$(_realpath "$(command -v "${BFS[0]}")") - -# The temporary directory that will hold our test data -TMP=$(mktemp -d "${TMPDIR:-/tmp}"/bfs.XXXXXXXXXX) -chown "$(id -u):$(id -g)" "$TMP" - -cd "$TESTS" - -if ((${#PATTERNS[@]} == 0)); then - PATTERNS=("*") -fi - -TEST_CASES=() -for TEST in {posix,common,bsd,gnu,bfs}/*.sh; do - TEST="${TEST%.sh}" - for PATTERN in "${PATTERNS[@]}"; do - if [[ $TEST == $PATTERN ]]; then - TEST_CASES+=("$TEST") - break - fi - done -done - -if ((${#TEST_CASES[@]} == 0)); then - cprintf "${RED}error:${RST} No tests matched" >&2 - cprintf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 - cprintf ".\n\n" >&2 - usage >&2 - exit 1 -fi - -function quote() { - printf '%q' "$1" - shift - if (($# > 0)); then - printf ' %q' "$@" - fi -} - -# Run a command when this (sub)shell exits -function defer() { - trap -- KILL - if ! trap -p EXIT | grep -q pop_defers; then - DEFER_CMDS=() - DEFER_LINES=() - DEFER_FILES=() - trap pop_defers EXIT - fi - - DEFER_CMDS+=("$(quote "$@")") - - local line file - read -r line file < <(caller) - DEFER_LINES+=("$line") - DEFER_FILES+=("$file") -} - -function report_err() { - local file="${1/#*\/tests\//tests\/}" - set -- "$file" "${@:2}" - - if ((COLOR_STDERR)); then - printf "${BLD}%s:%d:${RST} ${RED}error %d:${RST}\n %s\n" "$@" >&2 - else - printf "%s:%d: error %d:\n %s\n" "$@" >&2 - fi -} - -function pop_defer() { - local cmd="${DEFER_CMDS[-1]}" - local file="${DEFER_FILES[-1]}" - local line="${DEFER_LINES[-1]}" - unset "DEFER_CMDS[-1]" - unset "DEFER_FILES[-1]" - unset "DEFER_LINES[-1]" - - local ret=0 - eval "$cmd" || ret=$? - - if ((ret != 0)); then - report_err "$file" $line $ret "defer $cmd" - fi - - return $ret -} - -function pop_defers() { - local ret=0 - - while ((${#DEFER_CMDS[@]} > 0)); do - pop_defer || ret=$? - done - - return $ret -} - -function bfs_sudo() { - if ((${#SUDO[@]})); then - "${SUDO[@]}" "$@" - else - return 1 - fi -} - -function clean_scratch() { - if [ -e "$TMP/scratch" ]; then - # Try to unmount anything left behind - if ((${#SUDO[@]})) && command -v mountpoint &>/dev/null; then - for path in "$TMP"/scratch/*; do - if mountpoint -q "$path"; then - sudo umount "$path" - fi - done - fi - - # Reset any modified permissions - chmod -R +rX "$TMP/scratch" - - rm -rf "$TMP/scratch" - fi - - mkdir "$TMP/scratch" -} - -# Clean up temporary directories on exit -function cleanup() { - # Don't force rm to deal with long paths - for dir in "$TMP"/deep/*/*; do - if [ -d "$dir" ]; then - (cd "$dir" && rm -rf *) - fi - done - - # In case a test left anything weird in scratch/ - clean_scratch - - rm -rf "$TMP" -} - -if ((CLEAN)); then - defer cleanup -else - echo "Test files saved to $TMP" -fi - -# Creates a simple file+directory structure for tests -function make_basic() { - "$XTOUCH" -p "$1"/{a,b,c/d,e/f,g/h/,i/} - "$XTOUCH" -p "$1"/{j/foo,k/foo/bar,l/foo/bar/baz} - echo baz >"$1/l/foo/bar/baz" -} -make_basic "$TMP/basic" - -# Creates a file+directory structure with various permissions for tests -function make_perms() { - "$XTOUCH" -p -M000 "$1/0" - "$XTOUCH" -p -M444 "$1/r" - "$XTOUCH" -p -M222 "$1/w" - "$XTOUCH" -p -M644 "$1/rw" - "$XTOUCH" -p -M555 "$1/rx" - "$XTOUCH" -p -M311 "$1/wx" - "$XTOUCH" -p -M755 "$1/rwx" -} -make_perms "$TMP/perms" - -# Creates a file+directory structure with various symbolic and hard links -function make_links() { - "$XTOUCH" -p "$1/file" - ln -s file "$1/symlink" - ln "$1/file" "$1/hardlink" - ln -s nowhere "$1/broken" - ln -s symlink/file "$1/notdir" - "$XTOUCH" -p "$1/deeply/nested"/{dir/,file} - ln -s file "$1/deeply/nested/link" - ln -s nowhere "$1/deeply/nested/broken" - ln -s deeply/nested "$1/skip" -} -make_links "$TMP/links" - -# Creates a file+directory structure with symbolic link loops -function make_loops() { - "$XTOUCH" -p "$1/file" - ln -s file "$1/symlink" - ln -s nowhere "$1/broken" - ln -s symlink/file "$1/notdir" - ln -s loop "$1/loop" - mkdir -p "$1/deeply/nested/dir" - ln -s ../../deeply "$1/deeply/nested/loop" - ln -s deeply/nested/loop/nested "$1/skip" -} -make_loops "$TMP/loops" - -# Creates a file+directory structure with varying timestamps -function make_times() { - "$XTOUCH" -p -t "1991-12-14 00:00" "$1/a" - "$XTOUCH" -p -t "1991-12-14 00:01" "$1/b" - "$XTOUCH" -p -t "1991-12-14 00:02" "$1/c" - ln -s a "$1/l" - "$XTOUCH" -p -h -t "1991-12-14 00:03" "$1/l" - "$XTOUCH" -p -t "1991-12-14 00:04" "$1" -} -make_times "$TMP/times" - -# Creates a file+directory structure with various weird file/directory names -function make_weirdnames() { - "$XTOUCH" -p "$1/-/a" - "$XTOUCH" -p "$1/(/b" - "$XTOUCH" -p "$1/(-/c" - "$XTOUCH" -p "$1/!/d" - "$XTOUCH" -p "$1/!-/e" - "$XTOUCH" -p "$1/,/f" - "$XTOUCH" -p "$1/)/g" - "$XTOUCH" -p "$1/.../h" - "$XTOUCH" -p "$1/\\/i" - "$XTOUCH" -p "$1/ /j" - "$XTOUCH" -p "$1/[/k" -} -make_weirdnames "$TMP/weirdnames" - -# Creates a very deep directory structure for testing PATH_MAX handling -function make_deep() { - mkdir -p "$1" - - # $name will be 255 characters, aka _XOPEN_NAME_MAX - local name="0123456789ABCDEF" - name="${name}${name}${name}${name}" - name="${name}${name}${name}${name}" - name="${name:0:255}" - - for i in {0..9} A B C D E F; do - "$XTOUCH" -p "$1/$i/$name" - - ( - cd "$1/$i" - - # 8 * 512 == 4096 >= PATH_MAX - for _ in {1..8}; do - mv "$name" .. - mkdir -p "$name/$name" - mv "../$name" "$name/$name/" - done - ) - done -} -make_deep "$TMP/deep" - -# Creates a directory structure with many different types, and therefore colors -function make_rainbow() { - "$XTOUCH" -p "$1/file.txt" - "$XTOUCH" -p "$1/file.dat" - "$XTOUCH" -p "$1/lower".{gz,tar,tar.gz} - "$XTOUCH" -p "$1/upper".{GZ,TAR,TAR.GZ} - "$XTOUCH" -p "$1/lu.tar.GZ" "$1/ul.TAR.gz" - ln -s file.txt "$1/link.txt" - "$XTOUCH" -p "$1/mh1" - ln "$1/mh1" "$1/mh2" - mkfifo "$1/pipe" - # TODO: block - ln -s /dev/null "$1/chardev_link" - ln -s nowhere "$1/broken" - "$MKSOCK" "$1/socket" - "$XTOUCH" -p "$1"/s{u,g,ug}id - chmod u+s "$1"/su{,g}id - chmod g+s "$1"/s{u,}gid - mkdir "$1/ow" "$1"/sticky{,_ow} - chmod o+w "$1"/*ow - chmod +t "$1"/sticky* - "$XTOUCH" -p "$1"/exec.sh - chmod +x "$1"/exec.sh - "$XTOUCH" -p "$1/"$'\e[1m/\e[0m' -} -make_rainbow "$TMP/rainbow" - -mkdir "$TMP/scratch" - -# Close stdin so bfs doesn't think we're interactive -exec &1 -fi - -function bfs_verbose() { - if ((!VERBOSE_COMMANDS)); then - return - fi - - { - printf "${GRN}%q${RST} " "${BFS[@]}" - - local expr_started= - for arg; do - if [[ $arg == -[A-Z]* ]]; then - printf "${CYN}%q${RST} " "$arg" - elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then - expr_started=yes - printf "${RED}%q${RST} " "$arg" - elif [[ $expr_started && $arg == [\),] ]]; then - printf "${RED}%q${RST} " "$arg" - elif [[ $arg == -?* ]]; then - expr_started=yes - printf "${BLU}%q${RST} " "$arg" - elif [ "$expr_started" ]; then - printf "${BLD}%q${RST} " "$arg" - else - printf "${MAG}%q${RST} " "$arg" - fi - done - - printf '\n' - } | color >&3 -} - -function invoke_bfs() { - bfs_verbose "$@" - - local ret=0 - "${BFS[@]}" "$@" || ret=$? - - # Allow bfs to fail, but not crash - if ((ret > 125)); then - exit "$ret" - else - return "$ret" - fi -} - -if command -v unbuffer &>/dev/null; then - UNBUFFER=unbuffer -elif command -v expect_unbuffer &>/dev/null; then - UNBUFFER=expect_unbuffer -fi - -function bfs_pty() { - test -n "${UNBUFFER:-}" || skip - - bfs_verbose "$@" - - local ret=0 - "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" || ret=$? - - if ((ret > 125)); then - exit "$ret" - else - return "$ret" - fi -} - -function check_exit() { - local expected="$1" - local actual="0" - shift - "$@" || actual="$?" - ((actual == expected)) -} - -# Detect colored diff support -if ((COLOR_STDERR)) && diff --color=always /dev/null /dev/null 2>/dev/null; then - DIFF="diff --color=always" -else - DIFF="diff" -fi - -# Return value when a difference is detected -EX_DIFF=20 -# Return value when a test is skipped -EX_SKIP=77 - -function sort_output() { - sort -o "$OUT" "$OUT" -} - -function diff_output() { - local GOLD="$TESTS/$TEST.out" - - if ((UPDATE)); then - cp "$OUT" "$GOLD" - else - $DIFF -u "$GOLD" "$OUT" >&2 - fi -} - -function bfs_diff() ( - bfs_verbose "$@" - - # Close the dup()'d stdout to make sure we have enough fd's for the process - # substitution, even with low ulimit -n - exec 3>&- - - "${BFS[@]}" "$@" | sort >"$OUT" - local status="${PIPESTATUS[0]}" - - diff_output || exit $EX_DIFF - return "$status" -) - -function skip() { - if ((VERBOSE_SKIPPED)); then - caller | { - read -r line file - cprintf "${BOL}${CYN}%s skipped!${RST} (%s)\n" "$TEST" "$(awk "NR == $line" "$file")" - } - elif ((VERBOSE_TESTS)); then - cprintf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" - fi - - exit $EX_SKIP -} - -function closefrom() { - if [ -d /proc/self/fd ]; then - local fds=/proc/self/fd - else - local fds=/dev/fd - fi - - for fd in "$fds"/*; do - if [ ! -e "$fd" ]; then - continue - fi - - local fd="${fd##*/}" - if ((fd >= $1)); then - eval "exec ${fd}<&-" - fi - done -} - -function inum() { - ls -id "$@" | awk '{ print $1 }' -} - -function set_acl() { - case "$UNAME" in - Darwin) - chmod +a "$(id -un) allow read,write" "$1" - ;; - FreeBSD) - if (($(getconf ACL_NFS4 "$1") > 0)); then - setfacl -m "u:$(id -un):rw::allow" "$1" - else - setfacl -m "u:$(id -un):rw" "$1" - fi - ;; - *) - setfacl -m "u:$(id -un):rw" "$1" - ;; - esac -} - -function make_xattrs() { - clean_scratch - - "$XTOUCH" scratch/{normal,xattr,xattr_2} - ln -s xattr scratch/link - ln -s normal scratch/xattr_link - - case "$UNAME" in - Darwin) - xattr -w bfs_test true scratch/xattr \ - && xattr -w bfs_test_2 true scratch/xattr_2 \ - && xattr -s -w bfs_test true scratch/xattr_link - ;; - FreeBSD) - setextattr user bfs_test true scratch/xattr \ - && setextattr user bfs_test_2 true scratch/xattr_2 \ - && setextattr -h user bfs_test true scratch/xattr_link - ;; - *) - # Linux tmpfs doesn't support the user.* namespace, so we use the security.* - # namespace, which is writable by root and readable by others - bfs_sudo setfattr -n security.bfs_test scratch/xattr \ - && bfs_sudo setfattr -n security.bfs_test_2 scratch/xattr_2 \ - && bfs_sudo setfattr -h -n security.bfs_test scratch/xattr_link - ;; - esac -} - -cd "$TMP" -set +e - -BOL='\n' -EOL='\n' - -function update_eol() { - # Bash gets $COLUMNS from stderr, so if it's redirected use tput instead - local cols="${COLUMNS-}" - if [ -z "$cols" ]; then - cols=$(tput cols) - fi - - # Put the cursor at the last column, then write a space so the next - # character will wrap - EOL="\\033[${cols}G " -} - -if ((VERBOSE_TESTS)); then - BOL='' -elif ((COLOR_STDOUT)); then - BOL='\r\033[K' - - # Workaround for bash 4: checkwinsize is off by default. We can turn it on, - # but we also have to explicitly trigger a foreground job to finish so that - # it will update the window size before we use $COLUMNS - shopt -s checkwinsize - (:) - - update_eol - trap update_eol WINCH -fi - -function callers() { - local frame=0 - while caller $frame; do - ((++frame)) - done -} - -function debug_err() { - local ret=$? line func file - callers | while read -r line func file; do - if [ "$func" = source ]; then - local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : - report_err "$file" $line $ret "$cmd" - break - fi - done -} - -function run_test() ( - set -eE - trap debug_err ERR - source "$@" -) - -passed=0 -failed=0 -skipped=0 - -if ((COLOR_STDOUT || VERBOSE_TESTS)); then - TEST_FMT="${BOL}${YLW}%s${RST}${EOL}" -else - TEST_FMT="." -fi - -for TEST in "${TEST_CASES[@]}"; do - printf "$TEST_FMT" "$TEST" - - OUT="$TMP/$TEST.out" - mkdir -p "${OUT%/*}" - - if ((VERBOSE_ERRORS)); then - run_test "$TESTS/$TEST.sh" - else - run_test "$TESTS/$TEST.sh" 2>"$TMP/$TEST.err" - fi - status=$? - - if ((status == 0)); then - ((++passed)) - elif ((status == EX_SKIP)); then - ((++skipped)) - else - ((++failed)) - ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 - cprintf "${BOL}${RED}%s failed!${RST}\n" "$TEST" - ((STOP)) && break - fi -done - -printf "${BOL}" - -if ((passed > 0)); then - cprintf "${GRN}tests passed: %d${RST}\n" "$passed" -fi -if ((skipped > 0)); then - cprintf "${CYN}tests skipped: %s${RST}\n" "$skipped" -fi -if ((failed > 0)); then - cprintf "${RED}tests failed: %s${RST}\n" "$failed" - exit 1 -fi +TESTS="$(dirname -- "${BASH_SOURCE[0]}")" +. "$TESTS/util.sh" +. "$TESTS/color.sh" +. "$TESTS/stddirs.sh" +. "$TESTS/getopts.sh" +. "$TESTS/run.sh" + +stdenv +drop_root "$@" +parse_args "$@" +make_stddirs +run_tests diff --git a/tests/util.sh b/tests/util.sh new file mode 100644 index 0000000..5131522 --- /dev/null +++ b/tests/util.sh @@ -0,0 +1,189 @@ +#!/hint/bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +## Utility functions + +# Portable realpath(1) +_realpath() ( + cd "$(dirname -- "$1")" + echo "$PWD/$(basename -- "$1")" +) + +# Globals +TESTS=$(_realpath "$TESTS") +if [ "${BUILDDIR-}" ]; then + BIN=$(_realpath "$BUILDDIR/bin") +else + BIN=$(_realpath "$TESTS/../bin") +fi +MKSOCK="$BIN/tests/mksock" +XTOUCH="$BIN/tests/xtouch" +UNAME=$(uname) + +# Standardize the environment +stdenv() { + export LC_ALL=C + export TZ=UTC0 + + local SAN_OPTIONS="halt_on_error=1:log_to_syslog=0" + export ASAN_OPTIONS="$SAN_OPTIONS" + export LSAN_OPTIONS="$SAN_OPTIONS" + export MSAN_OPTIONS="$SAN_OPTIONS" + export TSAN_OPTIONS="$SAN_OPTIONS" + export UBSAN_OPTIONS="$SAN_OPTIONS" + + export LS_COLORS="" + unset BFS_COLORS + + if [ "$UNAME" = Darwin ]; then + # ASan on macOS likes to report + # + # malloc: nano zone abandoned due to inability to preallocate reserved vm space. + # + # to syslog, which as a side effect opens a socket which might take the + # place of one of the standard streams if the process is launched with + # it closed. This environment variable avoids the message. + export MallocNanoZone=0 + fi + + # Close non-standard inherited fds + if [ -d /proc/self/fd ]; then + local fds=/proc/self/fd + else + local fds=/dev/fd + fi + + for fd in "$fds"/*; do + if [ ! -e "$fd" ]; then + continue + fi + + local fd="${fd##*/}" + if ((fd > 2)); then + eval "exec ${fd}<&-" + fi + done + + # Close stdin so bfs doesn't think we're interactive + # dup() the standard fds for logging even when redirected + exec &1 4>&2 +} + +# Drop root priviliges or bail +drop_root() { + if command -v capsh &>/dev/null; then + if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then + if [ -n "${BFS_TRIED_DROP:-}" ]; then + color >&2 <&2 <&2 < 0)); then + printf ' %q' "$@" + fi +} + +# Run a command when this (sub)shell exits +defer() { + # Refresh trap state before trap -p + # See https://unix.stackexchange.com/a/556888/56202 + trap -- KILL + + # Check if the EXIT trap is already set + if ! trap -p EXIT | grep -q pop_defers; then + DEFER_CMDS=() + DEFER_LINES=() + DEFER_FILES=() + trap pop_defers EXIT + fi + + DEFER_CMDS+=("$(quote "$@")") + + local line file + read -r line file < <(caller) + DEFER_LINES+=("$line") + DEFER_FILES+=("$file") +} + +# Pop a single command from the defer stack and run it +pop_defer() { + local cmd="${DEFER_CMDS[-1]}" + local file="${DEFER_FILES[-1]}" + local line="${DEFER_LINES[-1]}" + unset "DEFER_CMDS[-1]" + unset "DEFER_FILES[-1]" + unset "DEFER_LINES[-1]" + + local ret=0 + eval "$cmd" || ret=$? + + if ((ret != 0)); then + debug "$file" $line "${RED}error $ret${RST}" "defer $cmd" >&4 + fi + + return $ret +} + +# Run all deferred commands +pop_defers() { + local ret=0 + + while ((${#DEFER_CMDS[@]} > 0)); do + pop_defer || ret=$? + done + + return $ret +} -- cgit v1.2.3 From 13053c43ee8f5a2a5f7a66258d047e1b68203da3 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 19 Oct 2023 17:05:36 -0400 Subject: tests: Don't unset array[-1] This was only added in Bash 4.3 which is too new for macOS. --- tests/util.sh | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) (limited to 'tests') diff --git a/tests/util.sh b/tests/util.sh index 5131522..efc24b0 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -160,12 +160,13 @@ defer() { # Pop a single command from the defer stack and run it pop_defer() { - local cmd="${DEFER_CMDS[-1]}" - local file="${DEFER_FILES[-1]}" - local line="${DEFER_LINES[-1]}" - unset "DEFER_CMDS[-1]" - unset "DEFER_FILES[-1]" - unset "DEFER_LINES[-1]" + local i=$((${#DEFER_CMDS[@]} - 1)) + local cmd="${DEFER_CMDS[$i]}" + local file="${DEFER_FILES[$i]}" + local line="${DEFER_LINES[$i]}" + unset "DEFER_CMDS[$i]" + unset "DEFER_FILES[$i]" + unset "DEFER_LINES[$i]" local ret=0 eval "$cmd" || ret=$? -- cgit v1.2.3 From 156602895250813a8a5a0850f3fec5ed0c774796 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 20 Oct 2023 11:18:15 -0400 Subject: tests/xtouch: Recover from ENAMETOOLONG --- tests/xtouch.c | 129 +++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 84 insertions(+), 45 deletions(-) (limited to 'tests') diff --git a/tests/xtouch.c b/tests/xtouch.c index a4c4d40..260a3a3 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -37,89 +37,128 @@ struct args { mode_t pmode; }; -/** Compute flags for fstatat()/utimensat(). */ -static int at_flags(const struct args *args) { - if (args->flags & NO_FOLLOW) { - return AT_SYMLINK_NOFOLLOW; - } else { - return 0; +/** Open (and maybe create) a single directory. */ +static int open_dir(const struct args *args, int dfd, const char *path) { + int ret = openat(dfd, path, O_SEARCH | O_DIRECTORY); + + if (ret < 0 && errno == ENOENT && (args->flags & CREATE_PARENTS)) { + if (mkdirat(dfd, path, args->pmode) == 0 || errno == EEXIST) { + ret = openat(dfd, path, O_SEARCH | O_DIRECTORY); + } } + + return ret; } -/** Create any parent directories of the given path. */ -static int mkdirs(const char *path, mode_t mode) { - int ret = -1; - char *dir = xdirname(path); +/** Open (and maybe create) the parent directory of the path. */ +static int open_parent(const struct args *args, const char **path) { + size_t max = xbaseoff(*path); + if (max == 0) { + return AT_FDCWD; + } + + char *dir = strndup(*path, max); if (!dir) { - goto err; + return -1; } - if (strcmp(dir, ".") == 0) { + // Optimistically try the whole path first + int dfd = open_dir(args, AT_FDCWD, dir); + if (dfd >= 0) { goto done; } - // Optimistically try the immediate parent first - if (mkdir(dir, mode) == 0 || errno == EEXIST) { - goto done; + switch (errno) { + case ENAMETOOLONG: + break; + case ENOENT: + if (args->flags & CREATE_PARENTS) { + break; + } else { + goto err; + } + default: + goto err; } - // Create the parents one-at-a-time - char *cur = dir + strspn(dir, "/"); + // Open the parents one-at-a-time + dfd = AT_FDCWD; + char *cur = dir; while (*cur) { - cur += strcspn(cur, "/"); - char *next = cur + strspn(cur, "/"); + char *next = cur; + next += strcspn(next, "/"); + next += strspn(next, "/"); + + char c = *next; + *next = '\0'; - char c = *cur; - *cur = '\0'; - if (mkdir(dir, mode) != 0 && errno != EEXIST) { + int parent = dfd; + dfd = open_dir(args, parent, cur); + if (parent >= 0) { + close_quietly(parent); + } + if (dfd < 0) { goto err; } - *cur = c; + + *next = c; cur = next; } done: - ret = 0; + *path += max; err: free(dir); - return ret; + return dfd; +} + +/** Compute flags for fstatat()/utimensat(). */ +static int at_flags(const struct args *args) { + if (args->flags & NO_FOLLOW) { + return AT_SYMLINK_NOFOLLOW; + } else { + return 0; + } } /** Touch one path. */ static int xtouch(const struct args *args, const char *path) { - int ret = utimensat(AT_FDCWD, path, args->times, at_flags(args)); + int dfd = open_parent(args, &path); + if (dfd < 0 && dfd != AT_FDCWD) { + return -1; + } + + int ret = utimensat(dfd, path, args->times, at_flags(args)); if (ret == 0 || errno != ENOENT) { - return ret; + goto done; } if (args->flags & NO_CREATE) { - return 0; - } else if (args->flags & CREATE_PARENTS) { - if (mkdirs(path, args->pmode) != 0) { - return -1; - } + ret = 0; + goto done; } size_t len = strlen(path); if (len > 0 && path[len - 1] == '/') { - if (mkdir(path, args->dmode) != 0) { - return -1; + if (mkdirat(dfd, path, args->dmode) == 0) { + ret = utimensat(dfd, path, args->times, at_flags(args)); } - - return utimensat(AT_FDCWD, path, args->times, at_flags(args)); } else { - int fd = open(path, O_WRONLY | O_CREAT, args->fmode); - if (fd < 0) { - return -1; - } - - if (futimens(fd, args->times) != 0) { - close_quietly(fd); - return -1; + int fd = openat(dfd, path, O_WRONLY | O_CREAT, args->fmode); + if (fd >= 0) { + if (futimens(fd, args->times) == 0) { + ret = xclose(fd); + } else { + close_quietly(fd); + } } + } - return xclose(fd); +done: + if (dfd >= 0) { + close_quietly(dfd); } + return ret; } int main(int argc, char *argv[]) { -- cgit v1.2.3 From 962d715ce2dee7a017dacca3b787ade6d33cd1c6 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 20 Oct 2023 11:18:25 -0400 Subject: tests: Use xtouch to create the whole deep tree at once --- tests/stddirs.sh | 21 ++++++--------------- 1 file changed, 6 insertions(+), 15 deletions(-) (limited to 'tests') diff --git a/tests/stddirs.sh b/tests/stddirs.sh index e7f7246..acc23dc 100644 --- a/tests/stddirs.sh +++ b/tests/stddirs.sh @@ -79,24 +79,15 @@ make_deep() { # $name will be 255 characters, aka _XOPEN_NAME_MAX local name="0123456789ABCDEF" - name="${name}${name}${name}${name}" - name="${name}${name}${name}${name}" + name="$name$name$name$name" + name="$name$name$name$name" name="${name:0:255}" - for i in {0..9} A B C D E F; do - "$XTOUCH" -p "$1/$i/$name" + # 4 * 4 * 256 == 4096 >= PATH_MAX + local path="$name/$name/$name/$name" + path="$path/$path/$path/$path" - ( - cd "$1/$i" - - # 8 * 512 == 4096 >= PATH_MAX - for _ in {1..8}; do - mv "$name" .. - mkdir -p "$name/$name" - mv "../$name" "$name/$name/" - done - ) - done + "$XTOUCH" -p "$1"/{{0..9},A,B,C,D,E,F}/"$path/$name" } # Creates a directory structure with many different types, and therefore colors -- cgit v1.2.3 From 60366e4583a1d148dd5f8171c9148ebb98890478 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 20 Oct 2023 15:52:45 -0400 Subject: tests/color: Remove some useless cats --- tests/color.sh | 12 +++------- tests/getopts.sh | 10 ++++---- tests/run.sh | 73 +++++++++++++++++++++++++++++--------------------------- tests/util.sh | 8 +++---- 4 files changed, 50 insertions(+), 53 deletions(-) (limited to 'tests') diff --git a/tests/color.sh b/tests/color.sh index 0d6ef68..805d2b8 100644 --- a/tests/color.sh +++ b/tests/color.sh @@ -24,20 +24,14 @@ color_fd() { color_fd 1 && COLOR_STDOUT=1 || COLOR_STDOUT=0 color_fd 2 && COLOR_STDERR=1 || COLOR_STDERR=0 -# Save these in case the tests unset PATH -CAT=$(command -v cat) +# Save this in case the tests unset PATH SED=$(command -v sed) # Filter out escape sequences if necessary color() { if color_fd 1; then - "$CAT" + "$@" else - "$SED" $'s/\e\\[[^m]*m//g' + "$@" | "$SED" $'s/\e\\[[^m]*m//g' fi } - -# printf with auto-detected color support -cprintf() { - printf "$@" | color -} diff --git a/tests/getopts.sh b/tests/getopts.sh index 6616a4a..7d3ef4b 100644 --- a/tests/getopts.sh +++ b/tests/getopts.sh @@ -8,7 +8,7 @@ # Print usage information usage() { local pad=$(printf "%*s" ${#0} "") - color <&2 + color printf "${RED}error:${RST} Unrecognized option '%s'.\n\n" "$arg" >&2 usage >&2 exit 1 ;; @@ -149,9 +149,9 @@ parse_args() { done if ((${#TEST_CASES[@]} == 0)); then - cprintf "${RED}error:${RST} No tests matched" >&2 - cprintf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 - cprintf ".\n\n" >&2 + color printf "${RED}error:${RST} No tests matched" >&2 + color printf " ${BLD}%s${RST}" "${PATTERNS[@]}" >&2 + color printf ".\n\n" >&2 usage >&2 exit 1 fi diff --git a/tests/run.sh b/tests/run.sh index 70c9cc2..5fcccad 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -93,7 +93,7 @@ run_tests() { else ((++failed)) ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 - cprintf "${BOL}${RED}%s failed!${RST}\n" "$TEST" + color printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" ((STOP)) && break fi done @@ -101,13 +101,13 @@ run_tests() { printf "${BOL}" if ((passed > 0)); then - cprintf "${GRN}tests passed: %d${RST}\n" "$passed" + color printf "${GRN}tests passed: %d${RST}\n" "$passed" fi if ((skipped > 0)); then - cprintf "${CYN}tests skipped: %s${RST}\n" "$skipped" + color printf "${CYN}tests skipped: %s${RST}\n" "$skipped" fi if ((failed > 0)); then - cprintf "${RED}tests failed: %s${RST}\n" "$failed" + color printf "${RED}tests failed: %s${RST}\n" "$failed" exit 1 fi } @@ -126,7 +126,7 @@ skip() { debug "$file" $line "${CYN}$TEST skipped!${RST}" "$(awk "NR == $line" "$file")" >&3 } elif ((VERBOSE_TESTS)); then - cprintf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" + color printf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" fi exit $EX_SKIP @@ -175,39 +175,42 @@ set_acl() { } # Print a bfs invocation for --verbose=commands -bfs_verbose() ( - if ((!VERBOSE_COMMANDS)); then - return +bfs_verbose() { + if ((VERBOSE_COMMANDS)); then + ( + # Close some fds to make room for the pipe, + # even with extremely low ulimit -n + exec >&- 4>&- + exec >&3 3>&- + color bfs_verbose_impl "$@" + ) fi +} - # Free up an fd for the pipe - exec 4>&- - - { - printf "${GRN}%q${RST} " "${BFS[@]}" - - local expr_started= - for arg; do - if [[ $arg == -[A-Z]* ]]; then - printf "${CYN}%q${RST} " "$arg" - elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then - expr_started=yes - printf "${RED}%q${RST} " "$arg" - elif [[ $expr_started && $arg == [\),] ]]; then - printf "${RED}%q${RST} " "$arg" - elif [[ $arg == -?* ]]; then - expr_started=yes - printf "${BLU}%q${RST} " "$arg" - elif [ "$expr_started" ]; then - printf "${BLD}%q${RST} " "$arg" - else - printf "${MAG}%q${RST} " "$arg" - fi - done +bfs_verbose_impl() { + printf "${GRN}%q${RST} " "${BFS[@]}" + + local expr_started= + for arg; do + if [[ $arg == -[A-Z]* ]]; then + printf "${CYN}%q${RST} " "$arg" + elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then + expr_started=yes + printf "${RED}%q${RST} " "$arg" + elif [[ $expr_started && $arg == [\),] ]]; then + printf "${RED}%q${RST} " "$arg" + elif [[ $arg == -?* ]]; then + expr_started=yes + printf "${BLU}%q${RST} " "$arg" + elif [ "$expr_started" ]; then + printf "${BLD}%q${RST} " "$arg" + else + printf "${MAG}%q${RST} " "$arg" + fi + done - printf '\n' - } | color >&3 -) + printf '\n' +} # Run the bfs we're testing invoke_bfs() { diff --git a/tests/util.sh b/tests/util.sh index efc24b0..31a7b6c 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -76,14 +76,14 @@ drop_root() { if command -v capsh &>/dev/null; then if capsh --has-p=cap_dac_override &>/dev/null || capsh --has-p=cap_dac_read_search &>/dev/null; then if [ -n "${BFS_TRIED_DROP:-}" ]; then - color >&2 <&2 <&2 <&2 <&2 <&2 < Date: Fri, 20 Oct 2023 15:56:55 -0400 Subject: tests: Fix column detection with redirected stderr --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index 5fcccad..7b7fe24 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -14,7 +14,7 @@ update_eol() { # Bash gets $COLUMNS from stderr, so if it's redirected use tput instead local cols="${COLUMNS-}" if [ -z "$cols" ]; then - cols=$(tput cols) + cols=$(tput cols 2>/dev/tty) fi # Put the cursor at the last column, then write a space so the next -- cgit v1.2.3 From a2af2746c4686201e2e0796fbdfa115d08727b86 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 22 Oct 2023 16:41:41 -0400 Subject: tests: Use test-specific scratch directories --- tests/bfs/L_capable.out | 4 +- tests/bfs/L_capable.sh | 13 +++--- tests/bfs/capable.out | 2 +- tests/bfs/capable.sh | 13 +++--- tests/bfs/color_ls.sh | 2 +- tests/bfs/exec_flush_fprint.sh | 3 +- tests/bsd/L_acl.out | 4 +- tests/bsd/L_acl.sh | 12 +++--- tests/bsd/L_xattr.out | 6 +-- tests/bsd/L_xattr.sh | 4 +- tests/bsd/L_xattrname.out | 4 +- tests/bsd/L_xattrname.sh | 6 +-- tests/bsd/acl.out | 2 +- tests/bsd/acl.sh | 12 +++--- tests/bsd/flags.out | 2 +- tests/bsd/flags.sh | 10 ++--- tests/bsd/rm.out | 2 +- tests/bsd/rm.sh | 10 ++--- tests/bsd/type_w.out | 72 ++++++++++++++++----------------- tests/bsd/type_w.sh | 36 ++++++++--------- tests/bsd/xattr.out | 6 +-- tests/bsd/xattr.sh | 4 +- tests/bsd/xattrname.out | 4 +- tests/bsd/xattrname.sh | 6 +-- tests/common/L_ls.sh | 3 +- tests/common/L_mount.out | 10 ++--- tests/common/L_mount.sh | 16 ++++---- tests/common/delete.out | 2 +- tests/common/delete.sh | 11 ++--- tests/common/delete_many.out | 2 +- tests/common/delete_many.sh | 10 ++--- tests/common/execdir_ulimit.out | 2 +- tests/common/execdir_ulimit.sh | 8 ++-- tests/common/inum_bind_mount.out | 4 +- tests/common/inum_bind_mount.sh | 10 ++--- tests/common/inum_mount.out | 2 +- tests/common/inum_mount.sh | 10 ++--- tests/common/ls.sh | 3 +- tests/common/mount.out | 8 ++-- tests/common/mount.sh | 12 +++--- tests/gnu/L_delete.out | 4 +- tests/gnu/L_delete.sh | 11 +++-- tests/gnu/files0_from_file.sh | 6 +-- tests/gnu/fls.sh | 3 +- tests/gnu/fls_nonexistent.sh | 2 +- tests/gnu/fprint0_nonexistent.sh | 2 +- tests/gnu/fprint_duplicate.sh | 10 ++--- tests/gnu/fprint_nonexistent.sh | 2 +- tests/gnu/fprintf_nonexistent.sh | 2 +- tests/gnu/fstype_stacked.out | 2 +- tests/gnu/fstype_stacked.sh | 14 +++---- tests/gnu/fstype_umount.sh | 14 +++---- tests/gnu/ignore_readdir_race.sh | 6 +-- tests/gnu/ignore_readdir_race_notdir.sh | 6 +-- tests/gnu/inum_automount.out | 2 +- tests/gnu/inum_automount.sh | 14 +++---- tests/gnu/printf_Y_error.out | 6 +-- tests/gnu/printf_Y_error.sh | 12 +++--- tests/gnu/regex_invalid_utf8.out | 2 +- tests/gnu/regex_invalid_utf8.sh | 10 ++--- tests/gnu/xtype_bind_mount.out | 4 +- tests/gnu/xtype_bind_mount.sh | 12 +++--- tests/posix/L_xdev.out | 10 ++--- tests/posix/L_xdev.sh | 16 ++++---- tests/posix/depth_error.out | 4 +- tests/posix/depth_error.sh | 10 ++--- tests/posix/overlayfs.out | 10 ++--- tests/posix/overlayfs.sh | 14 +++---- tests/posix/readdir_error.sh | 18 ++++----- tests/posix/type_bind_mount.out | 2 +- tests/posix/type_bind_mount.sh | 10 ++--- tests/posix/unionfs.out | 20 ++++----- tests/posix/unionfs.sh | 13 +++--- tests/posix/xdev.out | 8 ++-- tests/posix/xdev.sh | 12 +++--- tests/run.sh | 28 ++++++------- tests/stddirs.sh | 25 ------------ 77 files changed, 330 insertions(+), 368 deletions(-) (limited to 'tests') diff --git a/tests/bfs/L_capable.out b/tests/bfs/L_capable.out index e5ba3c7..0810d4a 100644 --- a/tests/bfs/L_capable.out +++ b/tests/bfs/L_capable.out @@ -1,2 +1,2 @@ -scratch/capable -scratch/link +./capable +./link diff --git a/tests/bfs/L_capable.sh b/tests/bfs/L_capable.sh index 232d6ac..97c404f 100644 --- a/tests/bfs/L_capable.sh +++ b/tests/bfs/L_capable.sh @@ -1,11 +1,10 @@ test "$UNAME" = "Linux" || skip +invoke_bfs . -quit -capable || skip -clean_scratch +cd "$TEST" -invoke_bfs scratch -quit -capable || skip +"$XTOUCH" normal capable +bfs_sudo setcap all+ep capable || skip +ln -s capable link -"$XTOUCH" scratch/{normal,capable} -bfs_sudo setcap all+ep scratch/capable || skip -ln -s capable scratch/link - -bfs_diff -L scratch -capable +bfs_diff -L . -capable diff --git a/tests/bfs/capable.out b/tests/bfs/capable.out index 78b5bd9..ac7b5ce 100644 --- a/tests/bfs/capable.out +++ b/tests/bfs/capable.out @@ -1 +1 @@ -scratch/capable +./capable diff --git a/tests/bfs/capable.sh b/tests/bfs/capable.sh index e5cad63..35bb0b4 100644 --- a/tests/bfs/capable.sh +++ b/tests/bfs/capable.sh @@ -1,11 +1,10 @@ test "$UNAME" = "Linux" || skip +invoke_bfs . -quit -capable || skip -clean_scratch +cd "$TEST" -invoke_bfs scratch -quit -capable || skip +"$XTOUCH" normal capable +bfs_sudo setcap all+ep capable || skip +ln -s capable link -"$XTOUCH" scratch/{normal,capable} -bfs_sudo setcap all+ep scratch/capable || skip -ln -s capable scratch/link - -bfs_diff scratch -capable +bfs_diff . -capable diff --git a/tests/bfs/color_ls.sh b/tests/bfs/color_ls.sh index f2d3c72..f1cc216 100644 --- a/tests/bfs/color_ls.sh +++ b/tests/bfs/color_ls.sh @@ -1,4 +1,4 @@ -clean_scratch +cd "$TEST" "$XTOUCH" -p scratch/foo/bar/baz ln -s foo/bar/baz scratch/link ln -s foo/bar/nowhere scratch/broken diff --git a/tests/bfs/exec_flush_fprint.sh b/tests/bfs/exec_flush_fprint.sh index bf6b62f..a862773 100644 --- a/tests/bfs/exec_flush_fprint.sh +++ b/tests/bfs/exec_flush_fprint.sh @@ -1,3 +1,2 @@ # Even non-stdstreams should be flushed -clean_scratch -bfs_diff basic/a -fprint scratch/foo -exec cat scratch/foo \; +bfs_diff basic/a -fprint "$OUT.f" -exec cat "$OUT.f" \; diff --git a/tests/bsd/L_acl.out b/tests/bsd/L_acl.out index 1dae00a..dd89800 100644 --- a/tests/bsd/L_acl.out +++ b/tests/bsd/L_acl.out @@ -1,2 +1,2 @@ -scratch/acl -scratch/link +./acl +./link diff --git a/tests/bsd/L_acl.sh b/tests/bsd/L_acl.sh index db97013..a3fcbc8 100644 --- a/tests/bsd/L_acl.sh +++ b/tests/bsd/L_acl.sh @@ -1,9 +1,9 @@ -clean_scratch +cd "$TEST" -invoke_bfs scratch -quit -acl || skip +invoke_bfs . -quit -acl || skip -"$XTOUCH" scratch/{normal,acl} -set_acl scratch/acl || skip -ln -s acl scratch/link +"$XTOUCH" normal acl +set_acl acl || skip +ln -s acl link -bfs_diff -L scratch -acl +bfs_diff -L . -acl diff --git a/tests/bsd/L_xattr.out b/tests/bsd/L_xattr.out index 12fac95..21eb50f 100644 --- a/tests/bsd/L_xattr.out +++ b/tests/bsd/L_xattr.out @@ -1,3 +1,3 @@ -scratch/link -scratch/xattr -scratch/xattr_2 +./link +./xattr +./xattr_2 diff --git a/tests/bsd/L_xattr.sh b/tests/bsd/L_xattr.sh index 1f61c78..f8b56d8 100644 --- a/tests/bsd/L_xattr.sh +++ b/tests/bsd/L_xattr.sh @@ -1,3 +1,3 @@ -invoke_bfs scratch -quit -xattr || skip +invoke_bfs . -quit -xattr || skip make_xattrs || skip -bfs_diff -L scratch -xattr +bfs_diff -L . -xattr diff --git a/tests/bsd/L_xattrname.out b/tests/bsd/L_xattrname.out index 4dc4836..9e4c172 100644 --- a/tests/bsd/L_xattrname.out +++ b/tests/bsd/L_xattrname.out @@ -1,2 +1,2 @@ -scratch/link -scratch/xattr +./link +./xattr diff --git a/tests/bsd/L_xattrname.sh b/tests/bsd/L_xattrname.sh index 3b2006b..8108d57 100644 --- a/tests/bsd/L_xattrname.sh +++ b/tests/bsd/L_xattrname.sh @@ -1,11 +1,11 @@ -invoke_bfs scratch -quit -xattr || skip +invoke_bfs . -quit -xattr || skip make_xattrs || skip case "$UNAME" in Darwin|FreeBSD) - bfs_diff -L scratch -xattrname bfs_test + bfs_diff -L . -xattrname bfs_test ;; *) - bfs_diff -L scratch -xattrname security.bfs_test + bfs_diff -L . -xattrname security.bfs_test ;; esac diff --git a/tests/bsd/acl.out b/tests/bsd/acl.out index ddf8446..92e2f67 100644 --- a/tests/bsd/acl.out +++ b/tests/bsd/acl.out @@ -1 +1 @@ -scratch/acl +./acl diff --git a/tests/bsd/acl.sh b/tests/bsd/acl.sh index c044398..a13c75f 100644 --- a/tests/bsd/acl.sh +++ b/tests/bsd/acl.sh @@ -1,9 +1,9 @@ -clean_scratch +cd "$TEST" -invoke_bfs scratch -quit -acl || skip +invoke_bfs . -quit -acl || skip -"$XTOUCH" scratch/{normal,acl} -set_acl scratch/acl || skip -ln -s acl scratch/link +"$XTOUCH" normal acl +set_acl acl || skip +ln -s acl link -bfs_diff scratch -acl +bfs_diff . -acl diff --git a/tests/bsd/flags.out b/tests/bsd/flags.out index 11998ed..3216ff5 100644 --- a/tests/bsd/flags.out +++ b/tests/bsd/flags.out @@ -1 +1 @@ -scratch/bar +./bar diff --git a/tests/bsd/flags.sh b/tests/bsd/flags.sh index 949a7d3..eb9bc22 100644 --- a/tests/bsd/flags.sh +++ b/tests/bsd/flags.sh @@ -1,8 +1,8 @@ -invoke_bfs scratch -quit -flags offline || skip +invoke_bfs . -quit -flags offline || skip -clean_scratch +cd "$TEST" -"$XTOUCH" scratch/{foo,bar} -chflags offline scratch/bar || skip +"$XTOUCH" foo bar +chflags offline bar || skip -bfs_diff scratch -flags -offline,nohidden +bfs_diff . -flags -offline,nohidden diff --git a/tests/bsd/rm.out b/tests/bsd/rm.out index fb188b9..9c558e3 100644 --- a/tests/bsd/rm.out +++ b/tests/bsd/rm.out @@ -1 +1 @@ -scratch +. diff --git a/tests/bsd/rm.sh b/tests/bsd/rm.sh index 9ee2b0a..595d514 100644 --- a/tests/bsd/rm.sh +++ b/tests/bsd/rm.sh @@ -1,6 +1,4 @@ -clean_scratch -"$XTOUCH" -p scratch/foo/bar/baz - -(cd scratch && invoke_bfs . -rm) - -bfs_diff scratch +cd "$TEST" +"$XTOUCH" -p foo/bar/baz +invoke_bfs . -rm +bfs_diff . diff --git a/tests/bsd/type_w.out b/tests/bsd/type_w.out index d383f69..a20a4f3 100644 --- a/tests/bsd/type_w.out +++ b/tests/bsd/type_w.out @@ -1,38 +1,34 @@ -1: -rw-r--r-- scratch/mnt/lower/bar -1: -rw-r--r-- scratch/mnt/lower/baz -1: -rw-r--r-- scratch/mnt/lower/foo -1: -rw-r--r-- scratch/mnt/upper/baz/qux -1: -rw-r--r-- scratch/mnt/upper/foo -1: drwxr-xr-x scratch/mnt -1: drwxr-xr-x scratch/mnt/lower -1: drwxr-xr-x scratch/mnt/upper -1: drwxr-xr-x scratch/mnt/upper/baz -2: w--------- scratch/mnt/upper/bar -3: -rw-r--r-- scratch/mnt/lower/bar -3: -rw-r--r-- scratch/mnt/lower/baz -3: -rw-r--r-- scratch/mnt/lower/foo -3: -rw-r--r-- scratch/mnt/upper/baz/qux -3: -rw-r--r-- scratch/mnt/upper/foo -3: drwxr-xr-x scratch/mnt -3: drwxr-xr-x scratch/mnt/lower -3: drwxr-xr-x scratch/mnt/upper -3: drwxr-xr-x scratch/mnt/upper/baz -3: w--------- scratch/mnt/upper/bar -4: -rw-r--r-- scratch/mnt/lower/bar -4: -rw-r--r-- scratch/mnt/lower/baz -4: -rw-r--r-- scratch/mnt/lower/foo -4: -rw-r--r-- scratch/mnt/upper/baz/qux -4: drwxr-xr-x scratch/mnt -4: drwxr-xr-x scratch/mnt/lower -4: drwxr-xr-x scratch/mnt/upper -4: drwxr-xr-x scratch/mnt/upper/baz -5: w--------- scratch/mnt/upper/bar -6: -rw-r--r-- scratch/mnt/lower/bar -6: -rw-r--r-- scratch/mnt/lower/baz -6: -rw-r--r-- scratch/mnt/lower/foo -6: -rw-r--r-- scratch/mnt/upper/baz/qux -6: drwxr-xr-x scratch/mnt -6: drwxr-xr-x scratch/mnt/lower -6: drwxr-xr-x scratch/mnt/upper -6: drwxr-xr-x scratch/mnt/upper/baz -6: w--------- scratch/mnt/upper/bar +1: -rw-r--r-- mnt/lower/bar +1: -rw-r--r-- mnt/lower/baz +1: -rw-r--r-- mnt/lower/foo +1: -rw-r--r-- mnt/upper/baz/qux +1: -rw-r--r-- mnt/upper/foo +1: drwxr-xr-x mnt/lower +1: drwxr-xr-x mnt/upper +1: drwxr-xr-x mnt/upper/baz +2: w--------- mnt/upper/bar +3: -rw-r--r-- mnt/lower/bar +3: -rw-r--r-- mnt/lower/baz +3: -rw-r--r-- mnt/lower/foo +3: -rw-r--r-- mnt/upper/baz/qux +3: -rw-r--r-- mnt/upper/foo +3: drwxr-xr-x mnt/lower +3: drwxr-xr-x mnt/upper +3: drwxr-xr-x mnt/upper/baz +3: w--------- mnt/upper/bar +4: -rw-r--r-- mnt/lower/bar +4: -rw-r--r-- mnt/lower/baz +4: -rw-r--r-- mnt/lower/foo +4: -rw-r--r-- mnt/upper/baz/qux +4: drwxr-xr-x mnt/lower +4: drwxr-xr-x mnt/upper +4: drwxr-xr-x mnt/upper/baz +5: w--------- mnt/upper/bar +6: -rw-r--r-- mnt/lower/bar +6: -rw-r--r-- mnt/lower/baz +6: -rw-r--r-- mnt/lower/foo +6: -rw-r--r-- mnt/upper/baz/qux +6: drwxr-xr-x mnt/lower +6: drwxr-xr-x mnt/upper +6: drwxr-xr-x mnt/upper/baz +6: w--------- mnt/upper/bar diff --git a/tests/bsd/type_w.sh b/tests/bsd/type_w.sh index 15b4e68..aec9cb1 100644 --- a/tests/bsd/type_w.sh +++ b/tests/bsd/type_w.sh @@ -2,51 +2,51 @@ command -v mdconfig &>/dev/null || skip command -v newfs &>/dev/null || skip -clean_scratch +cd "$TEST" # Create a ramdisk -truncate -s1M scratch/img -md=$(bfs_sudo mdconfig scratch/img) || skip +truncate -s1M img +md=$(bfs_sudo mdconfig img) || skip defer bfs_sudo mdconfig -du "$md" # Make an ffs filesystem bfs_sudo newfs -n "/dev/$md" >&2 || skip -mkdir scratch/mnt +mkdir mnt # Mount it -bfs_sudo mount "/dev/$md" scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount "/dev/$md" mnt || skip +defer bfs_sudo umount mnt # Make it owned by us -bfs_sudo chown "$(id -u):$(id -g)" scratch/mnt -"$XTOUCH" -p scratch/mnt/{lower/{foo,bar,baz},upper/{bar,baz/qux}} +bfs_sudo chown "$(id -u):$(id -g)" mnt +"$XTOUCH" -p mnt/{lower/{foo,bar,baz},upper/{bar,baz/qux}} # Mount a union filesystem within it -bfs_sudo mount -t unionfs -o below scratch/mnt/{lower,upper} -defer bfs_sudo umount scratch/mnt/upper +bfs_sudo mount -t unionfs -o below mnt/{lower,upper} +defer bfs_sudo umount mnt/upper # Create a whiteout -rm scratch/mnt/upper/bar +rm mnt/upper/bar # FreeBSD find doesn't have -printf, so munge -ls output munge_ls() { - sed -En 's|.*([-drwx]{10}).*(scratch/.*)|'"$1"': \1 \2|p' + sed -En 's|.*([-drwx]{10}).*(mnt/.*)|'"$1"': \1 \2|p' } # Do a few tests in one { # Normally, we shouldn't see the whiteouts - invoke_bfs scratch/mnt -ls | munge_ls 1 + invoke_bfs mnt -ls | munge_ls 1 # -type w adds whiteouts to the output - invoke_bfs scratch/mnt -type w -ls | munge_ls 2 + invoke_bfs mnt -type w -ls | munge_ls 2 # So this is not the same as test 1 - invoke_bfs scratch/mnt \( -type w -or -not -type w \) -ls | munge_ls 3 + invoke_bfs mnt \( -type w -or -not -type w \) -ls | munge_ls 3 # Unmount the unionfs pop_defer # Now repeat the same tests - invoke_bfs scratch/mnt -ls | munge_ls 4 - invoke_bfs scratch/mnt -type w -ls | munge_ls 5 - invoke_bfs scratch/mnt \( -type w -or -not -type w \) -ls | munge_ls 6 + invoke_bfs mnt -ls | munge_ls 4 + invoke_bfs mnt -type w -ls | munge_ls 5 + invoke_bfs mnt \( -type w -or -not -type w \) -ls | munge_ls 6 } >"$OUT" sort_output diff_output diff --git a/tests/bsd/xattr.out b/tests/bsd/xattr.out index 109e7c9..0afed35 100644 --- a/tests/bsd/xattr.out +++ b/tests/bsd/xattr.out @@ -1,3 +1,3 @@ -scratch/xattr -scratch/xattr_2 -scratch/xattr_link +./xattr +./xattr_2 +./xattr_link diff --git a/tests/bsd/xattr.sh b/tests/bsd/xattr.sh index 4a4658c..68f729a 100644 --- a/tests/bsd/xattr.sh +++ b/tests/bsd/xattr.sh @@ -1,3 +1,3 @@ -invoke_bfs scratch -quit -xattr || skip +invoke_bfs . -quit -xattr || skip make_xattrs || skip -bfs_diff scratch -xattr +bfs_diff . -xattr diff --git a/tests/bsd/xattrname.out b/tests/bsd/xattrname.out index 0285ac1..ef732bd 100644 --- a/tests/bsd/xattrname.out +++ b/tests/bsd/xattrname.out @@ -1,2 +1,2 @@ -scratch/xattr -scratch/xattr_link +./xattr +./xattr_link diff --git a/tests/bsd/xattrname.sh b/tests/bsd/xattrname.sh index 655bd74..38b111a 100644 --- a/tests/bsd/xattrname.sh +++ b/tests/bsd/xattrname.sh @@ -1,11 +1,11 @@ -invoke_bfs scratch -quit -xattr || skip +invoke_bfs . -quit -xattr || skip make_xattrs || skip case "$UNAME" in Darwin|FreeBSD) - bfs_diff scratch -xattrname bfs_test + bfs_diff . -xattrname bfs_test ;; *) - bfs_diff scratch -xattrname security.bfs_test + bfs_diff . -xattrname security.bfs_test ;; esac diff --git a/tests/common/L_ls.sh b/tests/common/L_ls.sh index ced16c6..7ee2b44 100644 --- a/tests/common/L_ls.sh +++ b/tests/common/L_ls.sh @@ -1,2 +1 @@ -clean_scratch -invoke_bfs -L rainbow -ls >scratch/L_ls.out +invoke_bfs -L rainbow -ls >"$OUT" diff --git a/tests/common/L_mount.out b/tests/common/L_mount.out index 2e80082..788579d 100644 --- a/tests/common/L_mount.out +++ b/tests/common/L_mount.out @@ -1,5 +1,5 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/foo/qux -scratch/mnt +. +./foo +./foo/bar +./foo/qux +./mnt diff --git a/tests/common/L_mount.sh b/tests/common/L_mount.sh index aaf9069..fd8042a 100644 --- a/tests/common/L_mount.sh +++ b/tests/common/L_mount.sh @@ -1,13 +1,13 @@ test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} +cd "$TEST" +mkdir foo mnt -bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -ln -s ../mnt scratch/foo/bar -"$XTOUCH" scratch/mnt/baz -ln -s ../mnt/baz scratch/foo/qux +ln -s ../mnt foo/bar +"$XTOUCH" mnt/baz +ln -s ../mnt/baz foo/qux -bfs_diff -L scratch -mount +bfs_diff -L . -mount diff --git a/tests/common/delete.out b/tests/common/delete.out index fb188b9..9c558e3 100644 --- a/tests/common/delete.out +++ b/tests/common/delete.out @@ -1 +1 @@ -scratch +. diff --git a/tests/common/delete.sh b/tests/common/delete.sh index 89cf2a2..638f307 100644 --- a/tests/common/delete.sh +++ b/tests/common/delete.sh @@ -1,7 +1,4 @@ -clean_scratch -"$XTOUCH" -p scratch/foo/bar/baz - -# Don't try to delete '.' -(cd scratch && invoke_bfs . -delete) - -bfs_diff scratch +cd "$TEST" +"$XTOUCH" -p foo/bar/baz +invoke_bfs . -delete +bfs_diff . diff --git a/tests/common/delete_many.out b/tests/common/delete_many.out index fb188b9..9c558e3 100644 --- a/tests/common/delete_many.out +++ b/tests/common/delete_many.out @@ -1 +1 @@ -scratch +. diff --git a/tests/common/delete_many.sh b/tests/common/delete_many.sh index 6274319..48fe4c2 100644 --- a/tests/common/delete_many.sh +++ b/tests/common/delete_many.sh @@ -1,8 +1,8 @@ # Test for https://github.com/tavianator/bfs/issues/67 -clean_scratch -mkdir scratch/foo -"$XTOUCH" scratch/foo/{1..256} +cd "$TEST" +mkdir foo +"$XTOUCH" foo/{1..256} -invoke_bfs scratch/foo -delete -bfs_diff scratch +invoke_bfs foo -delete +bfs_diff . diff --git a/tests/common/execdir_ulimit.out b/tests/common/execdir_ulimit.out index 7f53982..bf52c09 100644 --- a/tests/common/execdir_ulimit.out +++ b/tests/common/execdir_ulimit.out @@ -1,3 +1,4 @@ +./. ./0 ./1 ./2 @@ -30,7 +31,6 @@ ./q ./r ./s -./scratch ./t ./u ./v diff --git a/tests/common/execdir_ulimit.sh b/tests/common/execdir_ulimit.sh index f7fc467..90c93c1 100644 --- a/tests/common/execdir_ulimit.sh +++ b/tests/common/execdir_ulimit.sh @@ -1,6 +1,6 @@ -clean_scratch -mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z -mkdir -p scratch/a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C +cd "$TEST" +mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z +mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C ulimit -n 13 -bfs_diff scratch -execdir echo {} \; +bfs_diff . -execdir echo {} \; diff --git a/tests/common/inum_bind_mount.out b/tests/common/inum_bind_mount.out index a520de3..ede8749 100644 --- a/tests/common/inum_bind_mount.out +++ b/tests/common/inum_bind_mount.out @@ -1,2 +1,2 @@ -scratch/bar -scratch/foo +./bar +./foo diff --git a/tests/common/inum_bind_mount.sh b/tests/common/inum_bind_mount.sh index 47f7c36..892713e 100644 --- a/tests/common/inum_bind_mount.sh +++ b/tests/common/inum_bind_mount.sh @@ -1,9 +1,9 @@ test "$UNAME" = "Linux" || skip -clean_scratch -"$XTOUCH" scratch/{foo,bar} +cd "$TEST" +"$XTOUCH" foo bar baz -bfs_sudo mount --bind scratch/{foo,bar} || skip -defer bfs_sudo umount scratch/bar +bfs_sudo mount --bind foo bar || skip +defer bfs_sudo umount bar -bfs_diff scratch -inum "$(inum scratch/bar)" +bfs_diff . -inum "$(inum bar)" diff --git a/tests/common/inum_mount.out b/tests/common/inum_mount.out index 99c7511..99fa01e 100644 --- a/tests/common/inum_mount.out +++ b/tests/common/inum_mount.out @@ -1 +1 @@ -scratch/mnt +./mnt diff --git a/tests/common/inum_mount.sh b/tests/common/inum_mount.sh index 1bf2d86..7facf57 100644 --- a/tests/common/inum_mount.sh +++ b/tests/common/inum_mount.sh @@ -1,9 +1,9 @@ test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} +cd "$TEST" +mkdir foo mnt -bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -bfs_diff scratch -inum "$(inum scratch/mnt)" +bfs_diff . -inum "$(inum mnt)" diff --git a/tests/common/ls.sh b/tests/common/ls.sh index 85ca39c..bc50d90 100644 --- a/tests/common/ls.sh +++ b/tests/common/ls.sh @@ -1,2 +1 @@ -clean_scratch -invoke_bfs rainbow -ls >scratch/ls.out +invoke_bfs rainbow -ls >"$OUT" diff --git a/tests/common/mount.out b/tests/common/mount.out index f7839fb..6253434 100644 --- a/tests/common/mount.out +++ b/tests/common/mount.out @@ -1,4 +1,4 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/mnt +. +./foo +./foo/bar +./mnt diff --git a/tests/common/mount.sh b/tests/common/mount.sh index db8b801..c9abde5 100644 --- a/tests/common/mount.sh +++ b/tests/common/mount.sh @@ -1,11 +1,11 @@ test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} +cd "$TEST" +mkdir foo mnt -bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -"$XTOUCH" scratch/foo/bar scratch/mnt/baz +"$XTOUCH" foo/bar mnt/baz -bfs_diff scratch -mount +bfs_diff . -mount diff --git a/tests/gnu/L_delete.out b/tests/gnu/L_delete.out index ed0e9a1..7ed5f0d 100644 --- a/tests/gnu/L_delete.out +++ b/tests/gnu/L_delete.out @@ -1,2 +1,2 @@ -scratch -scratch/foo +. +./foo diff --git a/tests/gnu/L_delete.sh b/tests/gnu/L_delete.sh index 8fdb12a..0559c49 100644 --- a/tests/gnu/L_delete.sh +++ b/tests/gnu/L_delete.sh @@ -1,9 +1,8 @@ -clean_scratch -mkdir scratch/foo -mkdir scratch/bar -ln -s ../foo scratch/bar/baz +cd "$TEST" +mkdir foo bar +ln -s ../foo bar/baz # Don't try to rmdir() a symlink -invoke_bfs -L scratch/bar -delete +invoke_bfs -L bar -delete -bfs_diff scratch +bfs_diff . diff --git a/tests/gnu/files0_from_file.sh b/tests/gnu/files0_from_file.sh index 089a20e..81435a0 100644 --- a/tests/gnu/files0_from_file.sh +++ b/tests/gnu/files0_from_file.sh @@ -1,4 +1,4 @@ -clean_scratch +FILE="$TMP/$TEST.in" cd weirdnames -invoke_bfs -mindepth 1 -fprintf ../scratch/files0.in "%P\0" -bfs_diff -files0-from ../scratch/files0.in +invoke_bfs -mindepth 1 -fprintf "$FILE" "%P\0" +bfs_diff -files0-from "$FILE" diff --git a/tests/gnu/fls.sh b/tests/gnu/fls.sh index a86fa20..d2ff794 100644 --- a/tests/gnu/fls.sh +++ b/tests/gnu/fls.sh @@ -1,2 +1 @@ -clean_scratch -invoke_bfs rainbow -fls scratch/fls.out +invoke_bfs rainbow -fls "$OUT" diff --git a/tests/gnu/fls_nonexistent.sh b/tests/gnu/fls_nonexistent.sh index ff86763..2854569 100644 --- a/tests/gnu/fls_nonexistent.sh +++ b/tests/gnu/fls_nonexistent.sh @@ -1 +1 @@ -! invoke_bfs rainbow -fls scratch/nonexistent/path +! invoke_bfs rainbow -fls nonexistent/path diff --git a/tests/gnu/fprint0_nonexistent.sh b/tests/gnu/fprint0_nonexistent.sh index ec14c2d..4906081 100644 --- a/tests/gnu/fprint0_nonexistent.sh +++ b/tests/gnu/fprint0_nonexistent.sh @@ -1 +1 @@ -! invoke_bfs basic -fprint0 scratch/nonexistent/path +! invoke_bfs basic -fprint0 nonexistent/path diff --git a/tests/gnu/fprint_duplicate.sh b/tests/gnu/fprint_duplicate.sh index 5275502..8533b05 100644 --- a/tests/gnu/fprint_duplicate.sh +++ b/tests/gnu/fprint_duplicate.sh @@ -1,7 +1,7 @@ -"$XTOUCH" -p scratch/foo.out -ln scratch/foo.out scratch/foo.hard -ln -s foo.out scratch/foo.soft +"$XTOUCH" -p "$TEST/foo.out" +ln "$TEST/foo.out" "$TEST/foo.hard" +ln -s foo.out "$TEST/foo.soft" -invoke_bfs basic -fprint scratch/foo.out -fprint scratch/foo.hard -fprint scratch/foo.soft -sort scratch/foo.out >"$OUT" +invoke_bfs basic -fprint "$TEST/foo.out" -fprint "$TEST/foo.hard" -fprint "$TEST/foo.soft" +sort "$TEST/foo.out" >"$OUT" diff_output diff --git a/tests/gnu/fprint_nonexistent.sh b/tests/gnu/fprint_nonexistent.sh index 4409162..2a403a2 100644 --- a/tests/gnu/fprint_nonexistent.sh +++ b/tests/gnu/fprint_nonexistent.sh @@ -1 +1 @@ -! invoke_bfs basic -fprint scratch/nonexistent/path +! invoke_bfs basic -fprint nonexistent/path diff --git a/tests/gnu/fprintf_nonexistent.sh b/tests/gnu/fprintf_nonexistent.sh index 160e739..b1eea10 100644 --- a/tests/gnu/fprintf_nonexistent.sh +++ b/tests/gnu/fprintf_nonexistent.sh @@ -1 +1 @@ -! invoke_bfs basic -fprintf scratch/nonexistent/path '%p\n' +! invoke_bfs basic -fprintf nonexistent/path '%p\n' diff --git a/tests/gnu/fstype_stacked.out b/tests/gnu/fstype_stacked.out index 99c7511..c1e0e6c 100644 --- a/tests/gnu/fstype_stacked.out +++ b/tests/gnu/fstype_stacked.out @@ -1 +1 @@ -scratch/mnt +mnt diff --git a/tests/gnu/fstype_stacked.sh b/tests/gnu/fstype_stacked.sh index a4b067a..a9739bb 100644 --- a/tests/gnu/fstype_stacked.sh +++ b/tests/gnu/fstype_stacked.sh @@ -1,12 +1,12 @@ test "$UNAME" = "Linux" || skip -clean_scratch -mkdir scratch/mnt +cd "$TEST" +mkdir mnt -bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -bfs_sudo mount -t ramfs ramfs scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount -t ramfs ramfs mnt || skip +defer bfs_sudo umount mnt -bfs_diff scratch/mnt -fstype ramfs -print -o -printf '%p: %F\n' +bfs_diff mnt -fstype ramfs -print -o -printf '%p: %F\n' diff --git a/tests/gnu/fstype_umount.sh b/tests/gnu/fstype_umount.sh index b6da7a3..81c195f 100644 --- a/tests/gnu/fstype_umount.sh +++ b/tests/gnu/fstype_umount.sh @@ -1,12 +1,12 @@ test "$UNAME" = "Linux" || skip -clean_scratch +cd "$TEST" -mkdir scratch/tmp -bfs_sudo mount -t tmpfs tmpfs scratch/tmp || skip -defer bfs_sudo umount -R scratch/tmp +mkdir tmp +bfs_sudo mount -t tmpfs tmpfs tmp || skip +defer bfs_sudo umount -R tmp -mkdir scratch/tmp/ram -bfs_sudo mount -t ramfs ramfs scratch/tmp/ram || skip +mkdir tmp/ram +bfs_sudo mount -t ramfs ramfs tmp/ram || skip -bfs_diff scratch/tmp -path scratch/tmp -exec "${SUDO[@]}" umount scratch/tmp/ram \; , -fstype ramfs -print +bfs_diff tmp -path tmp -exec "${SUDO[@]}" umount tmp/ram \; , -fstype ramfs -print diff --git a/tests/gnu/ignore_readdir_race.sh b/tests/gnu/ignore_readdir_race.sh index 6586bcc..75165f6 100644 --- a/tests/gnu/ignore_readdir_race.sh +++ b/tests/gnu/ignore_readdir_race.sh @@ -1,5 +1,5 @@ -clean_scratch -"$XTOUCH" scratch/{foo,bar} +cd "$TEST" +"$XTOUCH" foo bar # -links 1 forces a stat() call, which will fail for the second file -invoke_bfs scratch -mindepth 1 -ignore_readdir_race -links 1 -exec "$TESTS/remove-sibling.sh" {} \; +invoke_bfs . -mindepth 1 -ignore_readdir_race -links 1 -exec "$TESTS/remove-sibling.sh" {} \; diff --git a/tests/gnu/ignore_readdir_race_notdir.sh b/tests/gnu/ignore_readdir_race_notdir.sh index 5b8b56d..8b03164 100644 --- a/tests/gnu/ignore_readdir_race_notdir.sh +++ b/tests/gnu/ignore_readdir_race_notdir.sh @@ -1,5 +1,5 @@ # Check -ignore_readdir_race handling when a directory is replaced with a file -clean_scratch -"$XTOUCH" -p scratch/foo/bar +cd "$TEST" +"$XTOUCH" -p foo/bar -invoke_bfs scratch -mindepth 1 -ignore_readdir_race -execdir rm -r {} \; -execdir "$XTOUCH" {} \; +invoke_bfs . -mindepth 1 -ignore_readdir_race -execdir rm -r {} \; -execdir "$XTOUCH" {} \; diff --git a/tests/gnu/inum_automount.out b/tests/gnu/inum_automount.out index 7b53ae3..3378e2d 100644 --- a/tests/gnu/inum_automount.out +++ b/tests/gnu/inum_automount.out @@ -1 +1 @@ -scratch/automnt +./automnt diff --git a/tests/gnu/inum_automount.sh b/tests/gnu/inum_automount.sh index 261a4be..86b23e1 100644 --- a/tests/gnu/inum_automount.sh +++ b/tests/gnu/inum_automount.sh @@ -2,13 +2,13 @@ command -v systemd-mount &>/dev/null || skip -clean_scratch -mkdir scratch/{foo,automnt} +cd "$TEST" +mkdir foo automnt -bfs_sudo systemd-mount -A -o bind basic scratch/automnt || skip -defer bfs_sudo systemd-umount scratch/automnt +bfs_sudo systemd-mount -A -o bind "$TMP/basic" automnt || skip +defer bfs_sudo systemd-umount automnt -before=$(inum scratch/automnt) -bfs_diff scratch -inum "$before" -prune -after=$(inum scratch/automnt) +before=$(inum automnt) +bfs_diff . -inum "$before" -prune +after=$(inum automnt) ((before == after)) diff --git a/tests/gnu/printf_Y_error.out b/tests/gnu/printf_Y_error.out index 410a9b5..1dd554e 100644 --- a/tests/gnu/printf_Y_error.out +++ b/tests/gnu/printf_Y_error.out @@ -1,3 +1,3 @@ -(scratch) () d d -(scratch/bar) (foo/bar) l ? -(scratch/foo) () d d +(.) () d d +(./bar) (foo/bar) l ? +(./foo) () d d diff --git a/tests/gnu/printf_Y_error.sh b/tests/gnu/printf_Y_error.sh index 13d52e3..d3130ce 100644 --- a/tests/gnu/printf_Y_error.sh +++ b/tests/gnu/printf_Y_error.sh @@ -1,8 +1,8 @@ -clean_scratch -mkdir scratch/foo -ln -s foo/bar scratch/bar +cd "$TEST" +mkdir foo +ln -s foo/bar bar -chmod -x scratch/foo -defer chmod +x scratch/foo +chmod -x foo +defer chmod +x foo -! bfs_diff scratch -printf '(%p) (%l) %y %Y\n' +! bfs_diff . -printf '(%p) (%l) %y %Y\n' diff --git a/tests/gnu/regex_invalid_utf8.out b/tests/gnu/regex_invalid_utf8.out index 03f3f58..a133b1a 100644 --- a/tests/gnu/regex_invalid_utf8.out +++ b/tests/gnu/regex_invalid_utf8.out @@ -1 +1 @@ -scratch/â„ +./â„ diff --git a/tests/gnu/regex_invalid_utf8.sh b/tests/gnu/regex_invalid_utf8.sh index 603d688..7006dcd 100644 --- a/tests/gnu/regex_invalid_utf8.sh +++ b/tests/gnu/regex_invalid_utf8.sh @@ -1,8 +1,8 @@ -clean_scratch +cd "$TEST" # Incomplete UTF-8 sequences -touch scratch/$'\xC3' || skip -touch scratch/$'\xE2\x84' || skip -touch scratch/$'\xF0\x9F\x92' || skip +touch $'\xC3' || skip +touch $'\xE2\x84' || skip +touch $'\xF0\x9F\x92' || skip -bfs_diff scratch -regex 'scratch/..' +bfs_diff . -regex '\./..' diff --git a/tests/gnu/xtype_bind_mount.out b/tests/gnu/xtype_bind_mount.out index 16804ea..d18d706 100644 --- a/tests/gnu/xtype_bind_mount.out +++ b/tests/gnu/xtype_bind_mount.out @@ -1,2 +1,2 @@ -scratch/link -scratch/null +./link +./null diff --git a/tests/gnu/xtype_bind_mount.sh b/tests/gnu/xtype_bind_mount.sh index 1cc20ec..35fb3f5 100644 --- a/tests/gnu/xtype_bind_mount.sh +++ b/tests/gnu/xtype_bind_mount.sh @@ -1,10 +1,10 @@ test "$UNAME" = "Linux" || skip -clean_scratch -"$XTOUCH" scratch/{file,null} -ln -s /dev/null scratch/link +cd "$TEST" +"$XTOUCH" file null +ln -s /dev/null link -bfs_sudo mount --bind /dev/null scratch/null || skip -defer bfs_sudo umount scratch/null +bfs_sudo mount --bind /dev/null null || skip +defer bfs_sudo umount null -bfs_diff -L scratch -type c +bfs_diff . -xtype c diff --git a/tests/posix/L_xdev.out b/tests/posix/L_xdev.out index 2e80082..788579d 100644 --- a/tests/posix/L_xdev.out +++ b/tests/posix/L_xdev.out @@ -1,5 +1,5 @@ -scratch -scratch/foo -scratch/foo/bar -scratch/foo/qux -scratch/mnt +. +./foo +./foo/bar +./foo/qux +./mnt diff --git a/tests/posix/L_xdev.sh b/tests/posix/L_xdev.sh index d16c211..82d8605 100644 --- a/tests/posix/L_xdev.sh +++ b/tests/posix/L_xdev.sh @@ -1,13 +1,13 @@ test "$UNAME" = "Darwin" && skip -clean_scratch -mkdir scratch/{foo,mnt} +cd "$TEST" +mkdir foo mnt -bfs_sudo mount -t tmpfs tmpfs scratch/mnt || skip -defer bfs_sudo umount scratch/mnt +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt -ln -s ../mnt scratch/foo/bar -"$XTOUCH" scratch/mnt/baz -ln -s ../mnt/baz scratch/foo/qux +ln -s ../mnt foo/bar +"$XTOUCH" mnt/baz +ln -s ../mnt/baz foo/qux -bfs_diff -L scratch -xdev +bfs_diff -L . -xdev diff --git a/tests/posix/depth_error.out b/tests/posix/depth_error.out index ed0e9a1..7ed5f0d 100644 --- a/tests/posix/depth_error.out +++ b/tests/posix/depth_error.out @@ -1,2 +1,2 @@ -scratch -scratch/foo +. +./foo diff --git a/tests/posix/depth_error.sh b/tests/posix/depth_error.sh index a6429d1..db414ba 100644 --- a/tests/posix/depth_error.sh +++ b/tests/posix/depth_error.sh @@ -1,7 +1,7 @@ -clean_scratch -"$XTOUCH" -p scratch/foo/bar +cd "$TEST" +"$XTOUCH" -p foo/bar -chmod a-r scratch/foo -defer chmod +r scratch/foo +chmod a-r foo +defer chmod +r foo -! bfs_diff scratch -depth +! bfs_diff . -depth diff --git a/tests/posix/overlayfs.out b/tests/posix/overlayfs.out index 754d01d..b472b56 100644 --- a/tests/posix/overlayfs.out +++ b/tests/posix/overlayfs.out @@ -1,5 +1,5 @@ -scratch/merged -scratch/merged/bar -scratch/merged/baz -scratch/merged/baz/qux -scratch/merged/foo +merged +merged/bar +merged/baz +merged/baz/qux +merged/foo diff --git a/tests/posix/overlayfs.sh b/tests/posix/overlayfs.sh index 4cccebf..21ef22f 100644 --- a/tests/posix/overlayfs.sh +++ b/tests/posix/overlayfs.sh @@ -1,11 +1,11 @@ test "$UNAME" = "Linux" || skip -clean_scratch -"$XTOUCH" -p scratch/{lower/{foo,bar,baz},upper/{bar,baz/qux}} +cd "$TEST" +"$XTOUCH" -p lower/{foo,bar,baz} upper/{bar,baz/qux} -mkdir -p scratch/{work,merged} -bfs_sudo mount -t overlay overlay -olowerdir=scratch/lower,upperdir=scratch/upper,workdir=scratch/work scratch/merged || skip -defer bfs_sudo rm -rf scratch/work -defer bfs_sudo umount scratch/merged +mkdir -p work merged +bfs_sudo mount -t overlay overlay -olowerdir=lower,upperdir=upper,workdir=work merged || skip +defer bfs_sudo rm -rf work +defer bfs_sudo umount merged -bfs_diff scratch/merged +bfs_diff merged diff --git a/tests/posix/readdir_error.sh b/tests/posix/readdir_error.sh index fc48eb1..82fcd17 100644 --- a/tests/posix/readdir_error.sh +++ b/tests/posix/readdir_error.sh @@ -1,27 +1,27 @@ test "$UNAME" = "Linux" || skip -clean_scratch -mkfifo scratch/{fever,pid,wait,running} +cd "$TEST" +mkfifo hang pid wait running ( # Create a zombie process - cat scratch/fever >/dev/null & - # Write the PID to scratch/pid - echo $! >scratch/pid + cat hang >/dev/null & + # Write the PID to pid + echo $! >pid # Don't wait on the zombie process - exec cat scratch/wait scratch/fever >scratch/running + exec cat wait hang >running ) & # Kill the parent cat on exit defer kill -9 %1 # Read the child PID -read -r pid scratch/wait & -read -r _ wait & +read -r _ /dev/null; then - for path in "$TMP/scratch"/*; do - if mountpoint -q "$path"; then - sudo umount "$path" - fi - done - fi - - # Reset any modified permissions - chmod -R +rX "$TMP/scratch" - - rm -rf "$TMP/scratch" - fi - - mkdir "$TMP/scratch" } # Clean up temporary directories on exit @@ -169,8 +147,5 @@ clean_stddirs() { fi done - # In case a test left anything weird in scratch/ - clean_scratch - rm -rf "$TMP" } -- cgit v1.2.3 From 4c7a49a83e082a7bfff85a9305882f0912655123 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 10:12:25 -0400 Subject: tests: Run test cases in parallel --- GNUmakefile | 7 ++++++- tests/getopts.sh | 12 +++++++++++ tests/run.sh | 61 ++++++++++++++++++++++++++++++++++++++++++-------------- 3 files changed, 64 insertions(+), 16 deletions(-) (limited to 'tests') diff --git a/GNUmakefile b/GNUmakefile index 45797e2..62f24fb 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -287,8 +287,13 @@ check: $(CHECKS) $(UNIT_CHECKS): check-%: $(BIN)/tests/% $< +JOBS := $(filter -j%,$(MAKEFLAGS)) +ifndef JOBS + JOBS := -j1 +endif + $(STRATEGY_CHECKS): check-%: $(BIN)/bfs $(TEST_UTILS) - ./tests/tests.sh --bfs="$(BIN)/bfs -S $*" $(TEST_FLAGS) + ./tests/tests.sh $(JOBS) --bfs="$(BIN)/bfs -S $*" $(TEST_FLAGS) # Custom test flags for distcheck DISTCHECK_FLAGS := -s TEST_FLAGS="--sudo --verbose=skipped" diff --git a/tests/getopts.sh b/tests/getopts.sh index 7d3ef4b..d34df4f 100644 --- a/tests/getopts.sh +++ b/tests/getopts.sh @@ -52,6 +52,7 @@ EOF # Parse the command line parse_args() { + JOBS=0 PATTERNS=() SUDO=() STOP=0 @@ -64,6 +65,9 @@ parse_args() { for arg; do case "$arg" in + -j*) + JOBS="${arg#-j}" + ;; --bfs=*) BFS="${arg#*=}" ;; @@ -127,6 +131,14 @@ parse_args() { esac done + if ((JOBS == 0)); then + if command -v nproc &>/dev/null; then + JOBS=$(nproc) + else + JOBS=1 + fi + fi + # Try to resolve the path to $BFS before we cd, while also supporting # --bfs="./bin/bfs -S ids" read -a BFS <<<"${BFS:-$BIN/bfs}" diff --git a/tests/run.sh b/tests/run.sh index 28801f4..b46fde6 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -42,6 +42,42 @@ run_test() ( source "$@" ) +# Run a test in the background +bg_test() { + if ((VERBOSE_ERRORS)); then + run_test "$1" + else + run_test "$1" 2>"$TMP/TEST.err" + fi + ret=$? + + if ((ret != 0 && ret != EX_SKIP)); then + ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 + color printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" + fi + + return $ret +} + +# Reap a background job +reap() { + wait -n + ret=$? + ((BG--)) + + case $ret in + 0) + ((++passed)) + ;; + $EX_SKIP) + ((++skipped)) + ;; + *) + ((++failed)) + ;; + esac +} + # Run all the tests run_tests() { if ((VERBOSE_TESTS)); then @@ -70,6 +106,8 @@ run_tests() { TEST_FMT="." fi + BG=0 + # Turn off set -e (but turn it back on in run_test) set +e @@ -79,23 +117,16 @@ run_tests() { mkdir -p "$TMP/$TEST" OUT="$TMP/$TEST.out" - if ((VERBOSE_ERRORS)); then - run_test "$TESTS/$TEST.sh" - else - run_test "$TESTS/$TEST.sh" 2>"$TMP/$TEST.err" + if ((BG >= JOBS)); then + reap fi - status=$? + ((++BG)) - if ((status == 0)); then - ((++passed)) - elif ((status == EX_SKIP)); then - ((++skipped)) - else - ((++failed)) - ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 - color printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" - ((STOP)) && break - fi + bg_test "$TESTS/$TEST.sh" & + done + + while ((BG > 0)); do + reap done printf "${BOL}" -- cgit v1.2.3 From de8e0fbb8c1e59b3f1af72a07477b63a8f0bbc75 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 11:42:33 -0400 Subject: tests: Fix Bash 3 compatibility --- tests/bfs/execdir_plus_nonexistent.sh | 6 ++--- tests/common/execdir_nonexistent.sh | 6 ++--- tests/posix/L_loops.sh | 4 ++-- tests/posix/exec_nonexistent.sh | 7 ++---- tests/posix/exec_plus_nonexistent.sh | 6 ++--- tests/run.sh | 44 +++++++++++++++++++++++++---------- tests/util.sh | 11 ++++----- 7 files changed, 47 insertions(+), 37 deletions(-) (limited to 'tests') diff --git a/tests/bfs/execdir_plus_nonexistent.sh b/tests/bfs/execdir_plus_nonexistent.sh index e3b4d2d..ed7ed56 100644 --- a/tests/bfs/execdir_plus_nonexistent.sh +++ b/tests/bfs/execdir_plus_nonexistent.sh @@ -1,4 +1,2 @@ -! stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} + 2>&1 >/dev/null) -[ -n "$stderr" ] - -! bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print +bfs_diff basic -execdir "$TESTS/nonexistent" {} + -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/common/execdir_nonexistent.sh b/tests/common/execdir_nonexistent.sh index 4bb4fdb..0ec013c 100644 --- a/tests/common/execdir_nonexistent.sh +++ b/tests/common/execdir_nonexistent.sh @@ -1,4 +1,2 @@ -! stderr=$(invoke_bfs basic -execdir "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) -[ -n "$stderr" ] - -! bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print +bfs_diff basic -print -execdir "$TESTS/nonexistent" {} \; -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/posix/L_loops.sh b/tests/posix/L_loops.sh index 1314401..01b7efc 100644 --- a/tests/posix/L_loops.sh +++ b/tests/posix/L_loops.sh @@ -1,4 +1,4 @@ # POSIX says it's okay to either stop or keep going on seeing a filesystem # loop, as long as a diagnostic is printed -! errors=$(invoke_bfs -L loops 2>&1 >/dev/null) -[ -n "$errors" ] +invoke_bfs -L loops >/dev/null 2>"$OUT" && fail +test -s "$OUT" diff --git a/tests/posix/exec_nonexistent.sh b/tests/posix/exec_nonexistent.sh index 901be86..a9ff052 100644 --- a/tests/posix/exec_nonexistent.sh +++ b/tests/posix/exec_nonexistent.sh @@ -1,7 +1,4 @@ # Failure to execute the command should lead to an error message and # non-zero exit status. See https://unix.stackexchange.com/q/704522/56202 - -! stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} \; 2>&1 >/dev/null) -[ -n "$stderr" ] - -! bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print +bfs_diff basic -print -exec "$TESTS/nonexistent" {} \; -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/posix/exec_plus_nonexistent.sh b/tests/posix/exec_plus_nonexistent.sh index 6bddc67..24582a3 100644 --- a/tests/posix/exec_plus_nonexistent.sh +++ b/tests/posix/exec_plus_nonexistent.sh @@ -1,4 +1,2 @@ -! stderr=$(invoke_bfs basic -exec "$TESTS/nonexistent" {} + 2>&1 >/dev/null) -[ -n "$stderr" ] - -! bfs_diff basic -exec "$TESTS/nonexistent" {} + -print +bfs_diff basic -exec "$TESTS/nonexistent" {} + -print 2>"$TEST/err" && fail +test -s "$TEST/err" diff --git a/tests/run.sh b/tests/run.sh index b46fde6..d581476 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -47,7 +47,7 @@ bg_test() { if ((VERBOSE_ERRORS)); then run_test "$1" else - run_test "$1" 2>"$TMP/TEST.err" + run_test "$1" 2>"$TMP/$TEST.err" fi ret=$? @@ -59,9 +59,21 @@ bg_test() { return $ret } -# Reap a background job -reap() { - wait -n +# Wait for any background job to complete +if ((BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 3))); then + wait_any() { + wait -n + } +else + wait_any() { + read -ra jobs < <(jobs -p) + wait ${jobs[0]} + } +fi + +# Wait for a background test to finish +wait_test() { + wait_any ret=$? ((BG--)) @@ -118,7 +130,7 @@ run_tests() { OUT="$TMP/$TEST.out" if ((BG >= JOBS)); then - reap + wait_test fi ((++BG)) @@ -126,7 +138,7 @@ run_tests() { done while ((BG > 0)); do - reap + wait_test done printf "${BOL}" @@ -145,6 +157,14 @@ run_tests() { ## Utilities for the tests themselves +# Default return value for failed tests +EX_FAIL=1 + +# Fail the current test +fail() { + exit $EX_FAIL +} + # Return value when a test is skipped EX_SKIP=77 @@ -166,9 +186,9 @@ skip() { # Run a command and check its exit status check_exit() { local expected="$1" - local actual="0" + local actual=0 shift - "$@" || actual="$?" + "$@" || actual=$? ((actual == expected)) } @@ -253,9 +273,9 @@ invoke_bfs() { # Allow bfs to fail, but not crash if ((ret > 125)); then - exit "$ret" + exit $ret else - return "$ret" + return $ret fi } @@ -275,9 +295,9 @@ bfs_pty() { "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" || ret=$? if ((ret > 125)); then - exit "$ret" + exit $ret else - return "$ret" + return $ret fi } diff --git a/tests/util.sh b/tests/util.sh index 31a7b6c..bfa5d16 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -120,7 +120,7 @@ callers() { # Print a message including path, line number, and command debug() { - local file="${1/#*\/tests\//tests\/}" + local file="${1/#*\/tests\//tests/}" set -- "$file" "${@:2}" color printf "${BLD}%s:%d:${RST} %s\n %s\n" "$@" } @@ -136,14 +136,13 @@ quote() { fi } +DEFER_LEVEL=-1 + # Run a command when this (sub)shell exits defer() { - # Refresh trap state before trap -p - # See https://unix.stackexchange.com/a/556888/56202 - trap -- KILL - # Check if the EXIT trap is already set - if ! trap -p EXIT | grep -q pop_defers; then + if ((DEFER_LEVEL != BASH_SUBSHELL)); then + DEFER_LEVEL=$BASH_SUBSHELL DEFER_CMDS=() DEFER_LINES=() DEFER_FILES=() -- cgit v1.2.3 From 63c43d7fb463529219a4de4d1fddb8953056346d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 13:00:51 -0400 Subject: tests: Clean up verbose logging a bit --- tests/run.sh | 56 ++++++++++++++++++++++++++++++++++++-------------------- 1 file changed, 36 insertions(+), 20 deletions(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index d581476..f93cb4b 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -239,25 +239,41 @@ bfs_verbose() { } bfs_verbose_impl() { - printf "${GRN}%q${RST} " "${BFS[@]}" + printf "${GRN}%q${RST}" "${BFS[0]}" + if ((${#BFS[@]} > 1)); then + printf " ${GRN}%q${RST}" "${BFS[1:]}" + fi - local expr_started= + local expr_started=0 color for arg; do - if [[ $arg == -[A-Z]* ]]; then - printf "${CYN}%q${RST} " "$arg" - elif [[ $arg == [\(!] || $arg == -[ao] || $arg == -and || $arg == -or || $arg == -not ]]; then - expr_started=yes - printf "${RED}%q${RST} " "$arg" - elif [[ $expr_started && $arg == [\),] ]]; then - printf "${RED}%q${RST} " "$arg" - elif [[ $arg == -?* ]]; then - expr_started=yes - printf "${BLU}%q${RST} " "$arg" - elif [ "$expr_started" ]; then - printf "${BLD}%q${RST} " "$arg" - else - printf "${MAG}%q${RST} " "$arg" - fi + case "$arg" in + -[A-Z]*|-[dsxf]|-j*) + color="${CYN}" + ;; + \(|!|-[ao]|-and|-or|-not|-exclude) + expr_started=1 + color="${RED}" + ;; + \)|,) + if ((expr_started)); then + color="${RED}" + else + color="${MAG}" + fi + ;; + -?*) + expr_started=1 + color="${BLU}" + ;; + *) + if ((expr_started)); then + color="${BLD}" + else + color="${MAG}" + fi + ;; + esac + printf " ${color}%q${RST}" "$arg" done printf '\n' @@ -336,8 +352,8 @@ make_xattrs() { EX_DIFF=20 # Detect colored diff support -if ((COLOR_STDERR)) && diff --color=always /dev/null /dev/null 2>/dev/null; then - DIFF="diff --color=always" +if diff --color /dev/null /dev/null 2>/dev/null; then + DIFF="diff --color" else DIFF="diff" fi @@ -354,7 +370,7 @@ diff_output() { if ((UPDATE)); then cp "$OUT" "$GOLD" else - $DIFF -u "$GOLD" "$OUT" >&2 + $DIFF -u "$GOLD" "$OUT" >&4 fi } -- cgit v1.2.3 From 62415c0d1530f271ce9d417fa075ac00b9473f1e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 13:05:15 -0400 Subject: tests: Document -j --- tests/getopts.sh | 41 ++++++++++++++++++++--------------------- 1 file changed, 20 insertions(+), 21 deletions(-) (limited to 'tests') diff --git a/tests/getopts.sh b/tests/getopts.sh index d34df4f..ac75140 100644 --- a/tests/getopts.sh +++ b/tests/getopts.sh @@ -5,14 +5,32 @@ ## Argument parsing +if command -v nproc &>/dev/null; then + JOBS=$(nproc) +else + JOBS=1 +fi +PATTERNS=() +SUDO=() +STOP=0 +CLEAN=1 +UPDATE=0 +VERBOSE_COMMANDS=0 +VERBOSE_ERRORS=0 +VERBOSE_SKIPPED=0 +VERBOSE_TESTS=0 + # Print usage information usage() { local pad=$(printf "%*s" ${#0} "") color cat </dev/null; then - JOBS=$(nproc) - else - JOBS=1 - fi - fi - # Try to resolve the path to $BFS before we cd, while also supporting # --bfs="./bin/bfs -S ids" read -a BFS <<<"${BFS:-$BIN/bfs}" -- cgit v1.2.3 From d2d5e5ad2e4b61493d27bfe8c9341fd81f703ae9 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 13:06:44 -0400 Subject: tests/bfs/printf_color: Fix race condition Invoking bfs in the top directory can lead it to explore other tests' scratch directories, so explicitly exclude them. --- tests/bfs/printf_color.out | 1 + tests/bfs/printf_color.sh | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/bfs/printf_color.out b/tests/bfs/printf_color.out index 6641e9a..77d21c3 100644 --- a/tests/bfs/printf_color.out +++ b/tests/bfs/printf_color.out @@ -1,4 +1,5 @@ . $'./rainbow/\e[1m' $'\e[0m' $'./rainbow/\e[1m/'$'\e[0m' $'rainbow/\e[1m/'$'\e[0m' +. . . . . . rainbow ./rainbow rainbow . ./rainbow exec.sh ./rainbow/exec.sh rainbow/exec.sh . ./rainbow $'\e[1m' $'./rainbow/\e[1m' $'rainbow/\e[1m' diff --git a/tests/bfs/printf_color.sh b/tests/bfs/printf_color.sh index 7bb38c2..3641ddb 100644 --- a/tests/bfs/printf_color.sh +++ b/tests/bfs/printf_color.sh @@ -1 +1 @@ -bfs_diff -color -path './rainbow*' -printf '%H %h %f %p %P %l\n' +bfs_diff -color -exclude \( -depth 1 -not -name rainbow \) -printf '%H %h %f %p %P %l\n' -- cgit v1.2.3 From 8c06e033483215e5148bc803b61207b99d458a48 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 13:34:33 -0400 Subject: tests/bfs/help: Use ... && fail instead of ! ... ! false doesn't trigger an error with set -e. --- tests/bfs/help.sh | 8 ++++---- tests/bsd/f_incomplete.sh | 1 - 2 files changed, 4 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/bfs/help.sh b/tests/bfs/help.sh index 2c0b28a..ec53867 100644 --- a/tests/bfs/help.sh +++ b/tests/bfs/help.sh @@ -1,4 +1,4 @@ -! invoke_bfs -help | grep -E '\{...?\}' -! invoke_bfs -D help | grep -E '\{...?\}' -! invoke_bfs -S help | grep -E '\{...?\}' -! invoke_bfs -regextype help | grep -E '\{...?\}' +invoke_bfs -help | grep -E '\{...?\}' && fail +invoke_bfs -D help | grep -E '\{...?\}' && fail +invoke_bfs -S help | grep -E '\{...?\}' && fail +invoke_bfs -regextype help | grep -E '\{...?\}' && fail diff --git a/tests/bsd/f_incomplete.sh b/tests/bsd/f_incomplete.sh index 50afe42..0dfb19f 100644 --- a/tests/bsd/f_incomplete.sh +++ b/tests/bsd/f_incomplete.sh @@ -1,2 +1 @@ ! invoke_bfs -f - -- cgit v1.2.3 From e1390e21d6fcfa97838bdaff968d9cfb26261065 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 23 Oct 2023 13:46:52 -0400 Subject: tests/bfs/help: Fix exit status --- tests/bfs/help.sh | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/bfs/help.sh b/tests/bfs/help.sh index ec53867..5029c7e 100644 --- a/tests/bfs/help.sh +++ b/tests/bfs/help.sh @@ -1,4 +1,4 @@ -invoke_bfs -help | grep -E '\{...?\}' && fail -invoke_bfs -D help | grep -E '\{...?\}' && fail -invoke_bfs -S help | grep -E '\{...?\}' && fail -invoke_bfs -regextype help | grep -E '\{...?\}' && fail +! invoke_bfs -help | grep -E '\{...?\}' || fail +! invoke_bfs -D help | grep -E '\{...?\}' || fail +! invoke_bfs -S help | grep -E '\{...?\}' || fail +! invoke_bfs -regextype help | grep -E '\{...?\}' || fail -- cgit v1.2.3 From 1cfa66a4268a4eefea29f207baaefd76d7792027 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 2 Nov 2023 11:29:28 -0400 Subject: tests: Add tests for ulimit -n after -exec --- tests/gnu/execdir_ulimit.out | 16 ++++++++++++++++ tests/gnu/execdir_ulimit.sh | 2 ++ tests/posix/exec_ulimit.out | 16 ++++++++++++++++ tests/posix/exec_ulimit.sh | 2 ++ 4 files changed, 36 insertions(+) create mode 100644 tests/gnu/execdir_ulimit.out create mode 100644 tests/gnu/execdir_ulimit.sh create mode 100644 tests/posix/exec_ulimit.out create mode 100644 tests/posix/exec_ulimit.sh (limited to 'tests') diff --git a/tests/gnu/execdir_ulimit.out b/tests/gnu/execdir_ulimit.out new file mode 100644 index 0000000..7931f9a --- /dev/null +++ b/tests/gnu/execdir_ulimit.out @@ -0,0 +1,16 @@ +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/gnu/execdir_ulimit.sh b/tests/gnu/execdir_ulimit.sh new file mode 100644 index 0000000..3245dad --- /dev/null +++ b/tests/gnu/execdir_ulimit.sh @@ -0,0 +1,2 @@ +ulimit -Sn 16 +bfs_diff deep -type f -execdir bash -c 'printf "%d %s\n" $(ulimit -Sn) "$1"' bash {} \; diff --git a/tests/posix/exec_ulimit.out b/tests/posix/exec_ulimit.out new file mode 100644 index 0000000..587ece2 --- /dev/null +++ b/tests/posix/exec_ulimit.out @@ -0,0 +1,16 @@ +16 deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +16 deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/posix/exec_ulimit.sh b/tests/posix/exec_ulimit.sh new file mode 100644 index 0000000..8ac8ff1 --- /dev/null +++ b/tests/posix/exec_ulimit.sh @@ -0,0 +1,2 @@ +ulimit -Sn 16 +bfs_diff deep -type f -exec bash -c 'printf "%d %s\n" $(ulimit -Sn) "${1:0:6}/.../${1##*/}"' bash {} \; -- cgit v1.2.3 From 79aee58a4621d01c4b1e98c332775f3b87213ddb Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 6 Nov 2023 13:08:56 -0500 Subject: Treat NO_COLOR="" the same as unset The docs say > Command-line software which adds ANSI color to its output by default > should check for a NO_COLOR environment variable that, when present > and not an empty string (regardless of its value), prevents the > addition of ANSI color. but we were not checking for the empty string. Link: https://no-color.org/ Link: https://github.com/sharkdp/fd/pull/1421 --- src/parse.c | 3 ++- tests/bfs/nocolor_env_empty.out | 27 +++++++++++++++++++++++++++ tests/bfs/nocolor_env_empty.sh | 5 +++++ 3 files changed, 34 insertions(+), 1 deletion(-) create mode 100644 tests/bfs/nocolor_env_empty.out create mode 100644 tests/bfs/nocolor_env_empty.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 3f32021..09cfdd3 100644 --- a/src/parse.c +++ b/src/parse.c @@ -3670,7 +3670,8 @@ struct bfs_ctx *bfs_parse_cmdline(int argc, char *argv[]) { } enum use_color use_color = COLOR_AUTO; - if (getenv("NO_COLOR")) { + const char *no_color = getenv("NO_COLOR"); + if (no_color && *no_color) { // https://no-color.org/ use_color = COLOR_NEVER; } diff --git a/tests/bfs/nocolor_env_empty.out b/tests/bfs/nocolor_env_empty.out new file mode 100644 index 0000000..a439814 --- /dev/null +++ b/tests/bfs/nocolor_env_empty.out @@ -0,0 +1,27 @@ +$'rainbow/\e[1m' +$'rainbow/\e[1m/'$'\e[0m' +rainbow +rainbow/exec.sh +rainbow/socket +rainbow/broken +rainbow/chardev_link +rainbow/link.txt +rainbow/sticky_ow +rainbow/sgid +rainbow/pipe +rainbow/ow +rainbow/sugid +rainbow/suid +rainbow/sticky +rainbow/file.dat +rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/mh1 +rainbow/mh2 +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/bfs/nocolor_env_empty.sh b/tests/bfs/nocolor_env_empty.sh new file mode 100644 index 0000000..0ffc046 --- /dev/null +++ b/tests/bfs/nocolor_env_empty.sh @@ -0,0 +1,5 @@ +command -v unbuffer &>/dev/null || skip + +NO_COLOR= bfs_pty rainbow >"$OUT" +sort_output +diff_output -- cgit v1.2.3 From ce90dc9bc00c46b27b437467bb2f053ab2307fbc Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 7 Nov 2023 13:09:21 -0500 Subject: tests: Fix output interleaving with -j1 --- tests/run.sh | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index f93cb4b..8f0a8fc 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -124,17 +124,17 @@ run_tests() { set +e for TEST in "${TEST_CASES[@]}"; do + if ((BG >= JOBS)); then + wait_test + fi + printf "$TEST_FMT" "$TEST" mkdir -p "$TMP/$TEST" OUT="$TMP/$TEST.out" - if ((BG >= JOBS)); then - wait_test - fi - ((++BG)) - bg_test "$TESTS/$TEST.sh" & + ((++BG)) done while ((BG > 0)); do -- cgit v1.2.3 From 163baf1c9af13be0ce705b133e41e0c3d6427398 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 7 Nov 2023 13:10:44 -0500 Subject: parse: Reject -{exec,ok}dir if $PATH contains a relative path This matches the behaviour of GNU find. --- src/parse.c | 26 ++++++++++++++++++++++++++ tests/gnu/execdir_path_dot.sh | 1 + tests/gnu/execdir_path_empty.sh | 1 + tests/gnu/execdir_path_relative.sh | 1 + tests/gnu/okdir_path_dot.sh | 1 + tests/gnu/okdir_path_empty.sh | 1 + tests/gnu/okdir_path_relative.sh | 1 + 7 files changed, 32 insertions(+) create mode 100644 tests/gnu/execdir_path_dot.sh create mode 100644 tests/gnu/execdir_path_empty.sh create mode 100644 tests/gnu/execdir_path_relative.sh create mode 100644 tests/gnu/okdir_path_dot.sh create mode 100644 tests/gnu/okdir_path_empty.sh create mode 100644 tests/gnu/okdir_path_relative.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 09cfdd3..d21ab40 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1263,6 +1263,28 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar expr->ephemeral_fds = 2; if (execbuf->flags & BFS_EXEC_CHDIR) { + // Check for relative paths in $PATH + const char *path = getenv("PATH"); + while (path) { + if (*path != '/') { + size_t len = strcspn(path, ":"); + char *comp = strndup(path, len); + if (comp) { + parse_expr_error(state, expr, + "This action would be unsafe, since ${bld}$$PATH${rs} contains the relative path ${bld}%pq${rs}\n", comp); + free(comp); + } else { + parse_perror(state, "strndup()"); + } + goto fail; + } + + path = strchr(path, ':'); + if (path) { + ++path; + } + } + // To dup() the parent directory if (execbuf->flags & BFS_EXEC_MULTI) { ++expr->persistent_fds; @@ -1276,6 +1298,10 @@ static struct bfs_expr *parse_exec(struct parser_state *state, int flags, int ar } return expr; + +fail: + bfs_expr_free(expr); + return NULL; } /** diff --git a/tests/gnu/execdir_path_dot.sh b/tests/gnu/execdir_path_dot.sh new file mode 100644 index 0000000..632dbb4 --- /dev/null +++ b/tests/gnu/execdir_path_dot.sh @@ -0,0 +1 @@ +! PATH=".:$PATH" invoke_bfs basic -execdir echo {} + diff --git a/tests/gnu/execdir_path_empty.sh b/tests/gnu/execdir_path_empty.sh new file mode 100644 index 0000000..eda6b1c --- /dev/null +++ b/tests/gnu/execdir_path_empty.sh @@ -0,0 +1 @@ +! PATH=":$PATH" invoke_bfs basic -execdir echo {} + diff --git a/tests/gnu/execdir_path_relative.sh b/tests/gnu/execdir_path_relative.sh new file mode 100644 index 0000000..69899ad --- /dev/null +++ b/tests/gnu/execdir_path_relative.sh @@ -0,0 +1 @@ +! PATH="foo:$PATH" invoke_bfs basic -execdir echo {} + diff --git a/tests/gnu/okdir_path_dot.sh b/tests/gnu/okdir_path_dot.sh new file mode 100644 index 0000000..5b40e27 --- /dev/null +++ b/tests/gnu/okdir_path_dot.sh @@ -0,0 +1 @@ +! PATH=".:$PATH" invoke_bfs basic -okdir echo {} \; diff --git a/tests/gnu/okdir_path_empty.sh b/tests/gnu/okdir_path_empty.sh new file mode 100644 index 0000000..2669ee8 --- /dev/null +++ b/tests/gnu/okdir_path_empty.sh @@ -0,0 +1 @@ +! PATH=":$PATH" invoke_bfs basic -okdir echo {} \; diff --git a/tests/gnu/okdir_path_relative.sh b/tests/gnu/okdir_path_relative.sh new file mode 100644 index 0000000..05100a1 --- /dev/null +++ b/tests/gnu/okdir_path_relative.sh @@ -0,0 +1 @@ +! PATH="foo:$PATH" invoke_bfs basic -okdir echo {} \; -- cgit v1.2.3 From e25261a90222de75781726a93ab809c660208afd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 9 Nov 2023 15:16:04 -0500 Subject: config: Add (de)allocator attributes --- src/alloc.h | 40 +++++++++++++++++++++++----------------- src/config.h | 49 +++++++++++++++++++++++++++++++++++++++++++++++++ src/dstring.h | 21 +++++++++++++-------- tests/alloc.c | 4 ++++ 4 files changed, 89 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/src/alloc.h b/src/alloc.h index fd3e5f0..34f6949 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -9,7 +9,7 @@ #define BFS_ALLOC_H #include "config.h" -#include +#include /** Round down to a multiple of an alignment. */ static inline size_t align_floor(size_t align, size_t size) { @@ -108,6 +108,8 @@ static inline size_t flex_size(size_t align, size_t min, size_t offset, size_t s * @return * The allocated memory, or NULL on failure. */ +attr_malloc(free, 1) +attr_aligned_alloc(1, 2) void *alloc(size_t align, size_t size); /** @@ -120,6 +122,8 @@ void *alloc(size_t align, size_t size); * @return * The allocated memory, or NULL on failure. */ +attr_malloc(free, 1) +attr_aligned_alloc(1, 2) void *zalloc(size_t align, size_t size); /** Allocate memory for the given type. */ @@ -176,14 +180,15 @@ void arena_init(struct arena *arena, size_t align, size_t size); arena_init((arena), alignof(type), sizeof(type)) /** - * Allocate an object out of the arena. + * Free an object from the arena. */ -void *arena_alloc(struct arena *arena); +void arena_free(struct arena *arena, void *ptr); /** - * Free an object from the arena. + * Allocate an object out of the arena. */ -void arena_free(struct arena *arena, void *ptr); +attr_malloc(arena_free, 2) +void *arena_alloc(struct arena *arena); /** * Free all allocations from an arena. @@ -242,6 +247,18 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, #define VARENA_INIT(varena, type, member) \ varena_init(varena, alignof(type), sizeof(type), offsetof(type, member), sizeof_member(type, member[0])) +/** + * Free an arena-allocated flexible struct. + * + * @param varena + * The that allocated the object. + * @param ptr + * The object to free. + * @param count + * The length of the flexible array. + */ +void varena_free(struct varena *varena, void *ptr, size_t count); + /** * Arena-allocate a flexible struct. * @@ -252,6 +269,7 @@ void varena_init(struct varena *varena, size_t align, size_t min, size_t offset, * @return * The allocated struct, or NULL on failure. */ +attr_malloc(varena_free, 2) void *varena_alloc(struct varena *varena, size_t count); /** @@ -284,18 +302,6 @@ void *varena_realloc(struct varena *varena, void *ptr, size_t old_count, size_t */ void *varena_grow(struct varena *varena, void *ptr, size_t *count); -/** - * Free an arena-allocated flexible struct. - * - * @param varena - * The that allocated the object. - * @param ptr - * The object to free. - * @param count - * The length of the flexible array. - */ -void varena_free(struct varena *varena, void *ptr, size_t count); - /** * Free all allocations from a varena. */ diff --git a/src/config.h b/src/config.h index b95abaa..db62ef8 100644 --- a/src/config.h +++ b/src/config.h @@ -184,6 +184,17 @@ # define fallthru ((void)0) #endif +/** + * Warn if a value is unused. + */ +#if __has_c_attribute(nodiscard) +# define attr_nodiscard [[nodiscard]] +#elif __has_attribute(nodiscard) +# define attr_nodiscard __attribute__((nodiscard)) +#else +# define attr_nodiscard +#endif + /** * Hint to avoid inlining a function. */ @@ -211,6 +222,44 @@ # define attr_format(fmt, args) #endif +/** + * Annotates allocator-like functions. + */ +#if __has_attribute(malloc) +# if __clang__ +# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) +# else +# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) +# endif +#else +# define attr_malloc(...) attr_nodiscard +#endif + +/** + * Specifies that a function returns allocations with a given alignment. + */ +#if __has_attribute(alloc_align) +# define attr_alloc_align(param) __attribute__((alloc_align(param))) +#else +# define attr_alloc_align(param) +#endif + +/** + * Specifies that a function returns allocations with a given size. + */ +#if __has_attribute(alloc_size) +# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else +# define attr_alloc_size(...) +#endif + +/** + * Shorthand for attr_alloc_align() and attr_alloc_size(). + */ +#define attr_aligned_alloc(align, ...) \ + attr_alloc_align(align) \ + attr_alloc_size(__VA_ARGS__) + /** * Check if function multiversioning via GNU indirect functions (ifunc) is supported. */ diff --git a/src/dstring.h b/src/dstring.h index fd98df8..07b4ee9 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -27,12 +27,21 @@ typedef __attribute__((aligned(alignof(size_t)))) char dchar; typedef char dchar; #endif +/** + * Free a dynamic string. + * + * @param dstr + * The string to free. + */ +void dstrfree(dchar *dstr); + /** * Allocate a dynamic string. * * @param capacity * The initial capacity of the string. */ +attr_malloc(dstrfree, 1) dchar *dstralloc(size_t capacity); /** @@ -41,6 +50,7 @@ dchar *dstralloc(size_t capacity); * @param str * The NUL-terminated string to copy. */ +attr_malloc(dstrfree, 1) dchar *dstrdup(const char *str); /** @@ -51,6 +61,7 @@ dchar *dstrdup(const char *str); * @param n * The maximum number of characters to copy from str. */ +attr_malloc(dstrfree, 1) dchar *dstrndup(const char *str, size_t n); /** @@ -59,6 +70,7 @@ dchar *dstrndup(const char *str, size_t n); * @param dstr * The dynamic string to copy. */ +attr_malloc(dstrfree, 1) dchar *dstrddup(const dchar *dstr); /** @@ -69,6 +81,7 @@ dchar *dstrddup(const dchar *dstr); * @param len * The length of the string, which may include internal NUL bytes. */ +attr_malloc(dstrfree, 1) dchar *dstrxdup(const char *str, size_t len); /** @@ -306,12 +319,4 @@ int dstrescat(dchar **dest, const char *str, enum wesc_flags flags); */ int dstrnescat(dchar **dest, const char *str, size_t n, enum wesc_flags flags); -/** - * Free a dynamic string. - * - * @param dstr - * The string to free. - */ -void dstrfree(dchar *dstr); - #endif // BFS_DSTRING_H diff --git a/tests/alloc.c b/tests/alloc.c index 2334241..37b70bf 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -24,6 +24,10 @@ int main(void) { bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); // Make sure we detect allocation size overflows +#if __GNUC__ && !__clang__ +# pragma GCC diagnostic ignored "-Walloc-size-larger-than=" +#endif + bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); -- cgit v1.2.3 From 208376ef99da243545efcd6fb02d3469b4c068ed Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 10 Nov 2023 12:28:00 -0500 Subject: bit: Implement a branchless has_single_bit() --- src/bit.h | 3 ++- tests/bit.c | 3 +++ 2 files changed, 5 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/bit.h b/src/bit.h index 21a8076..e680fed 100644 --- a/src/bit.h +++ b/src/bit.h @@ -350,7 +350,8 @@ UINT_OVERLOADS(FIRST_TRAILING_ONE) #define HAS_SINGLE_BIT(type, suffix, width) \ static inline bool has_single_bit##suffix(type n) { \ - return n && !(n & (n - 1)); \ + /** Branchless n && !(n & (n - 1)) */ \ + return n < (n ^ (n - 1)) + 1; \ } UINT_OVERLOADS(ROTATE_LEFT) diff --git a/tests/bit.c b/tests/bit.c index cb339f4..7b20770 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -117,5 +117,8 @@ int main(void) { verify_eq(bit_floor(0), 0); verify_eq(bit_ceil(0), 1); + bfs_verify(!has_single_bit(0)); + bfs_verify(!has_single_bit(UINT32_MAX)); + return EXIT_SUCCESS; } -- cgit v1.2.3 From 56e35928daaec12c97ccc4707546f669db1d909d Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 11 Nov 2023 13:46:21 -0500 Subject: Revert "bit: Implement a branchless has_single_bit()" Doesn't work for the highest bit due to overflow. This reverts commit 208376ef99da243545efcd6fb02d3469b4c068ed. --- src/bit.h | 3 +-- tests/bit.c | 1 + 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/src/bit.h b/src/bit.h index e680fed..21a8076 100644 --- a/src/bit.h +++ b/src/bit.h @@ -350,8 +350,7 @@ UINT_OVERLOADS(FIRST_TRAILING_ONE) #define HAS_SINGLE_BIT(type, suffix, width) \ static inline bool has_single_bit##suffix(type n) { \ - /** Branchless n && !(n & (n - 1)) */ \ - return n < (n ^ (n - 1)) + 1; \ + return n && !(n & (n - 1)); \ } UINT_OVERLOADS(ROTATE_LEFT) diff --git a/tests/bit.c b/tests/bit.c index 7b20770..f9071be 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -119,6 +119,7 @@ int main(void) { bfs_verify(!has_single_bit(0)); bfs_verify(!has_single_bit(UINT32_MAX)); + bfs_verify(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); return EXIT_SUCCESS; } -- cgit v1.2.3 From 0733f4338ff409727f0a032b0f01268db45ebaf3 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 13 Nov 2023 11:18:39 -0500 Subject: tests: Fix --verbose=commands with --bfs="wrapper bfs" --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index 8f0a8fc..c7e819a 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -241,7 +241,7 @@ bfs_verbose() { bfs_verbose_impl() { printf "${GRN}%q${RST}" "${BFS[0]}" if ((${#BFS[@]} > 1)); then - printf " ${GRN}%q${RST}" "${BFS[1:]}" + printf " ${GRN}%q${RST}" "${BFS[@]:1}" fi local expr_started=0 color -- cgit v1.2.3 From fb4760db85c552b0f538f7af2ad40fa631c20540 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 15 Nov 2023 09:45:49 -0500 Subject: tests: Fix a possible infinite loop The POSIX spec for readdir() [1] says: > If a file is removed from or added to the directory after the most > recent call to opendir() or rewinddir(), whether a subsequent call > to readdir() returns an entry for that file is unspecified. which implies that a loop of readdir()/unlink()/creat() may continue to return new files unendingly. This was even observed on a Linux 6.6 tmpfs mount [2]. It's not clear whether find(1) is also permitted to loop endlessly in this case, but in case it is, let's avoid the whole problem by limiting the -exec to happen at most once. [1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/readdir.html [2]: https://lore.kernel.org/linux-fsdevel/20231113180616.2831430-1-tavianator@tavianator.com/ --- tests/gnu/ignore_readdir_race_notdir.sh | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/gnu/ignore_readdir_race_notdir.sh b/tests/gnu/ignore_readdir_race_notdir.sh index 8b03164..12e9fe6 100644 --- a/tests/gnu/ignore_readdir_race_notdir.sh +++ b/tests/gnu/ignore_readdir_race_notdir.sh @@ -1,5 +1,7 @@ # Check -ignore_readdir_race handling when a directory is replaced with a file cd "$TEST" -"$XTOUCH" -p foo/bar +mkdir foo -invoke_bfs . -mindepth 1 -ignore_readdir_race -execdir rm -r {} \; -execdir "$XTOUCH" {} \; +invoke_bfs . -mindepth 1 -ignore_readdir_race \ + -type d -execdir rmdir {} \; \ + -execdir "$XTOUCH" {} \; -- cgit v1.2.3 From dcf18f988e7737a5bbb77618da99cc5dc7035539 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 Nov 2023 11:51:14 -0500 Subject: tests: Fix --stop --- tests/run.sh | 6 ++++++ 1 file changed, 6 insertions(+) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index c7e819a..785c414 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -80,12 +80,15 @@ wait_test() { case $ret in 0) ((++passed)) + return 0 ;; $EX_SKIP) ((++skipped)) + return 0 ;; *) ((++failed)) + return $ret ;; esac } @@ -126,6 +129,9 @@ run_tests() { for TEST in "${TEST_CASES[@]}"; do if ((BG >= JOBS)); then wait_test + if (($? && STOP)); then + break + fi fi printf "$TEST_FMT" "$TEST" -- cgit v1.2.3 From 3114d6c94332cf4b7f2c0c87a425e513c006dd14 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 23 Nov 2023 13:55:10 -0500 Subject: tests: Set abort_on_error for the sanitizers --- tests/util.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/util.sh b/tests/util.sh index bfa5d16..5bd3328 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -27,7 +27,7 @@ stdenv() { export LC_ALL=C export TZ=UTC0 - local SAN_OPTIONS="halt_on_error=1:log_to_syslog=0" + local SAN_OPTIONS="abort_on_error=1:halt_on_error=1:log_to_syslog=0" export ASAN_OPTIONS="$SAN_OPTIONS" export LSAN_OPTIONS="$SAN_OPTIONS" export MSAN_OPTIONS="$SAN_OPTIONS" -- cgit v1.2.3 From e0d7dc5dfd7bdaa62b6bc18e9c1cce00bbe08577 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 9 Dec 2023 17:02:57 -0500 Subject: parse: Reject integers that start with whitespace --- src/parse.c | 6 +++++- tests/bfs/links_leading_space.sh | 1 + 2 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/bfs/links_leading_space.sh (limited to 'tests') diff --git a/src/parse.c b/src/parse.c index 13e65bc..778fc68 100644 --- a/src/parse.c +++ b/src/parse.c @@ -565,13 +565,17 @@ enum int_flags { * Parse an integer. */ static const char *parse_int(const struct parser_state *state, char **arg, const char *str, void *result, enum int_flags flags) { - char *endptr; + // strtoll() skips leading spaces, but we want to reject them + if (xisspace(str[0])) { + goto bad; + } int base = flags & IF_BASE_MASK; if (base == 0) { base = 10; } + char *endptr; errno = 0; long long value = strtoll(str, &endptr, base); if (errno != 0) { diff --git a/tests/bfs/links_leading_space.sh b/tests/bfs/links_leading_space.sh new file mode 100644 index 0000000..15957af --- /dev/null +++ b/tests/bfs/links_leading_space.sh @@ -0,0 +1 @@ +! invoke_bfs links -links ' 1' -- cgit v1.2.3 From 9cf8ea46ecc274351db419c2ab8fdf68c2d223d8 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 1 Jan 2024 23:46:11 -0500 Subject: tests: Remove unneeded command -v unbuffer || skip --- tests/bfs/nocolor_env.sh | 2 -- tests/bfs/nocolor_env_empty.sh | 2 -- 2 files changed, 4 deletions(-) (limited to 'tests') diff --git a/tests/bfs/nocolor_env.sh b/tests/bfs/nocolor_env.sh index 0a17fb2..d1c2afb 100644 --- a/tests/bfs/nocolor_env.sh +++ b/tests/bfs/nocolor_env.sh @@ -1,5 +1,3 @@ -command -v unbuffer &>/dev/null || skip - NO_COLOR=1 bfs_pty rainbow >"$OUT" sort_output diff_output diff --git a/tests/bfs/nocolor_env_empty.sh b/tests/bfs/nocolor_env_empty.sh index 0ffc046..1edfb1d 100644 --- a/tests/bfs/nocolor_env_empty.sh +++ b/tests/bfs/nocolor_env_empty.sh @@ -1,5 +1,3 @@ -command -v unbuffer &>/dev/null || skip - NO_COLOR= bfs_pty rainbow >"$OUT" sort_output diff_output -- cgit v1.2.3 From 7c232609183f41745d5a451fac34b41f59741e99 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 3 Jan 2024 11:58:45 -0500 Subject: tests/bsd/type_w: Check for truncate(1) before using it --- tests/bsd/type_w.sh | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/bsd/type_w.sh b/tests/bsd/type_w.sh index aec9cb1..3aa50d5 100644 --- a/tests/bsd/type_w.sh +++ b/tests/bsd/type_w.sh @@ -5,7 +5,11 @@ command -v newfs &>/dev/null || skip cd "$TEST" # Create a ramdisk -truncate -s1M img +if command -v truncate &>/dev/null; then + truncate -s1M img +else + dd if=/dev/zero of=img bs=1k count=1k +fi md=$(bfs_sudo mdconfig img) || skip defer bfs_sudo mdconfig -du "$md" -- cgit v1.2.3 From b7aa47b983f2d80a7a80d7c026d648767e0f18e4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 3 Jan 2024 12:01:06 -0500 Subject: tests/posix/name_bracket: Skip on NetBSD too --- tests/posix/name_bracket.sh | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/posix/name_bracket.sh b/tests/posix/name_bracket.sh index 80ca186..e2f943d 100644 --- a/tests/posix/name_bracket.sh +++ b/tests/posix/name_bracket.sh @@ -1,5 +1,9 @@ -# fnmatch() is broken on macOS -test "$UNAME" = "Darwin" && skip +# fnmatch() is broken on some platforms +case "$UNAME" in + Darwin|NetBSD) + skip + ;; +esac # An unclosed [ should be matched literally bfs_diff weirdnames -name '[' -- cgit v1.2.3 From 45fb1d952c3b262278a3b22e9c7d60cca19a5407 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 3 Jan 2024 17:17:35 -0500 Subject: Work around DragonFly BSD kernel bug DragonFly's x86_64 assembly implementation of copyinstr() checks the wrong pointer when deciding whether to return EFAULT or ENAMETOOLONG, causing it to always return EFAULT for overlong paths. Work around it by treating EFAULT the same as ENAMETOOLONG on DragonFly. Link: https://twitter.com/tavianator/status/1742991411203485713 --- src/bftw.c | 14 +++++++++++++- tests/xtouch.c | 6 ++++++ 2 files changed, 19 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/bftw.c b/src/bftw.c index 0b74cd9..355cb54 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -739,10 +739,22 @@ static int bftw_file_open(struct bftw_state *state, struct bftw_file *file, cons } int fd = bftw_file_openat(state, file, base, at_path); - if (fd >= 0 || errno != ENAMETOOLONG) { + if (fd >= 0) { return fd; } + switch (errno) { + case ENAMETOOLONG: +#if __DragonFly__ + // https://twitter.com/tavianator/status/1742991411203485713 + case EFAULT: +#endif + break; + + default: + return -1; + } + // Handle ENAMETOOLONG by manually traversing the path component-by-component struct bftw_list parents; SLIST_INIT(&parents); diff --git a/tests/xtouch.c b/tests/xtouch.c index 260a3a3..ed8bbee 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -70,13 +70,19 @@ static int open_parent(const struct args *args, const char **path) { switch (errno) { case ENAMETOOLONG: +#if __DragonFly__ + // https://twitter.com/tavianator/status/1742991411203485713 + case EFAULT: +#endif break; + case ENOENT: if (args->flags & CREATE_PARENTS) { break; } else { goto err; } + default: goto err; } -- cgit v1.2.3 From 4010140cb748cc4f7f57b0a3d514485796c665ce Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 7 Jan 2024 12:42:03 -0500 Subject: tests/bfs/D_opt: New test for more -D opt coverage --- tests/bfs/D_opt.out | 18 ++++++++++++++++++ tests/bfs/D_opt.sh | 1 + 2 files changed, 19 insertions(+) create mode 100644 tests/bfs/D_opt.out create mode 100644 tests/bfs/D_opt.sh (limited to 'tests') diff --git a/tests/bfs/D_opt.out b/tests/bfs/D_opt.out new file mode 100644 index 0000000..3b461cf --- /dev/null +++ b/tests/bfs/D_opt.out @@ -0,0 +1,18 @@ +basic/a +basic/b +basic/c +basic/c/d +basic/e +basic/e/f +basic/g +basic/g/h +basic/i +basic/j +basic/j/foo +basic/k +basic/k/foo +basic/k/foo/bar +basic/l +basic/l/foo +basic/l/foo/bar +basic/l/foo/bar/baz diff --git a/tests/bfs/D_opt.sh b/tests/bfs/D_opt.sh new file mode 100644 index 0000000..d95cf86 --- /dev/null +++ b/tests/bfs/D_opt.sh @@ -0,0 +1 @@ +bfs_diff -D opt -nohidden -not \( -type b -o -type c \) -links -5 -links -10 -not -hidden basic -- cgit v1.2.3 From ae94cdc00136685abe61d55e1e357caaa636d785 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 9 Jan 2024 15:04:52 -0500 Subject: tests: Don't do chmod +s POSIX says > When using the symbolic mode form on a regular file, it is > implementation-defined whether or not: > > - Requests to set the set-user-ID-on-execution or set-group-ID-on- > execution bit when all execute bits are currently clear and none > are being set are ignored. And indeed, illumos ignores them with a warning: chmod: WARNING: /tmp/bfs.XXXX7KaGWb/rainbow/suid: Execute permission required for set-ID on execution Link: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/chmod.html --- tests/stddirs.sh | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/stddirs.sh b/tests/stddirs.sh index a51c878..e08e6bf 100644 --- a/tests/stddirs.sh +++ b/tests/stddirs.sh @@ -106,8 +106,9 @@ make_rainbow() { ln -s nowhere "$1/broken" "$MKSOCK" "$1/socket" "$XTOUCH" -p "$1"/s{u,g,ug}id - chmod u+s "$1"/su{,g}id - chmod g+s "$1"/s{u,}gid + chmod 06644 "$1"/sugid + chmod 04644 "$1"/suid + chmod 02644 "$1"/sgid mkdir "$1/ow" "$1"/sticky{,_ow} chmod o+w "$1"/*ow chmod +t "$1"/sticky* -- cgit v1.2.3 From 67eebd6fd514ede9e10cd4639b8d0cce690513f4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 11 Jan 2024 13:01:14 -0500 Subject: tests: New output format --- tests/run.sh | 41 ++++++++++++++++++++++++++++------------- 1 file changed, 28 insertions(+), 13 deletions(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index 785c414..41df716 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -51,10 +51,24 @@ bg_test() { fi ret=$? - if ((ret != 0 && ret != EX_SKIP)); then - ((VERBOSE_ERRORS)) || cat "$TMP/$TEST.err" >&2 - color printf "${BOL}${RED}%s failed!${RST}\n" "$TEST" - fi + case $ret in + 0) + if ((VERBOSE_TESTS)); then + color printf "${BOL}${GRN}[PASS]${RST} ${BLD}%s${RST}\n" "$TEST" + fi + ;; + $EX_SKIP) + if ((VERBOSE_SKIPPED || VERBOSE_TESTS)); then + color printf "${BOL}${CYN}[SKIP]${RST} ${BLD}%s${RST}\n" "$TEST" + fi + ;; + *) + if ((!VERBOSE_ERRORS)); then + cat "$TMP/$TEST.err" >&2 + fi + color printf "${BOL}${RED}[FAIL]${RST} ${BLD}%s${RST}\n" "$TEST" + ;; + esac return $ret } @@ -114,9 +128,11 @@ run_tests() { passed=0 failed=0 skipped=0 + ran=0 + total=${#TEST_CASES[@]} if ((COLOR_STDOUT || VERBOSE_TESTS)); then - TEST_FMT="${BOL}${YLW}%s${RST}${EOL}" + TEST_FMT="${BOL}${YLW}[%3d%%]${RST} ${BLD}%s${RST}${EOL}" else TEST_FMT="." fi @@ -134,13 +150,14 @@ run_tests() { fi fi - printf "$TEST_FMT" "$TEST" + percent=$((100 * ran / total)) + printf "$TEST_FMT" $percent "$TEST" mkdir -p "$TMP/$TEST" OUT="$TMP/$TEST.out" bg_test "$TESTS/$TEST.sh" & - ((++BG)) + ((++BG, ++ran)) done while ((BG > 0)); do @@ -150,13 +167,13 @@ run_tests() { printf "${BOL}" if ((passed > 0)); then - color printf "${GRN}tests passed: %d${RST}\n" "$passed" + color printf "${GRN}[PASS]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $passed $total fi if ((skipped > 0)); then - color printf "${CYN}tests skipped: %s${RST}\n" "$skipped" + color printf "${CYN}[SKIP]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $skipped $total fi if ((failed > 0)); then - color printf "${RED}tests failed: %s${RST}\n" "$failed" + color printf "${RED}[FAIL]${RST} ${BLD}%3d${RST} / ${BLD}%d${RST}\n" $failed $total exit 1 fi } @@ -180,10 +197,8 @@ skip() { caller | { read -r line file printf "${BOL}" - debug "$file" $line "${CYN}$TEST skipped!${RST}" "$(awk "NR == $line" "$file")" >&3 + debug "$file" $line "" "$(awk "NR == $line" "$file")" >&3 } - elif ((VERBOSE_TESTS)); then - color printf "${BOL}${CYN}%s skipped!${RST}\n" "$TEST" fi exit $EX_SKIP -- cgit v1.2.3 From a9c9fc392bf2c3803251e5f63c944cd221a1ca67 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 12 Jan 2024 12:01:07 -0500 Subject: tests: Merge unit test executables into one --- GNUmakefile | 27 +++++++++------ tests/alloc.c | 6 ++-- tests/bfstd.c | 5 +-- tests/bit.c | 5 +-- tests/main.c | 106 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/tests.h | 26 ++++++++++++++ tests/trie.c | 5 +-- tests/xtime.c | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ tests/xtimegm.c | 94 ------------------------------------------------- 9 files changed, 256 insertions(+), 113 deletions(-) create mode 100644 tests/main.c create mode 100644 tests/tests.h create mode 100644 tests/xtime.c delete mode 100644 tests/xtimegm.c (limited to 'tests') diff --git a/GNUmakefile b/GNUmakefile index 66af797..34c7f37 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -269,32 +269,39 @@ LIBBFS := \ # The main executable $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) -# Standalone unit tests -UNITS := alloc bfstd bit trie xtimegm -UNIT_TESTS := $(UNITS:%=$(BIN)/tests/%) -UNIT_CHECKS := $(UNITS:%=check-%) - # Testing utilities TEST_UTILS := $(BIN)/tests/mksock $(BIN)/tests/xtouch -TESTS := $(UNIT_TESTS) $(TEST_UTILS) +$(BIN)/tests/mksock: $(OBJ)/tests/mksock.o $(LIBBFS) + +$(BIN)/tests/xtouch: $(OBJ)/tests/xtouch.o $(LIBBFS) + +# All test binaries +TESTS := $(BIN)/tests/units $(TEST_UTILS) + +$(BIN)/tests/units: \ + $(OBJ)/tests/alloc.o \ + $(OBJ)/tests/bfstd.o \ + $(OBJ)/tests/bit.o \ + $(OBJ)/tests/main.o \ + $(OBJ)/tests/trie.o \ + $(OBJ)/tests/xtime.o \ + $(LIBBFS) tests: $(TESTS) .PHONY: tests -$(TESTS): $(BIN)/tests/%: $(OBJ)/tests/%.o $(LIBBFS) - # The different search strategies that we test STRATEGIES := bfs dfs ids eds STRATEGY_CHECKS := $(STRATEGIES:%=check-%) # All the different checks we run -CHECKS := $(UNIT_CHECKS) $(STRATEGY_CHECKS) +CHECKS := check-units $(STRATEGY_CHECKS) check: $(CHECKS) .PHONY: check $(CHECKS) -$(UNIT_CHECKS): check-%: $(BIN)/tests/% +check-units: $(BIN)/tests/units $< JOBS := $(filter -j%,$(MAKEFLAGS)) diff --git a/tests/alloc.c b/tests/alloc.c index 37b70bf..e14f131 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -1,13 +1,14 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "tests.h" #include "../src/alloc.h" #include "../src/diag.h" #include #include #include -int main(void) { +bool check_alloc(void) { // Check sizeof_flex() struct flexible { alignas(64) int foo[8]; @@ -44,6 +45,5 @@ int main(void) { } varena_destroy(&varena); - - return EXIT_SUCCESS; + return true; } diff --git a/tests/bfstd.c b/tests/bfstd.c index c386279..d385c6b 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,6 +1,7 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "tests.h" #include "../src/bfstd.h" #include "../src/config.h" #include "../src/diag.h" @@ -34,7 +35,7 @@ static void check_wordesc(const char *str, const char *exp, enum wesc_flags flag bfs_verify(strcmp(buf, exp) == 0, "wordesc(%s) == %s (!= %s)", str, buf, exp); } -int main(void) { +bool check_bfstd(void) { // Try to set a UTF-8 locale if (!setlocale(LC_ALL, "C.UTF-8")) { setlocale(LC_ALL, ""); @@ -69,5 +70,5 @@ int main(void) { check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); } - return EXIT_SUCCESS; + return true; } diff --git a/tests/bit.c b/tests/bit.c index f9071be..1c6a4de 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -1,6 +1,7 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "tests.h" #include "../src/bit.h" #include "../src/diag.h" #include @@ -54,7 +55,7 @@ bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); #define verify_eq(a, b) \ bfs_verify((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) -int main(void) { +bool check_bit(void) { verify_eq(bswap((uint8_t)0x12), 0x12); verify_eq(bswap((uint16_t)0x1234), 0x3412); verify_eq(bswap((uint32_t)0x12345678), 0x78563412); @@ -121,5 +122,5 @@ int main(void) { bfs_verify(!has_single_bit(UINT32_MAX)); bfs_verify(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); - return EXIT_SUCCESS; + return true; } diff --git a/tests/main.c b/tests/main.c new file mode 100644 index 0000000..b7292b4 --- /dev/null +++ b/tests/main.c @@ -0,0 +1,106 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Entry point for unit tests. + */ + +#include "tests.h" +#include "../src/bfstd.h" +#include "../src/color.h" +#include "../src/config.h" +#include "../src/diag.h" +#include +#include +#include +#include + +/** + * Test context. + */ +struct test_ctx { + /** Number of command line arguments. */ + int argc; + /** The arguments themselves. */ + char **argv; + + /** Parsed colors. */ + struct colors *colors; + /** Colorized output stream. */ + CFILE *cout; + + /** Eventual exit status. */ + int ret; +}; + +/** Initialize the test context. */ +static int test_init(struct test_ctx *ctx, int argc, char **argv) { + ctx->argc = argc; + ctx->argv = argv; + + ctx->colors = parse_colors(); + ctx->cout = cfwrap(stdout, ctx->colors, false); + if (!ctx->cout) { + ctx->ret = EXIT_FAILURE; + return -1; + } + + ctx->ret = EXIT_SUCCESS; + return 0; +} + +/** Finalize the test context. */ +static int test_fini(struct test_ctx *ctx) { + if (ctx->cout) { + cfclose(ctx->cout); + } + + free_colors(ctx->colors); + + return ctx->ret; +} + +/** Check if a test case is enabled for this run. */ +static bool should_run(const struct test_ctx *ctx, const char *test) { + // Run all tests by default + if (ctx->argc < 2) { + return true; + } + + // With args, run only specified tests + for (int i = 1; i < ctx->argc; ++i) { + if (strcmp(test, ctx->argv[i]) == 0) { + return true; + } + } + + return false; +} + +/** Run a test if it's enabled. */ +static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) { + if (should_run(ctx, test)) { + if (fn()) { + cfprintf(ctx->cout, "${grn}[PASS]${rs} ${bld}%s${rs}\n", test); + } else { + cfprintf(ctx->cout, "${red}[FAIL]${rs} ${bld}%s${rs}\n", test); + ctx->ret = EXIT_FAILURE; + } + } +} + +int main(int argc, char *argv[]) { + struct test_ctx ctx; + if (test_init(&ctx, argc, argv) != 0) { + goto done; + } + + run_test(&ctx, "alloc", check_alloc); + run_test(&ctx, "bfstd", check_bfstd); + run_test(&ctx, "bit", check_bit); + run_test(&ctx, "trie", check_trie); + run_test(&ctx, "xtime", check_xtime); + +done: + return test_fini(&ctx); +} diff --git a/tests/tests.h b/tests/tests.h new file mode 100644 index 0000000..34c58b7 --- /dev/null +++ b/tests/tests.h @@ -0,0 +1,26 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Unit tests. + */ + +#include "../src/config.h" + +/** Unit test function type. */ +typedef bool test_fn(void); + +/** Memory allocation tests. */ +bool check_alloc(void); + +/** Standard library wrapper tests. */ +bool check_bfstd(void); + +/** Bit manipulation tests. */ +bool check_bit(void); + +/** Trie tests. */ +bool check_trie(void); + +/** Time tests. */ +bool check_xtime(void); diff --git a/tests/trie.c b/tests/trie.c index 656fd85..f94d7c8 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,6 +1,7 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "tests.h" #include "../src/trie.h" #include "../src/config.h" #include "../src/diag.h" @@ -38,7 +39,7 @@ const char *keys[] = { const size_t nkeys = countof(keys); -int main(void) { +bool check_trie(void) { struct trie trie; trie_init(&trie); @@ -130,5 +131,5 @@ int main(void) { free(longstr); trie_destroy(&trie); - return EXIT_SUCCESS; + return true; } diff --git a/tests/xtime.c b/tests/xtime.c new file mode 100644 index 0000000..53ecbc4 --- /dev/null +++ b/tests/xtime.c @@ -0,0 +1,95 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "tests.h" +#include "../src/xtime.h" +#include "../src/config.h" +#include +#include +#include +#include + +static bool tm_equal(const struct tm *tma, const struct tm *tmb) { + if (tma->tm_year != tmb->tm_year) { + return false; + } + if (tma->tm_mon != tmb->tm_mon) { + return false; + } + if (tma->tm_mday != tmb->tm_mday) { + return false; + } + if (tma->tm_hour != tmb->tm_hour) { + return false; + } + if (tma->tm_min != tmb->tm_min) { + return false; + } + if (tma->tm_sec != tmb->tm_sec) { + return false; + } + if (tma->tm_wday != tmb->tm_wday) { + return false; + } + if (tma->tm_yday != tmb->tm_yday) { + return false; + } + if (tma->tm_isdst != tmb->tm_isdst) { + return false; + } + + return true; +} + +static void tm_print(FILE *file, const struct tm *tm) { + fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", + tm->tm_year, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec, + tm->tm_wday, tm->tm_yday, + tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); +} + +bool check_xtime(void) { + if (setenv("TZ", "UTC0", true) != 0) { + perror("setenv()"); + return false; + } + + struct tm tm = { + .tm_isdst = -1, + }; + + for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) + for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) + for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) + for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) + for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) + for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { + struct tm tma = tm, tmb = tm; + time_t ta, tb; + ta = mktime(&tma); + if (xtimegm(&tmb, &tb) != 0) { + tb = -1; + } + + bool fail = false; + if (ta != tb) { + printf("Mismatch: %jd != %jd\n", (intmax_t)ta, (intmax_t)tb); + fail = true; + } + if (ta != -1 && !tm_equal(&tma, &tmb)) { + printf("mktime(): "); + tm_print(stdout, &tma); + printf("xtimegm(): "); + tm_print(stdout, &tmb); + fail = true; + } + if (fail) { + printf("Input: "); + tm_print(stdout, &tm); + return false; + } + } + + return true; +} diff --git a/tests/xtimegm.c b/tests/xtimegm.c deleted file mode 100644 index 973b2eb..0000000 --- a/tests/xtimegm.c +++ /dev/null @@ -1,94 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include "../src/xtime.h" -#include "../src/config.h" -#include -#include -#include -#include - -static bool tm_equal(const struct tm *tma, const struct tm *tmb) { - if (tma->tm_year != tmb->tm_year) { - return false; - } - if (tma->tm_mon != tmb->tm_mon) { - return false; - } - if (tma->tm_mday != tmb->tm_mday) { - return false; - } - if (tma->tm_hour != tmb->tm_hour) { - return false; - } - if (tma->tm_min != tmb->tm_min) { - return false; - } - if (tma->tm_sec != tmb->tm_sec) { - return false; - } - if (tma->tm_wday != tmb->tm_wday) { - return false; - } - if (tma->tm_yday != tmb->tm_yday) { - return false; - } - if (tma->tm_isdst != tmb->tm_isdst) { - return false; - } - - return true; -} - -static void tm_print(FILE *file, const struct tm *tm) { - fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, - tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); -} - -int main(void) { - if (setenv("TZ", "UTC0", true) != 0) { - perror("setenv()"); - return EXIT_FAILURE; - } - - struct tm tm = { - .tm_isdst = -1, - }; - - for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) - for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) - for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) - for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) - for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) - for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { - struct tm tma = tm, tmb = tm; - time_t ta, tb; - ta = mktime(&tma); - if (xtimegm(&tmb, &tb) != 0) { - tb = -1; - } - - bool fail = false; - if (ta != tb) { - printf("Mismatch: %jd != %jd\n", (intmax_t)ta, (intmax_t)tb); - fail = true; - } - if (ta != -1 && !tm_equal(&tma, &tmb)) { - printf("mktime(): "); - tm_print(stdout, &tma); - printf("xtimegm(): "); - tm_print(stdout, &tmb); - fail = true; - } - if (fail) { - printf("Input: "); - tm_print(stdout, &tm); - return EXIT_FAILURE; - } - } - - return EXIT_SUCCESS; -} -- cgit v1.2.3 From d6cae04b54c1d58223e1719101b7c54d348e8d80 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 13 Jan 2024 11:44:09 -0500 Subject: tests.h: Add a header guard --- tests/tests.h | 5 +++++ 1 file changed, 5 insertions(+) (limited to 'tests') diff --git a/tests/tests.h b/tests/tests.h index 34c58b7..d2f3611 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -5,6 +5,9 @@ * Unit tests. */ +#ifndef BFS_TESTS_H +#define BFS_TESTS_H + #include "../src/config.h" /** Unit test function type. */ @@ -24,3 +27,5 @@ bool check_trie(void); /** Time tests. */ bool check_xtime(void); + +#endif // BFS_TESTS_H -- cgit v1.2.3 From e9588c49d5539ded993f720fc6855d6fa878c997 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 13 Jan 2024 12:42:42 -0500 Subject: bfstd: New {error,errno}_is_like() functions We used to have is_nonexistence_error() to consistently treat ENOENT and ENOTDIR the same. Recently, we started considering EFAULT the same as ENAMETOOLONG on DragonFly BSD to work around a kernel bug. Unify both of these behind a more generic interface. --- src/bfstd.c | 23 +++++++++++++++++++++-- src/bfstd.h | 18 ++++++++++++++++-- src/bftw.c | 18 +++--------------- src/eval.c | 2 +- src/stat.c | 2 +- tests/xtouch.c | 17 +++-------------- 6 files changed, 45 insertions(+), 35 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index c0e61cb..15e8667 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -36,8 +36,27 @@ # include #endif -bool is_nonexistence_error(int error) { - return error == ENOENT || errno == ENOTDIR; +bool error_is_like(int error, int category) { + if (error == category) { + return true; + } + + switch (category) { + case ENOENT: + return error == ENOTDIR; + +#if __DragonFly__ + // https://twitter.com/tavianator/status/1742991411203485713 + case ENAMETOOLONG: + return error == EFAULT; +#endif + } + + return false; +} + +bool errno_is_like(int category) { + return error_is_like(errno, category); } char *xdirname(const char *path) { diff --git a/src/bfstd.h b/src/bfstd.h index 0fcb892..8953b9b 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -45,9 +45,23 @@ // #include /** - * Return whether an error code is due to a path not existing. + * Check if an error code is "like" another one. For example, ENOTDIR is + * like ENOENT because they can both be triggered by non-existent paths. + * + * @param error + * The error code to check. + * @param category + * The category to test for. Known categories include ENOENT and + * ENAMETOOLONG. + * @return + * Whether the error belongs to the given category. + */ +bool error_is_like(int error, int category); + +/** + * Equivalent to error_is_like(errno, category). */ -bool is_nonexistence_error(int error); +bool errno_is_like(int category); #include diff --git a/src/bftw.c b/src/bftw.c index 355cb54..49f07df 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -68,7 +68,7 @@ const struct bfs_stat *bftw_stat(const struct BFTW *ftwbuf, enum bfs_stat_flags } } else { ret = bftw_stat_impl(mutbuf, &mutbuf->stat_cache, BFS_STAT_FOLLOW); - if (!ret && (flags & BFS_STAT_TRYFOLLOW) && is_nonexistence_error(errno)) { + if (!ret && (flags & BFS_STAT_TRYFOLLOW) && errno_is_like(ENOENT)) { ret = bftw_stat_impl(mutbuf, &mutbuf->lstat_cache, BFS_STAT_NOFOLLOW); } } @@ -81,7 +81,7 @@ const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat return ftwbuf->lstat_cache.buf; } else if (ftwbuf->stat_cache.buf) { return ftwbuf->stat_cache.buf; - } else if ((flags & BFS_STAT_TRYFOLLOW) && is_nonexistence_error(ftwbuf->stat_cache.error)) { + } else if ((flags & BFS_STAT_TRYFOLLOW) && error_is_like(ftwbuf->stat_cache.error, ENOENT)) { return ftwbuf->lstat_cache.buf; } else { return NULL; @@ -739,22 +739,10 @@ static int bftw_file_open(struct bftw_state *state, struct bftw_file *file, cons } int fd = bftw_file_openat(state, file, base, at_path); - if (fd >= 0) { + if (fd >= 0 || !errno_is_like(ENAMETOOLONG)) { return fd; } - switch (errno) { - case ENAMETOOLONG: -#if __DragonFly__ - // https://twitter.com/tavianator/status/1742991411203485713 - case EFAULT: -#endif - break; - - default: - return -1; - } - // Handle ENAMETOOLONG by manually traversing the path component-by-component struct bftw_list parents; SLIST_INIT(&parents); diff --git a/src/eval.c b/src/eval.c index 130b366..1a814b3 100644 --- a/src/eval.c +++ b/src/eval.c @@ -80,7 +80,7 @@ static void eval_error(struct bfs_eval *state, const char *format, ...) { */ static bool eval_should_ignore(const struct bfs_eval *state, int error) { return state->ctx->ignore_races - && is_nonexistence_error(error) + && error_is_like(error, ENOENT) && state->ftwbuf->depth > 0; } diff --git a/src/stat.c b/src/stat.c index d7387c6..91aa092 100644 --- a/src/stat.c +++ b/src/stat.c @@ -260,7 +260,7 @@ static int bfs_stat_tryfollow(int at_fd, const char *at_path, int at_flags, int if (ret != 0 && (bfs_flags & (BFS_STAT_NOFOLLOW | BFS_STAT_TRYFOLLOW)) == BFS_STAT_TRYFOLLOW - && is_nonexistence_error(errno)) + && errno_is_like(ENOENT)) { at_flags |= AT_SYMLINK_NOFOLLOW; ret = bfs_stat_explicit(at_fd, at_path, at_flags, x_flags, buf); diff --git a/tests/xtouch.c b/tests/xtouch.c index ed8bbee..6099128 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -68,22 +68,11 @@ static int open_parent(const struct args *args, const char **path) { goto done; } - switch (errno) { - case ENAMETOOLONG: -#if __DragonFly__ - // https://twitter.com/tavianator/status/1742991411203485713 - case EFAULT: -#endif - break; - - case ENOENT: - if (args->flags & CREATE_PARENTS) { - break; - } else { + if (errno == ENOENT) { + if (!(args->flags & CREATE_PARENTS)) { goto err; } - - default: + } else if (!errno_is_like(ENAMETOOLONG)) { goto err; } -- cgit v1.2.3 From ef03769f3f69f3d6d9d3427b1d84ec00a1c3cc36 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 13 Jan 2024 16:14:06 -0500 Subject: tests: Properly filter escape sequences with --verbose=tests --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index 41df716..4a159f9 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -151,7 +151,7 @@ run_tests() { fi percent=$((100 * ran / total)) - printf "$TEST_FMT" $percent "$TEST" + color printf "$TEST_FMT" $percent "$TEST" mkdir -p "$TMP/$TEST" OUT="$TMP/$TEST.out" -- cgit v1.2.3 From 40e195c1bd60737964bb2b8ab25780a02003f8e1 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 17 Jan 2024 13:58:39 -0500 Subject: tests/posix: Avoid catastrophic backtracking Using -path 'deep/*/*/.../*' to simulate -mindepth 18 falls off a performance cliff on systems that use backtracking for fnmatch(). This was observed on macOS 12.4. Instead, just use -type f. --- tests/posix/nogroup_ulimit.sh | 7 +------ tests/posix/nouser_ulimit.sh | 6 +----- 2 files changed, 2 insertions(+), 11 deletions(-) (limited to 'tests') diff --git a/tests/posix/nogroup_ulimit.sh b/tests/posix/nogroup_ulimit.sh index 2186321..5b4c440 100644 --- a/tests/posix/nogroup_ulimit.sh +++ b/tests/posix/nogroup_ulimit.sh @@ -1,7 +1,2 @@ ulimit -n 16 - -# -mindepth 18, but POSIX -path="*/*/*/*/*/*" -path="$path/$path/$path" -bfs_diff deep -path "deep/$path" -nogroup - +bfs_diff deep -type f -nogroup diff --git a/tests/posix/nouser_ulimit.sh b/tests/posix/nouser_ulimit.sh index be0a65f..e1400f0 100644 --- a/tests/posix/nouser_ulimit.sh +++ b/tests/posix/nouser_ulimit.sh @@ -1,6 +1,2 @@ ulimit -n 16 - -# -mindepth 18, but POSIX -path="*/*/*/*/*/*" -path="$path/$path/$path" -bfs_diff deep -path "deep/$path" -nouser +bfs_diff deep -type f -nouser -- cgit v1.2.3 From 670ebd97fb431e830b1500b2e7e8013b121fb2c5 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 31 Jan 2024 20:58:43 -0500 Subject: bftw: Actually stop if the callback returns BFTW_STOP Otherwise, bftw_ids() or bftw_eds() might keep going! Fixes: 5f16169 ("bftw: Share the bftw_state between iterations of ids/eds") --- src/bftw.c | 2 +- tests/bsd/s_quit.out | 1 + tests/bsd/s_quit.sh | 4 ++++ 3 files changed, 6 insertions(+), 1 deletion(-) create mode 100644 tests/bsd/s_quit.out create mode 100644 tests/bsd/s_quit.sh (limited to 'tests') diff --git a/src/bftw.c b/src/bftw.c index ab930f1..c9ba67e 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -1791,7 +1791,7 @@ static int bftw_impl(struct bftw_state *state) { break; } if (bftw_visit(state, NULL) != 0) { - break; + return -1; } } diff --git a/tests/bsd/s_quit.out b/tests/bsd/s_quit.out new file mode 100644 index 0000000..5ea492b --- /dev/null +++ b/tests/bsd/s_quit.out @@ -0,0 +1 @@ +basic/j/foo diff --git a/tests/bsd/s_quit.sh b/tests/bsd/s_quit.sh new file mode 100644 index 0000000..6bd55ab --- /dev/null +++ b/tests/bsd/s_quit.sh @@ -0,0 +1,4 @@ +# Regression test: bfs -S ids -s -name foo -quit would not actually quit, +# ending up in a confused state and erroring/crashing + +bfs_diff -s basic -name foo -print -quit -- cgit v1.2.3 From f8b22f20147d872aef9fa1037139c39e4dd0e687 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 1 Feb 2024 16:03:05 -0500 Subject: tests: Use variable redirections to dup std{out,err} Previously, we hardcoded file descriptors 3 and 4 for duplicating stdandard output/error respectively. In preparation for keeping inherited FDs open, switch to using bash's variable redirection feature to dynamically assign FDs. This feature is only available from bash 4.1 onwards, so this marks the end of our support for bash 3. macOS users will need to install a modern bash version to run our tests. --- .github/workflows/ci.yml | 4 +++- tests/run.sh | 26 +++++++------------------- tests/util.sh | 17 ++++++++--------- 3 files changed, 18 insertions(+), 29 deletions(-) (limited to 'tests') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 72037e3..ff0ff6f 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -52,7 +52,9 @@ jobs: - name: Install dependencies run: | - brew install expect + brew install \ + bash \ + expect - name: Run tests run: | diff --git a/tests/run.sh b/tests/run.sh index 4a159f9..85d961f 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -28,7 +28,7 @@ debug_err() { callers | while read -r line func file; do if [ "$func" = source ]; then local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : - debug "$file" $line "${RED}error $ret${RST}" "$cmd" >&4 + debug "$file" $line "${RED}error $ret${RST}" "$cmd" >&$DUPERR break fi done @@ -73,21 +73,9 @@ bg_test() { return $ret } -# Wait for any background job to complete -if ((BASH_VERSINFO[0] > 4 || (BASH_VERSINFO[0] == 4 && BASH_VERSINFO[1] >= 3))); then - wait_any() { - wait -n - } -else - wait_any() { - read -ra jobs < <(jobs -p) - wait ${jobs[0]} - } -fi - # Wait for a background test to finish wait_test() { - wait_any + wait -n ret=$? ((BG--)) @@ -197,7 +185,7 @@ skip() { caller | { read -r line file printf "${BOL}" - debug "$file" $line "" "$(awk "NR == $line" "$file")" >&3 + debug "$file" $line "" "$(awk "NR == $line" "$file")" >&$DUPOUT } fi @@ -252,8 +240,8 @@ bfs_verbose() { ( # Close some fds to make room for the pipe, # even with extremely low ulimit -n - exec >&- 4>&- - exec >&3 3>&- + exec >&- {DUPERR}>&- + exec >&$DUPOUT {DUPOUT}>&- color bfs_verbose_impl "$@" ) fi @@ -306,7 +294,7 @@ invoke_bfs() { local ret=0 # Close the logging fds - "${BFS[@]}" "$@" 3>&- 4>&- || ret=$? + "${BFS[@]}" "$@" {DUPOUT}>&- {DUPERR}>&- || ret=$? # Allow bfs to fail, but not crash if ((ret > 125)); then @@ -391,7 +379,7 @@ diff_output() { if ((UPDATE)); then cp "$OUT" "$GOLD" else - $DIFF -u "$GOLD" "$OUT" >&4 + $DIFF -u "$GOLD" "$OUT" >&$DUPERR fi } diff --git a/tests/util.sh b/tests/util.sh index 5bd3328..686cc77 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -68,7 +68,7 @@ stdenv() { # Close stdin so bfs doesn't think we're interactive # dup() the standard fds for logging even when redirected - exec &1 4>&2 + exec &1 {DUPERR}>&2 } # Drop root priviliges or bail @@ -159,19 +159,18 @@ defer() { # Pop a single command from the defer stack and run it pop_defer() { - local i=$((${#DEFER_CMDS[@]} - 1)) - local cmd="${DEFER_CMDS[$i]}" - local file="${DEFER_FILES[$i]}" - local line="${DEFER_LINES[$i]}" - unset "DEFER_CMDS[$i]" - unset "DEFER_FILES[$i]" - unset "DEFER_LINES[$i]" + local cmd="${DEFER_CMDS[-1]}" + local file="${DEFER_FILES[-1]}" + local line="${DEFER_LINES[-1]}" + unset "DEFER_CMDS[-1]" + unset "DEFER_FILES[-1]" + unset "DEFER_LINES[-1]" local ret=0 eval "$cmd" || ret=$? if ((ret != 0)); then - debug "$file" $line "${RED}error $ret${RST}" "defer $cmd" >&4 + debug "$file" $line "${RED}error $ret${RST}" "defer $cmd" >&$DUPERR fi return $ret -- cgit v1.2.3 From c4334184502c25a41b5cab060681229b2b570c0a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 1 Feb 2024 16:14:19 -0500 Subject: tests: Don't clobber inherited FDs Rather than attempting to close any unexpected FDs, just count them and adjust our ulimit -n calls to account for them. --- .github/workflows/ci.yml | 1 + tests/bfs/deep_strict.sh | 2 +- tests/common/execdir_ulimit.sh | 2 +- tests/gnu/execdir_ulimit.out | 32 ++++++++++++++++---------------- tests/gnu/execdir_ulimit.sh | 2 +- tests/gnu/printf_u_g_ulimit.sh | 2 +- tests/posix/deep.sh | 2 +- tests/posix/exec_ulimit.out | 32 ++++++++++++++++---------------- tests/posix/exec_ulimit.sh | 2 +- tests/posix/nogroup_ulimit.sh | 2 +- tests/posix/nouser_ulimit.sh | 2 +- tests/util.sh | 17 +++++------------ 12 files changed, 46 insertions(+), 52 deletions(-) (limited to 'tests') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index ff0ff6f..8e96526 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -86,6 +86,7 @@ jobs: tcl-wrapper pw useradd -n action -m -G wheel -s /usr/local/bin/bash echo "%wheel ALL=(ALL) NOPASSWD: ALL" >>/usr/local/etc/sudoers + mount -t fdescfs none /dev/fd run: | chown -R action:action . diff --git a/tests/bfs/deep_strict.sh b/tests/bfs/deep_strict.sh index 50c8f05..22453c0 100644 --- a/tests/bfs/deep_strict.sh +++ b/tests/bfs/deep_strict.sh @@ -1,3 +1,3 @@ # Not even enough fds to keep the root open -ulimit -n 7 +ulimit -n $((NOPENFD + 4)) bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \; diff --git a/tests/common/execdir_ulimit.sh b/tests/common/execdir_ulimit.sh index 90c93c1..122c282 100644 --- a/tests/common/execdir_ulimit.sh +++ b/tests/common/execdir_ulimit.sh @@ -2,5 +2,5 @@ cd "$TEST" mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/n/o/p/q/r/s/t/u/v/w/x/y/z mkdir -p a/b/c/d/e/f/g/h/i/j/k/l/m/0/1/2/3/4/5/6/7/8/9/A/B/C -ulimit -n 13 +ulimit -n $((NOPENFD + 10)) bfs_diff . -execdir echo {} \; diff --git a/tests/gnu/execdir_ulimit.out b/tests/gnu/execdir_ulimit.out index 7931f9a..6749f7d 100644 --- a/tests/gnu/execdir_ulimit.out +++ b/tests/gnu/execdir_ulimit.out @@ -1,16 +1,16 @@ -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 ./0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/gnu/execdir_ulimit.sh b/tests/gnu/execdir_ulimit.sh index 3245dad..e14e716 100644 --- a/tests/gnu/execdir_ulimit.sh +++ b/tests/gnu/execdir_ulimit.sh @@ -1,2 +1,2 @@ -ulimit -Sn 16 +ulimit -Sn 64 bfs_diff deep -type f -execdir bash -c 'printf "%d %s\n" $(ulimit -Sn) "$1"' bash {} \; diff --git a/tests/gnu/printf_u_g_ulimit.sh b/tests/gnu/printf_u_g_ulimit.sh index 390ad48..c621b9b 100644 --- a/tests/gnu/printf_u_g_ulimit.sh +++ b/tests/gnu/printf_u_g_ulimit.sh @@ -1,2 +1,2 @@ -ulimit -n 16 +ulimit -n $((NOPENFD + 13)) [ "$(invoke_bfs deep -printf '%u %g\n' | uniq)" = "$(id -un) $(id -gn)" ] diff --git a/tests/posix/deep.sh b/tests/posix/deep.sh index 431705e..36a88c0 100644 --- a/tests/posix/deep.sh +++ b/tests/posix/deep.sh @@ -1,2 +1,2 @@ -ulimit -n 16 +ulimit -n $((NOPENFD + 13)) bfs_diff deep -type f -exec bash -c 'echo "${1:0:6}/.../${1##*/} (${#1})"' bash {} \; diff --git a/tests/posix/exec_ulimit.out b/tests/posix/exec_ulimit.out index 587ece2..144169e 100644 --- a/tests/posix/exec_ulimit.out +++ b/tests/posix/exec_ulimit.out @@ -1,16 +1,16 @@ -16 deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE -16 deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/0/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/1/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/2/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/3/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/4/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/5/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/6/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/7/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/8/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/9/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/A/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/B/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/C/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/D/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/E/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE +64 deep/F/.../0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDEF0123456789ABCDE diff --git a/tests/posix/exec_ulimit.sh b/tests/posix/exec_ulimit.sh index 8ac8ff1..655fbec 100644 --- a/tests/posix/exec_ulimit.sh +++ b/tests/posix/exec_ulimit.sh @@ -1,2 +1,2 @@ -ulimit -Sn 16 +ulimit -Sn 64 bfs_diff deep -type f -exec bash -c 'printf "%d %s\n" $(ulimit -Sn) "${1:0:6}/.../${1##*/}"' bash {} \; diff --git a/tests/posix/nogroup_ulimit.sh b/tests/posix/nogroup_ulimit.sh index 5b4c440..a39dd1f 100644 --- a/tests/posix/nogroup_ulimit.sh +++ b/tests/posix/nogroup_ulimit.sh @@ -1,2 +1,2 @@ -ulimit -n 16 +ulimit -n $((NOPENFD + 13)) bfs_diff deep -type f -nogroup diff --git a/tests/posix/nouser_ulimit.sh b/tests/posix/nouser_ulimit.sh index e1400f0..a94b8c5 100644 --- a/tests/posix/nouser_ulimit.sh +++ b/tests/posix/nouser_ulimit.sh @@ -1,2 +1,2 @@ -ulimit -n 16 +ulimit -n $((NOPENFD + 13)) bfs_diff deep -type f -nouser diff --git a/tests/util.sh b/tests/util.sh index 686cc77..ec24958 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -48,23 +48,16 @@ stdenv() { export MallocNanoZone=0 fi - # Close non-standard inherited fds + # Count the inherited FDs if [ -d /proc/self/fd ]; then local fds=/proc/self/fd else local fds=/dev/fd fi - - for fd in "$fds"/*; do - if [ ! -e "$fd" ]; then - continue - fi - - local fd="${fd##*/}" - if ((fd > 2)); then - eval "exec ${fd}<&-" - fi - done + # We use ls $fds on purpose, rather than e.g. ($fds/*), to avoid counting + # internal bash fds that are not exposed to spawned processes + NOPENFD=$(ls -1q "$fds/" 2>/dev/null | wc -l) + NOPENFD=$((NOPENFD > 3 ? NOPENFD - 1 : 3)) # Close stdin so bfs doesn't think we're interactive # dup() the standard fds for logging even when redirected -- cgit v1.2.3 From 4d0d84f935159c395ccf3b95d5727a3553003b68 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 1 Feb 2024 12:11:41 -0500 Subject: tests: Implement jobserver inheritance --- .github/workflows/ci.yml | 12 ++--- GNUmakefile | 7 +-- tests/getopts.sh | 22 ++++++--- tests/run.sh | 119 +++++++++++++++++++++++++++++++++++++---------- tests/tests.mk | 7 +++ tests/util.sh | 12 +++-- 6 files changed, 132 insertions(+), 47 deletions(-) create mode 100644 tests/tests.mk (limited to 'tests') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 8e96526..5ca0a75 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -59,7 +59,7 @@ jobs: - name: Run tests run: | jobs=$(sysctl -n hw.ncpu) - make -j$jobs distcheck JOBS=-j$jobs + make -j$jobs distcheck freebsd: name: FreeBSD @@ -90,7 +90,7 @@ jobs: run: | chown -R action:action . - sudo -u action gmake -j$(nproc) distcheck JOBS=-j$(nproc) + sudo -u action gmake -j$(nproc) distcheck openbsd: name: OpenBSD @@ -120,7 +120,7 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) - doas -u action gmake -j$jobs check JOBS=-j$jobs TEST_FLAGS="--sudo=doas --verbose=skipped" + doas -u action gmake -j$jobs check TEST_FLAGS="--sudo=doas --verbose=skipped" netbsd: name: NetBSD @@ -153,7 +153,7 @@ jobs: PATH="/sbin:/usr/sbin:$PATH" chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action gmake -j$jobs check CC=clang LDFLAGS="-rpath /usr/pkg/lib" JOBS=-j$jobs TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action gmake -j$jobs check CC=clang LDFLAGS="-rpath /usr/pkg/lib" TEST_FLAGS="--sudo --verbose=skipped" dragonflybsd: name: DragonFly BSD @@ -184,7 +184,7 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action gmake -j$jobs check JOBS=-j$jobs TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" omnios: name: OmniOS @@ -216,4 +216,4 @@ jobs: PATH="/usr/xpg4/bin:$PATH" chown -R action:staff . jobs=$(getconf NPROCESSORS_ONLN) - sudo -u action gmake -j$jobs check LDFLAGS="-Wl,-rpath,/opt/ooce/lib/amd64" JOBS=-j$jobs TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action gmake -j$jobs check LDFLAGS="-Wl,-rpath,/opt/ooce/lib/amd64" TEST_FLAGS="--sudo --verbose=skipped" diff --git a/GNUmakefile b/GNUmakefile index 34c7f37..e01e93a 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -304,13 +304,8 @@ check: $(CHECKS) check-units: $(BIN)/tests/units $< -JOBS := $(filter -j%,$(MAKEFLAGS)) -ifndef JOBS - JOBS := -j1 -endif - $(STRATEGY_CHECKS): check-%: $(BIN)/bfs $(TEST_UTILS) - ./tests/tests.sh $(JOBS) --bfs="$(BIN)/bfs -S $*" $(TEST_FLAGS) + +./tests/tests.sh --make="$(MAKE)" --bfs="$(BIN)/bfs -S $*" $(TEST_FLAGS) # Custom test flags for distcheck DISTCHECK_FLAGS := -s TEST_FLAGS="--sudo --verbose=skipped" diff --git a/tests/getopts.sh b/tests/getopts.sh index ac75140..5214e9f 100644 --- a/tests/getopts.sh +++ b/tests/getopts.sh @@ -10,6 +10,7 @@ if command -v nproc &>/dev/null; then else JOBS=1 fi +MAKE= PATTERNS=() SUDO=() STOP=0 @@ -24,15 +25,19 @@ VERBOSE_TESTS=0 usage() { local pad=$(printf "%*s" ${#0} "") color cat </dev/null)" || : - debug "$file" $line "${RED}error $ret${RST}" "$cmd" >&$DUPERR + debug "$file" $line "${RED}error $ret${RST}" >&$DUPERR break fi done } -# Run a single test -run_test() ( +# Source a test +source_test() ( set -eE trap debug_err ERR + + if ((${#MAKE[@]})); then + # Close the jobserver pipes + exec {READY_PIPE}<&- {DONE_PIPE}>&- + fi + cd "$TMP" source "$@" ) -# Run a test in the background -bg_test() { +# Run a test +run_test() { if ((VERBOSE_ERRORS)); then - run_test "$1" + source_test "$1" else - run_test "$1" 2>"$TMP/$TEST.err" + source_test "$1" 2>"$TMP/$TEST.err" fi ret=$? + if ((${#MAKE[@]})); then + # Write one byte to the done pipe + printf . >&$DONE_PIPE + fi + case $ret in 0) if ((VERBOSE_TESTS)); then @@ -73,28 +83,87 @@ bg_test() { return $ret } -# Wait for a background test to finish -wait_test() { - wait -n - ret=$? +# Count the tests running in the background +BG=0 + +# Run a test in the background +bg_test() { + run_test "$1" & + ((++BG)) +} + +# Reap a finished background test +reap_test() { ((BG--)) - case $ret in + case "$1" in 0) ((++passed)) - return 0 ;; $EX_SKIP) ((++skipped)) - return 0 ;; *) ((++failed)) - return $ret ;; esac } +# Wait for a background test to finish +wait_test() { + local pid + wait -n -ppid + ret=$? + if [ -z "${pid:-}" ]; then + debug "${BASH_SOURCE[0]}" $((LINENO - 3)) "${RED}error $ret${RST}" >&$DUPERR + exit 1 + fi + + reap_test $ret +} + +# Wait until we're ready to run another test +wait_ready() { + if ((${#MAKE[@]})); then + # We'd like to parse the output of jobs -n, but we can't run it in a + # subshell or we won't get the right output + jobs -n >"$TMP/jobs" + while read -r job status ret foo; do + case "$status" in + Done) + reap_test 0 + ;; + Exit) + reap_test $ret + ;; + esac + done <"$TMP/jobs" + + # Read one byte from the ready pipe + read -r -N1 -u$READY_PIPE + elif ((BG >= JOBS)); then + wait_test + fi +} + +# Run make as a co-process to use its job control +comake() { + coproc { + # We can't just use std{in,out}, due to + # https://www.gnu.org/software/make/manual/html_node/Parallel-Input.html + exec {DONE_PIPE}<&0 {READY_PIPE}>&1 + exec "${MAKE[@]}" -s \ + -f "$TESTS/tests.mk" \ + DONE=$DONE_PIPE \ + READY=$READY_PIPE \ + "${TEST_CASES[@]/#/tests/}" \ + /dev/null + } + + # coproc pipes aren't inherited by subshells, so dup them + exec {READY_PIPE}<&${COPROC[0]} {DONE_PIPE}>&${COPROC[1]} +} + # Run all the tests run_tests() { if ((VERBOSE_TESTS)); then @@ -125,17 +194,17 @@ run_tests() { TEST_FMT="." fi - BG=0 + if ((${#MAKE[@]})); then + comake + fi # Turn off set -e (but turn it back on in run_test) set +e for TEST in "${TEST_CASES[@]}"; do - if ((BG >= JOBS)); then - wait_test - if (($? && STOP)); then - break - fi + wait_ready + if (($? && STOP)); then + break fi percent=$((100 * ran / total)) @@ -144,8 +213,8 @@ run_tests() { mkdir -p "$TMP/$TEST" OUT="$TMP/$TEST.out" - bg_test "$TESTS/$TEST.sh" & - ((++BG, ++ran)) + bg_test "$TESTS/$TEST.sh" + ((++ran)) done while ((BG > 0)); do @@ -185,7 +254,7 @@ skip() { caller | { read -r line file printf "${BOL}" - debug "$file" $line "" "$(awk "NR == $line" "$file")" >&$DUPOUT + debug "$file" $line "" >&$DUPOUT } fi diff --git a/tests/tests.mk b/tests/tests.mk new file mode 100644 index 0000000..5bf4f6c --- /dev/null +++ b/tests/tests.mk @@ -0,0 +1,7 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# GNU makefile that exposes make's job control to tests.sh + +tests/%: + bash -c 'printf . >&$(READY) && read -r -N1 -u$(DONE)' diff --git a/tests/util.sh b/tests/util.sh index ec24958..7dba9fb 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -113,9 +113,13 @@ callers() { # Print a message including path, line number, and command debug() { - local file="${1/#*\/tests\//tests/}" - set -- "$file" "${@:2}" - color printf "${BLD}%s:%d:${RST} %s\n %s\n" "$@" + local file="$1" + local line="$2" + local msg="$3" + local cmd="$(awk "NR == $line" "$file" 2>/dev/null)" || : + file="${file/#*\/tests\//tests/}" + + color printf "${BLD}%s:%d:${RST} %s\n %s\n" "$file" "$line" "$msg" "$cmd" } ## Deferred cleanup @@ -163,7 +167,7 @@ pop_defer() { eval "$cmd" || ret=$? if ((ret != 0)); then - debug "$file" $line "${RED}error $ret${RST}" "defer $cmd" >&$DUPERR + debug "$file" $line "${RED}error $ret${RST}" >&$DUPERR fi return $ret -- cgit v1.2.3 From dab66643f54d0f79c212db9e68d8545b98a2d653 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 5 Feb 2024 13:52:44 -0500 Subject: tests: Fix --stop --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index 0b975b9..ccd2ac1 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -203,7 +203,7 @@ run_tests() { for TEST in "${TEST_CASES[@]}"; do wait_ready - if (($? && STOP)); then + if ((STOP && failed > 0)); then break fi -- cgit v1.2.3 From 89ecb2a08467cd8aa6ba70f8519df494652cac96 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 5 Feb 2024 14:02:55 -0500 Subject: bftw: stat() files asynchronously --- src/bftw.c | 677 +++++++++++++++++++++++++++++++++------------- src/bftw.h | 20 +- src/eval.c | 20 +- tests/bfs/execdir_plus.sh | 2 +- 4 files changed, 507 insertions(+), 212 deletions(-) (limited to 'tests') diff --git a/src/bftw.c b/src/bftw.c index d392aed..664651c 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -36,58 +36,130 @@ #include #include +/** Initialize a bftw_stat cache. */ +static void bftw_stat_init(struct bftw_stat *bufs, struct bfs_stat *stat_buf, struct bfs_stat *lstat_buf) { + bufs->stat_buf = stat_buf; + bufs->lstat_buf = lstat_buf; + bufs->stat_err = -1; + bufs->lstat_err = -1; +} + +/** Fill a bftw_stat cache from another one. */ +static void bftw_stat_fill(struct bftw_stat *dest, const struct bftw_stat *src) { + if (dest->stat_err < 0 && src->stat_err >= 0) { + dest->stat_buf = src->stat_buf; + dest->stat_err = src->stat_err; + } + + if (dest->lstat_err < 0 && src->lstat_err >= 0) { + dest->lstat_buf = src->lstat_buf; + dest->lstat_err = src->lstat_err; + } +} + +/** Cache a bfs_stat() result. */ +static void bftw_stat_cache(struct bftw_stat *bufs, enum bfs_stat_flags flags, const struct bfs_stat *buf, int err) { + if (flags & BFS_STAT_NOFOLLOW) { + bufs->lstat_buf = buf; + bufs->lstat_err = err; + if (err || !S_ISLNK(buf->mode)) { + // Non-link, so share stat info + bufs->stat_buf = buf; + bufs->stat_err = err; + } + } else if (flags & BFS_STAT_TRYFOLLOW) { + if (err) { + bufs->stat_err = err; + } else if (S_ISLNK(buf->mode)) { + bufs->lstat_buf = buf; + bufs->lstat_err = err; + bufs->stat_err = ENOENT; + } else { + bufs->stat_buf = buf; + bufs->stat_err = err; + } + } else { + bufs->stat_buf = buf; + bufs->stat_err = err; + } +} + /** Caching bfs_stat(). */ -static const struct bfs_stat *bftw_stat_impl(struct BFTW *ftwbuf, struct bftw_stat *cache, enum bfs_stat_flags flags) { - if (!cache->buf) { - if (cache->error) { - errno = cache->error; - } else if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, flags, &cache->storage) == 0) { - cache->buf = &cache->storage; +static const struct bfs_stat *bftw_stat_impl(struct BFTW *ftwbuf, enum bfs_stat_flags flags) { + struct bftw_stat *bufs = &ftwbuf->stat_bufs; + struct bfs_stat *buf; + + if (flags & BFS_STAT_NOFOLLOW) { + buf = (struct bfs_stat *)bufs->lstat_buf; + if (bufs->lstat_err == 0) { + return buf; + } else if (bufs->lstat_err > 0) { + errno = bufs->lstat_err; + return NULL; + } + } else { + buf = (struct bfs_stat *)bufs->stat_buf; + if (bufs->stat_err == 0) { + return buf; + } else if (bufs->stat_err > 0) { + errno = bufs->stat_err; + return NULL; + } + } + + struct bfs_stat *ret; + int err; + if (bfs_stat(ftwbuf->at_fd, ftwbuf->at_path, flags, buf) == 0) { + ret = buf; + err = 0; #ifdef S_IFWHT - } else if (errno == ENOENT && ftwbuf->type == BFS_WHT) { - // This matches the behavior of FTS_WHITEOUT on BSD - memset(&cache->storage, 0, sizeof(cache->storage)); - cache->storage.mode = S_IFWHT; - cache->buf = &cache->storage; + } else if (errno == ENOENT && ftwbuf->type == BFS_WHT) { + // This matches the behavior of FTS_WHITEOUT on BSD + ret = memset(buf, 0, sizeof(*buf)); + ret->mode = S_IFWHT; + err = 0; #endif - } else { - cache->error = errno; - } + } else { + ret = NULL; + err = errno; } - return cache->buf; + bftw_stat_cache(bufs, flags, ret, err); + return ret; } const struct bfs_stat *bftw_stat(const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { struct BFTW *mutbuf = (struct BFTW *)ftwbuf; const struct bfs_stat *ret; - if (flags & BFS_STAT_NOFOLLOW) { - ret = bftw_stat_impl(mutbuf, &mutbuf->lstat_cache, BFS_STAT_NOFOLLOW); - if (ret && !S_ISLNK(ret->mode) && !mutbuf->stat_cache.buf) { - // Non-link, so share stat info - mutbuf->stat_cache.buf = ret; + if (flags & BFS_STAT_TRYFOLLOW) { + ret = bftw_stat_impl(mutbuf, BFS_STAT_FOLLOW); + if (!ret && errno_is_like(ENOENT)) { + ret = bftw_stat_impl(mutbuf, BFS_STAT_NOFOLLOW); } } else { - ret = bftw_stat_impl(mutbuf, &mutbuf->stat_cache, BFS_STAT_FOLLOW); - if (!ret && (flags & BFS_STAT_TRYFOLLOW) && errno_is_like(ENOENT)) { - ret = bftw_stat_impl(mutbuf, &mutbuf->lstat_cache, BFS_STAT_NOFOLLOW); - } + ret = bftw_stat_impl(mutbuf, flags); } return ret; } const struct bfs_stat *bftw_cached_stat(const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { + const struct bftw_stat *bufs = &ftwbuf->stat_bufs; + if (flags & BFS_STAT_NOFOLLOW) { - return ftwbuf->lstat_cache.buf; - } else if (ftwbuf->stat_cache.buf) { - return ftwbuf->stat_cache.buf; - } else if ((flags & BFS_STAT_TRYFOLLOW) && error_is_like(ftwbuf->stat_cache.error, ENOENT)) { - return ftwbuf->lstat_cache.buf; - } else { - return NULL; + if (bufs->lstat_err == 0) { + return bufs->lstat_buf; + } + } else if (bufs->stat_err == 0) { + return bufs->stat_buf; + } else if ((flags & BFS_STAT_TRYFOLLOW) && error_is_like(bufs->stat_err, ENOENT)) { + if (bufs->lstat_err == 0) { + return bufs->lstat_buf; + } } + + return NULL; } enum bfs_type bftw_type(const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { @@ -156,6 +228,9 @@ struct bftw_file { /** The inode number, for cycle detection. */ ino_t ino; + /** Cached bfs_stat() info. */ + struct bftw_stat stat_bufs; + /** The offset of this file in the full path. */ size_t nameoff; /** The length of the file's name. */ @@ -276,6 +351,10 @@ struct bftw_queue { struct bftw_list waiting; /** A list of already-serviced files. */ struct bftw_list ready; + /** The current size of the queue. */ + size_t size; + /** The number of files currently in-service. */ + size_t ioqueued; /** Tracks the imbalance between synchronous and async service. */ unsigned long imbalance; }; @@ -286,6 +365,8 @@ static void bftw_queue_init(struct bftw_queue *queue, enum bftw_qflags flags) { SLIST_INIT(&queue->buffer); SLIST_INIT(&queue->waiting); SLIST_INIT(&queue->ready); + queue->size = 0; + queue->ioqueued = 0; queue->imbalance = 0; } @@ -304,6 +385,8 @@ static void bftw_queue_push(struct bftw_queue *queue, struct bftw_file *file) { SLIST_APPEND(&queue->ready, file, ready); } } + + ++queue->size; } /** Add any buffered files to the queue. */ @@ -339,8 +422,19 @@ static bool bftw_queue_balanced(const struct bftw_queue *queue) { } } -/** Detatch the next waiting file to service it asynchronously. */ -static void bftw_queue_detach(struct bftw_queue *queue, struct bftw_file *file) { +/** Update the queue balance for (a)sync service. */ +static void bftw_queue_rebalance(struct bftw_queue *queue, bool async) { + if (async) { + --queue->imbalance; + } else { + ++queue->imbalance; + } +} + +/** Detatch the next waiting file. */ +static void bftw_queue_detach(struct bftw_queue *queue, struct bftw_file *file, bool async) { + bfs_assert(!file->ioqueued); + if (file == SLIST_HEAD(&queue->buffer)) { // To maintain order, we can't detach any files until they're // added to the waiting/ready lists @@ -352,19 +446,34 @@ static void bftw_queue_detach(struct bftw_queue *queue, struct bftw_file *file) bfs_bug("Detached file was not buffered or waiting"); } - file->ioqueued = true; - --queue->imbalance; + if (async) { + file->ioqueued = true; + ++queue->ioqueued; + bftw_queue_rebalance(queue, true); + } } /** Reattach a serviced file to the queue. */ -static void bftw_queue_attach(struct bftw_queue *queue, struct bftw_file *file) { - file->ioqueued = false; +static void bftw_queue_attach(struct bftw_queue *queue, struct bftw_file *file, bool async) { + if (async) { + bfs_assert(file->ioqueued); + file->ioqueued = false; + --queue->ioqueued; + } else { + bfs_assert(!file->ioqueued); + } if (!(queue->flags & BFTW_QORDER)) { SLIST_APPEND(&queue->ready, file, ready); } } +/** Make a file ready immediately. */ +static void bftw_queue_skip(struct bftw_queue *queue, struct bftw_file *file) { + bftw_queue_detach(queue, file, false); + bftw_queue_attach(queue, file, false); +} + /** Get the next waiting file. */ static struct bftw_file *bftw_queue_waiting(const struct bftw_queue *queue) { if (!(queue->flags & BFTW_QBUFFER)) { @@ -395,13 +504,6 @@ static struct bftw_file *bftw_queue_ready(const struct bftw_queue *queue) { return SLIST_HEAD(&queue->ready); } -/** Check if a queue is empty. */ -static bool bftw_queue_empty(const struct bftw_queue *queue) { - return SLIST_EMPTY(&queue->buffer) - && SLIST_EMPTY(&queue->waiting) - && SLIST_EMPTY(&queue->ready); -} - /** Pop a file from the queue. */ static struct bftw_file *bftw_queue_pop(struct bftw_queue *queue) { // Don't pop until we've had a chance to sort the buffer @@ -413,10 +515,10 @@ static struct bftw_file *bftw_queue_pop(struct bftw_queue *queue) { // If no files are ready, try the waiting list. Or, if // BFTW_QORDER is set, we may need to pop from both lists. file = SLIST_POP(&queue->waiting); - if (file) { - // This file will be serviced synchronously - ++queue->imbalance; - } + } + + if (file) { + --queue->size; } return file; @@ -444,6 +546,9 @@ struct bftw_cache { size_t dir_limit; /** Excess force-allocated bfs_dirs. */ size_t dir_excess; + + /** bfs_stat arena. */ + struct arena stat_bufs; }; /** Initialize a cache. */ @@ -462,6 +567,8 @@ static void bftw_cache_init(struct bftw_cache *cache, size_t capacity) { } cache->dir_excess = 0; + + ARENA_INIT(&cache->stat_bufs, struct bfs_stat); } /** Allocate a directory. */ @@ -603,8 +710,9 @@ static void bftw_cache_destroy(struct bftw_cache *cache) { bfs_assert(LIST_EMPTY(cache)); bfs_assert(!cache->target); - varena_destroy(&cache->files); + arena_destroy(&cache->stat_bufs); arena_destroy(&cache->dirs); + varena_destroy(&cache->files); } /** Create a new bftw_file. */ @@ -642,6 +750,8 @@ static struct bftw_file *bftw_file_new(struct bftw_cache *cache, struct bftw_fil file->dev = -1; file->ino = -1; + bftw_stat_init(&file->stat_bufs, NULL, NULL); + file->namelen = namelen; memcpy(file->name, name, namelen + 1); @@ -661,6 +771,21 @@ static void bftw_file_set_dir(struct bftw_cache *cache, struct bftw_file *file, } } +/** Free a file's cached stat() buffers. */ +static void bftw_stat_recycle(struct bftw_cache *cache, struct bftw_file *file) { + struct bftw_stat *bufs = &file->stat_bufs; + + struct bfs_stat *stat_buf = (struct bfs_stat *)bufs->stat_buf; + struct bfs_stat *lstat_buf = (struct bfs_stat *)bufs->lstat_buf; + if (stat_buf) { + arena_free(&cache->stat_bufs, stat_buf); + } else if (lstat_buf) { + arena_free(&cache->stat_bufs, lstat_buf); + } + + bftw_stat_init(bufs, NULL, NULL); +} + /** Free a bftw_file. */ static void bftw_file_free(struct bftw_cache *cache, struct bftw_file *file) { bfs_assert(file->refcount == 0); @@ -669,6 +794,8 @@ static void bftw_file_free(struct bftw_cache *cache, struct bftw_file *file) { bftw_file_close(cache, file); } + bftw_stat_recycle(cache, file); + varena_free(&cache->files, file, file->namelen + 1); } @@ -729,6 +856,10 @@ struct bftw_state { /** Extra data about the current file. */ struct BFTW ftwbuf; + /** stat() buffer storage. */ + struct bfs_stat stat_buf; + /** lstat() buffer storage. */ + struct bfs_stat lstat_buf; }; /** Check if we have to buffer files before visiting them. */ @@ -755,6 +886,11 @@ static bool bftw_must_buffer(const struct bftw_state *state) { return true; } + if ((state->flags & BFTW_STAT) && state->nthreads > 1) { + // We will be buffering every file anyway for ioq_stat() + return true; + } + return false; } @@ -841,19 +977,32 @@ static int bftw_state_init(struct bftw_state *state, const struct bftw_args *arg return 0; } -/** Unpin a directory, and possibly queue it for unwrapping. */ -static void bftw_unpin_dir(struct bftw_state *state, struct bftw_file *file, bool force) { - bftw_cache_unpin(&state->cache, file); +/** Queue a directory for unwrapping. */ +static void bftw_delayed_unwrap(struct bftw_state *state, struct bftw_file *file) { + bfs_assert(file->dir); - if (file->dir && (force || file->pincount == 0)) { - if (!SLIST_ATTACHED(&state->to_close, file)) { - SLIST_APPEND(&state->to_close, file); - } + if (!SLIST_ATTACHED(&state->to_close, file, ready)) { + SLIST_APPEND(&state->to_close, file, ready); + } +} + +/** Unpin a file's parent. */ +static void bftw_unpin_parent(struct bftw_state *state, struct bftw_file *file, bool unwrap) { + struct bftw_file *parent = file->parent; + if (!parent) { + return; + } + + bftw_cache_unpin(&state->cache, parent); + + if (unwrap && parent->dir && parent->pincount == 0) { + bftw_delayed_unwrap(state, parent); } } /** Pop a response from the I/O queue. */ static int bftw_ioq_pop(struct bftw_state *state, bool block) { + struct bftw_cache *cache = &state->cache; struct ioq *ioq = state->ioq; if (!ioq) { return -1; @@ -864,10 +1013,10 @@ static int bftw_ioq_pop(struct bftw_state *state, bool block) { return -1; } - struct bftw_cache *cache = &state->cache; - struct bftw_file *file; - struct bftw_file *parent; - struct bfs_dir *dir; + struct bftw_file *file = ent->ptr; + if (file) { + bftw_unpin_parent(state, file, true); + } enum ioq_op op = ent->op; switch (op) { @@ -877,30 +1026,30 @@ static int bftw_ioq_pop(struct bftw_state *state, bool block) { case IOQ_CLOSEDIR: ++cache->capacity; - dir = ent->closedir.dir; - bftw_freedir(cache, dir); + bftw_freedir(cache, ent->closedir.dir); break; case IOQ_OPENDIR: - file = ent->ptr; - ++cache->capacity; - parent = file->parent; - if (parent) { - bftw_unpin_dir(state, parent, false); - } - dir = ent->opendir.dir; if (ent->result >= 0) { - bftw_file_set_dir(cache, file, dir); + bftw_file_set_dir(cache, file, ent->opendir.dir); } else { - bftw_freedir(cache, dir); + bftw_freedir(cache, ent->opendir.dir); } - bftw_queue_attach(&state->dirq, file); + bftw_queue_attach(&state->dirq, file, true); break; case IOQ_STAT: + if (ent->result >= 0) { + bftw_stat_cache(&file->stat_bufs, ent->stat.flags, ent->stat.buf, 0); + } else { + arena_free(&cache->stat_bufs, ent->stat.buf); + bftw_stat_cache(&file->stat_bufs, ent->stat.flags, NULL, -ent->result); + } + + bftw_queue_attach(&state->fileq, file, true); break; } @@ -1125,21 +1274,34 @@ static int bftw_unwrapdir(struct bftw_state *state, struct bftw_file *file) { return bftw_ioq_closedir(state, dir); } +/** Try to pin a file's parent. */ +static int bftw_pin_parent(struct bftw_state *state, struct bftw_file *file) { + struct bftw_file *parent = file->parent; + if (!parent) { + return AT_FDCWD; + } + + int fd = parent->fd; + if (fd < 0) { + bfs_static_assert(AT_FDCWD != -1); + return -1; + } + + bftw_cache_pin(&state->cache, parent); + return fd; +} + /** Open a directory asynchronously. */ static int bftw_ioq_opendir(struct bftw_state *state, struct bftw_file *file) { + struct bftw_cache *cache = &state->cache; + if (bftw_ioq_reserve(state) != 0) { goto fail; } - int dfd = AT_FDCWD; - struct bftw_cache *cache = &state->cache; - struct bftw_file *parent = file->parent; - if (parent) { - dfd = parent->fd; - if (dfd < 0) { - goto fail; - } - bftw_cache_pin(cache, parent); + int dfd = bftw_pin_parent(state, file); + if (dfd < 0 && dfd != AT_FDCWD) { + goto fail; } if (bftw_cache_reserve(state) != 0) { @@ -1161,9 +1323,7 @@ static int bftw_ioq_opendir(struct bftw_state *state, struct bftw_file *file) { free: bftw_freedir(cache, dir); unpin: - if (parent) { - bftw_cache_unpin(cache, parent); - } + bftw_unpin_parent(state, file, false); fail: return -1; } @@ -1177,7 +1337,7 @@ static void bftw_ioq_opendirs(struct bftw_state *state) { } if (bftw_ioq_opendir(state, dir) == 0) { - bftw_queue_detach(&state->dirq, dir); + bftw_queue_detach(&state->dirq, dir, true); } else { break; } @@ -1191,24 +1351,36 @@ static void bftw_push_dir(struct bftw_state *state, struct bftw_file *file) { bftw_ioq_opendirs(state); } -/** Check if we should block on the ioq when popping a directory. */ -static bool bftw_block_for_dir(const struct bftw_state *state) { - // Always block with more than one background thread - if (state->nthreads > 1) { - return true; +/** Pop a file from a queue, then activate it. */ +static bool bftw_pop(struct bftw_state *state, struct bftw_queue *queue) { + if (queue->size == 0) { + return false; } - // Block if the cache is full - if (state->cache.capacity == 0) { - return true; + while (!bftw_queue_ready(queue) && queue->ioqueued > 0) { + bool block = true; + if (bftw_queue_waiting(queue) && state->nthreads == 1) { + // With only one background thread, balance the work + // between it and the main thread + block = false; + } + + if (bftw_ioq_pop(state, block) < 0) { + break; + } } - // Block if we have no other files/dirs to visit - if (!bftw_queue_waiting(&state->dirq) && bftw_queue_empty(&state->fileq)) { - return true; + struct bftw_file *file = bftw_queue_pop(queue); + if (!file) { + return false; } - return false; + while (file->ioqueued) { + bftw_ioq_pop(state, true); + } + + state->file = file; + return true; } /** Pop a directory to read from the queue. */ @@ -1220,32 +1392,143 @@ static bool bftw_pop_dir(struct bftw_state *state) { if (state->strategy == BFTW_BFS && bftw_queue_ready(&state->fileq)) { return false; } + } else if (!bftw_queue_ready(&state->dirq)) { + // Don't block if we have files ready to visit + if (bftw_queue_ready(&state->fileq)) { + return false; + } + } + + return bftw_pop(state, &state->dirq); +} + +/** Figure out bfs_stat() flags. */ +static enum bfs_stat_flags bftw_stat_flags(const struct bftw_state *state, size_t depth) { + enum bftw_flags mask = BFTW_FOLLOW_ALL; + if (depth == 0) { + mask |= BFTW_FOLLOW_ROOTS; + } + + if (state->flags & mask) { + return BFS_STAT_TRYFOLLOW; } else { - while (!bftw_queue_ready(&state->dirq)) { - if (bftw_ioq_pop(state, bftw_block_for_dir(state)) < 0) { - break; - } + return BFS_STAT_NOFOLLOW; + } +} + +/** Check if a stat() call is necessary. */ +static bool bftw_must_stat(const struct bftw_state *state, size_t depth, enum bfs_type type, const char *name) { + if (state->flags & BFTW_STAT) { + return true; + } + + switch (type) { + case BFS_UNKNOWN: + return true; + + case BFS_DIR: + return state->flags & (BFTW_DETECT_CYCLES | BFTW_SKIP_MOUNTS | BFTW_PRUNE_MOUNTS); + + case BFS_LNK: + if (!(bftw_stat_flags(state, depth) & BFS_STAT_NOFOLLOW)) { + return true; + } + fallthru; + + default: +#if __linux__ + if (state->mtab && bfs_might_be_mount(state->mtab, name)) { + return true; } +#endif + return false; } +} - struct bftw_file *file = bftw_queue_pop(&state->dirq); - if (!file) { +/** stat() a file asynchronously. */ +static int bftw_ioq_stat(struct bftw_state *state, struct bftw_file *file) { + if (bftw_ioq_reserve(state) != 0) { + goto fail; + } + + int dfd = bftw_pin_parent(state, file); + if (dfd < 0 && dfd != AT_FDCWD) { + goto fail; + } + + struct bftw_cache *cache = &state->cache; + struct bfs_stat *buf = arena_alloc(&cache->stat_bufs); + if (!buf) { + goto unpin; + } + + enum bfs_stat_flags flags = bftw_stat_flags(state, file->depth); + if (ioq_stat(state->ioq, dfd, file->name, flags, buf, file) != 0) { + goto free; + } + + return 0; + +free: + arena_free(&cache->stat_bufs, buf); +unpin: + bftw_unpin_parent(state, file, false); +fail: + return -1; +} + +/** Check if we should stat() a file asynchronously. */ +static bool bftw_should_ioq_stat(struct bftw_state *state, struct bftw_file *file) { + // To avoid surprising users too much, process the roots in order + if (file->depth == 0) { return false; } - while (file->ioqueued) { - bftw_ioq_pop(state, true); +#ifdef S_IFWHT + // ioq_stat() does not do whiteout emulation like bftw_stat_impl() + if (file->type == BFS_WHT) { + return false; } +#endif - state->file = file; - return true; + return bftw_must_stat(state, file->depth, file->type, file->name); +} + +/** Call stat() on files that need it. */ +static void bftw_stat_files(struct bftw_state *state) { + while (true) { + struct bftw_file *file = bftw_queue_waiting(&state->fileq); + if (!file) { + break; + } + + if (!bftw_should_ioq_stat(state, file)) { + bftw_queue_skip(&state->fileq, file); + continue; + } + + if (!bftw_queue_balanced(&state->fileq)) { + break; + } + + if (bftw_ioq_stat(state, file) == 0) { + bftw_queue_detach(&state->fileq, file, true); + } else { + break; + } + } +} + +/** Push a file onto the queue. */ +static void bftw_push_file(struct bftw_state *state, struct bftw_file *file) { + bftw_queue_push(&state->fileq, file); + bftw_stat_files(state); } /** Pop a file to visit from the queue. */ static bool bftw_pop_file(struct bftw_state *state) { bfs_assert(!state->file); - state->file = bftw_queue_pop(&state->fileq); - return state->file; + return bftw_pop(state, &state->fileq); } /** Build the path to the current file. */ @@ -1334,6 +1617,8 @@ static int bftw_opendir(struct bftw_state *state) { return -1; } + bftw_queue_rebalance(&state->dirq, false); + state->dir = bftw_file_opendir(state, file, state->path); if (!state->dir) { state->direrror = errno; @@ -1364,47 +1649,6 @@ static int bftw_readdir(struct bftw_state *state) { return ret; } -/** Check if a stat() call is needed for this visit. */ -static bool bftw_need_stat(const struct bftw_state *state) { - if (state->flags & BFTW_STAT) { - return true; - } - - const struct BFTW *ftwbuf = &state->ftwbuf; - if (ftwbuf->type == BFS_UNKNOWN) { - return true; - } - - if (ftwbuf->type == BFS_LNK && !(ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) { - return true; - } - - if (ftwbuf->type == BFS_DIR) { - if (state->flags & (BFTW_DETECT_CYCLES | BFTW_SKIP_MOUNTS | BFTW_PRUNE_MOUNTS)) { - return true; - } -#if __linux__ - } else if (state->mtab) { - // Linux fills in d_type from the underlying inode, even when - // the directory entry is a bind mount point. In that case, we - // need to stat() to get the correct type. We don't need to - // check for directories because they can only be mounted over - // by other directories. - if (bfs_might_be_mount(state->mtab, ftwbuf->path + ftwbuf->nameoff)) { - return true; - } -#endif - } - - return false; -} - -/** Initialize bftw_stat cache. */ -static void bftw_stat_init(struct bftw_stat *cache) { - cache->buf = NULL; - cache->error = 0; -} - /** Open a file if necessary. */ static int bftw_ensure_open(struct bftw_state *state, struct bftw_file *file, const char *path) { int ret = file->fd; @@ -1436,9 +1680,7 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) { ftwbuf->error = state->direrror; ftwbuf->at_fd = AT_FDCWD; ftwbuf->at_path = ftwbuf->path; - ftwbuf->stat_flags = BFS_STAT_NOFOLLOW; - bftw_stat_init(&ftwbuf->lstat_cache); - bftw_stat_init(&ftwbuf->stat_cache); + bftw_stat_init(&ftwbuf->stat_bufs, &state->stat_buf, &state->lstat_buf); struct bftw_file *parent = NULL; if (de) { @@ -1451,6 +1693,7 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) { ftwbuf->depth = file->depth; ftwbuf->type = file->type; ftwbuf->nameoff = file->nameoff; + bftw_stat_fill(&ftwbuf->stat_bufs, &file->stat_bufs); } if (parent) { @@ -1468,22 +1711,15 @@ static void bftw_init_ftwbuf(struct bftw_state *state, enum bftw_visit visit) { ftwbuf->nameoff = xbaseoff(ftwbuf->path); } + ftwbuf->stat_flags = bftw_stat_flags(state, ftwbuf->depth); + if (ftwbuf->error != 0) { ftwbuf->type = BFS_ERROR; return; } - int follow_flags = BFTW_FOLLOW_ALL; - if (ftwbuf->depth == 0) { - follow_flags |= BFTW_FOLLOW_ROOTS; - } - bool follow = state->flags & follow_flags; - if (follow) { - ftwbuf->stat_flags = BFS_STAT_TRYFOLLOW; - } - const struct bfs_stat *statbuf = NULL; - if (bftw_need_stat(state)) { + if (bftw_must_stat(state, ftwbuf->depth, ftwbuf->type, ftwbuf->path + ftwbuf->nameoff)) { statbuf = bftw_stat(ftwbuf, ftwbuf->stat_flags); if (statbuf) { ftwbuf->type = bfs_mode_to_type(statbuf->mode); @@ -1522,6 +1758,11 @@ static bool bftw_is_mount(struct bftw_state *state, const char *name) { return statbuf && statbuf->dev != parent->dev; } +/** Check if bfs_stat() was called from the main thread. */ +static bool bftw_stat_was_sync(const struct bftw_state *state, const struct bfs_stat *buf) { + return buf == &state->stat_buf || buf == &state->lstat_buf; +} + /** Invoke the callback. */ static enum bftw_action bftw_call_back(struct bftw_state *state, const char *name, enum bftw_visit visit) { if (visit == BFTW_POST && !(state->flags & BFTW_POST_ORDER)) { @@ -1541,31 +1782,43 @@ static enum bftw_action bftw_call_back(struct bftw_state *state, const char *nam return BFTW_STOP; } + enum bftw_action ret = BFTW_PRUNE; if ((state->flags & BFTW_SKIP_MOUNTS) && bftw_is_mount(state, name)) { - return BFTW_PRUNE; + goto done; } - enum bftw_action ret = state->callback(ftwbuf, state->ptr); + ret = state->callback(ftwbuf, state->ptr); switch (ret) { case BFTW_CONTINUE: - if (visit != BFTW_PRE) { - return BFTW_PRUNE; - } - if (ftwbuf->type != BFS_DIR) { - return BFTW_PRUNE; - } - if ((state->flags & BFTW_PRUNE_MOUNTS) && bftw_is_mount(state, name)) { - return BFTW_PRUNE; + if (visit != BFTW_PRE || ftwbuf->type != BFS_DIR) { + ret = BFTW_PRUNE; + } else if (state->flags & BFTW_PRUNE_MOUNTS) { + if (bftw_is_mount(state, name)) { + ret = BFTW_PRUNE; + } } - fallthru; + break; + case BFTW_PRUNE: case BFTW_STOP: - return ret; + break; default: state->error = EINVAL; return BFTW_STOP; } + +done: + if (state->fileq.flags & BFTW_QBALANCE) { + // Detect any main-thread stat() calls to rebalance the queue + const struct bfs_stat *buf = bftw_cached_stat(ftwbuf, BFS_STAT_FOLLOW); + const struct bfs_stat *lbuf = bftw_cached_stat(ftwbuf, BFS_STAT_NOFOLLOW); + if (bftw_stat_was_sync(state, buf) || bftw_stat_was_sync(state, lbuf)) { + bftw_queue_rebalance(&state->fileq, false); + } + } + + return ret; } /** @@ -1589,8 +1842,13 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { int ret = 0; struct bftw_file *file = state->file; - if (file && state->dir) { - bftw_unpin_dir(state, file, true); + if (file) { + if (state->dir) { + bftw_cache_unpin(&state->cache, file); + } + if (file->dir) { + bftw_delayed_unwrap(state, file); + } } state->dir = NULL; state->de = NULL; @@ -1607,7 +1865,7 @@ static int bftw_gc(struct bftw_state *state, enum bftw_gc_flags flags) { } state->direrror = 0; - while ((file = SLIST_POP(&state->to_close))) { + while ((file = SLIST_POP(&state->to_close, ready))) { bftw_unwrapdir(state, file); } @@ -1685,6 +1943,7 @@ static void bftw_flush(struct bftw_state *state) { bftw_list_sort(&state->fileq.buffer); } bftw_queue_flush(&state->fileq); + bftw_stat_files(state); bftw_queue_flush(&state->dirq); bftw_ioq_opendirs(state); @@ -1704,22 +1963,46 @@ static int bftw_closedir(struct bftw_state *state) { static void bftw_save_ftwbuf(struct bftw_file *file, const struct BFTW *ftwbuf) { file->type = ftwbuf->type; - const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf; - if (!statbuf || (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW)) { - statbuf = ftwbuf->lstat_cache.buf; - } + const struct bfs_stat *statbuf = bftw_cached_stat(ftwbuf, ftwbuf->stat_flags); if (statbuf) { file->dev = statbuf->dev; file->ino = statbuf->ino; } } +/** Check if we should buffer a file instead of visiting it. */ +static bool bftw_buffer_file(const struct bftw_state *state, const struct bftw_file *file, const char *name) { + if (!name) { + // Already buffered + return false; + } + + if (state->flags & BFTW_BUFFER) { + return true; + } + + // If we need to call stat(), and can do it async, buffer this file + if (!state->ioq) { + return false; + } + + if (!bftw_queue_balanced(&state->fileq)) { + // stat() would run synchronously anyway + return false; + } + + size_t depth = file ? file->depth + 1 : 1; + enum bfs_type type = state->de ? state->de->type : BFS_UNKNOWN; + return bftw_must_stat(state, depth, type, name); +} + /** Visit and/or enqueue the current file. */ static int bftw_visit(struct bftw_state *state, const char *name) { + struct bftw_cache *cache = &state->cache; struct bftw_file *file = state->file; - if (name && (state->flags & BFTW_BUFFER)) { - file = bftw_file_new(&state->cache, file, name); + if (bftw_buffer_file(state, file, name)) { + file = bftw_file_new(cache, file, name); if (!file) { state->error = errno; return -1; @@ -1729,14 +2012,14 @@ static int bftw_visit(struct bftw_state *state, const char *name) { file->type = state->de->type; } - bftw_queue_push(&state->fileq, file); + bftw_push_file(state, file); return 0; } switch (bftw_call_back(state, name, BFTW_PRE)) { case BFTW_CONTINUE: if (name) { - file = bftw_file_new(&state->cache, state->file, name); + file = bftw_file_new(cache, state->file, name); } else { state->file = NULL; } @@ -1746,6 +2029,7 @@ static int bftw_visit(struct bftw_state *state, const char *name) { } bftw_save_ftwbuf(file, &state->ftwbuf); + bftw_stat_recycle(cache, file); bftw_push_dir(state, file); return 0; @@ -1757,10 +2041,22 @@ static int bftw_visit(struct bftw_state *state, const char *name) { } default: + if (file && !name) { + bftw_gc(state, BFTW_VISIT_NONE); + } return -1; } } +/** Drain a bftw_queue. */ +static void bftw_drain(struct bftw_state *state, struct bftw_queue *queue) { + bftw_queue_flush(queue); + + while (bftw_pop(state, queue)) { + bftw_gc(state, BFTW_VISIT_NONE); + } +} + /** * Dispose of the bftw() state. * @@ -1777,11 +2073,9 @@ static int bftw_state_destroy(struct bftw_state *state) { state->ioq = NULL; } - bftw_queue_flush(&state->dirq); - bftw_queue_flush(&state->fileq); - do { - bftw_gc(state, BFTW_VISIT_NONE); - } while (bftw_pop_dir(state) || bftw_pop_file(state)); + bftw_gc(state, BFTW_VISIT_NONE); + bftw_drain(state, &state->dirq); + bftw_drain(state, &state->fileq); ioq_destroy(ioq); @@ -1823,6 +2117,7 @@ static int bftw_impl(struct bftw_state *state) { if (bftw_visit(state, NULL) != 0) { return -1; } + bftw_flush(state); } return 0; diff --git a/src/bftw.h b/src/bftw.h index e325d14..2805361 100644 --- a/src/bftw.h +++ b/src/bftw.h @@ -26,12 +26,14 @@ enum bftw_visit { * Cached bfs_stat() info for a file. */ struct bftw_stat { - /** A pointer to the bfs_stat() buffer, if available. */ - const struct bfs_stat *buf; - /** Storage for the bfs_stat() buffer, if needed. */ - struct bfs_stat storage; - /** The cached error code, if any. */ - int error; + /** The bfs_stat(BFS_STAT_FOLLOW) buffer. */ + const struct bfs_stat *stat_buf; + /** The bfs_stat(BFS_STAT_NOFOLLOW) buffer. */ + const struct bfs_stat *lstat_buf; + /** The cached bfs_stat(BFS_STAT_FOLLOW) error. */ + int stat_err; + /** The cached bfs_stat(BFS_STAT_NOFOLLOW) error. */ + int lstat_err; }; /** @@ -62,10 +64,8 @@ struct BFTW { /** Flags for bfs_stat(). */ enum bfs_stat_flags stat_flags; - /** Cached bfs_stat() info for BFS_STAT_NOFOLLOW. */ - struct bftw_stat lstat_cache; - /** Cached bfs_stat() info for BFS_STAT_FOLLOW. */ - struct bftw_stat stat_cache; + /** Cached bfs_stat() info. */ + struct bftw_stat stat_bufs; }; /** diff --git a/src/eval.c b/src/eval.c index 6dc73d8..dfeaa1e 100644 --- a/src/eval.c +++ b/src/eval.c @@ -1234,7 +1234,7 @@ static bool eval_file_unique(struct bfs_eval *state, struct trie *seen) { /** * Log a stat() call. */ -static void debug_stat(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf, const struct bftw_stat *cache, enum bfs_stat_flags flags) { +static void debug_stat(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf, enum bfs_stat_flags flags, int err) { bfs_debug_prefix(ctx, DEBUG_STAT); fprintf(stderr, "bfs_stat("); @@ -1254,10 +1254,10 @@ static void debug_stat(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf, con DEBUG_FLAG(flags, BFS_STAT_TRYFOLLOW); DEBUG_FLAG(flags, BFS_STAT_NOSYNC); - fprintf(stderr, ") == %d", cache->buf ? 0 : -1); + fprintf(stderr, ") == %d", err ? 0 : -1); - if (cache->error) { - fprintf(stderr, " [%d]", cache->error); + if (err) { + fprintf(stderr, " [%d]", err); } fprintf(stderr, "\n"); @@ -1271,14 +1271,14 @@ static void debug_stats(const struct bfs_ctx *ctx, const struct BFTW *ftwbuf) { return; } - const struct bfs_stat *statbuf = ftwbuf->stat_cache.buf; - if (statbuf || ftwbuf->stat_cache.error) { - debug_stat(ctx, ftwbuf, &ftwbuf->stat_cache, BFS_STAT_FOLLOW); + const struct bftw_stat *bufs = &ftwbuf->stat_bufs; + + if (bufs->stat_err >= 0) { + debug_stat(ctx, ftwbuf, BFS_STAT_FOLLOW, bufs->stat_err); } - const struct bfs_stat *lstatbuf = ftwbuf->lstat_cache.buf; - if ((lstatbuf && lstatbuf != statbuf) || ftwbuf->lstat_cache.error) { - debug_stat(ctx, ftwbuf, &ftwbuf->lstat_cache, BFS_STAT_NOFOLLOW); + if (bufs->lstat_err >= 0) { + debug_stat(ctx, ftwbuf, BFS_STAT_NOFOLLOW, bufs->lstat_err); } } diff --git a/tests/bfs/execdir_plus.sh b/tests/bfs/execdir_plus.sh index f66b898..6f24bdc 100644 --- a/tests/bfs/execdir_plus.sh +++ b/tests/bfs/execdir_plus.sh @@ -1,4 +1,4 @@ tree=$(invoke_bfs -D tree 2>&1 -quit) [[ "$tree" == *"-S dfs"* ]] && skip -bfs_diff basic -execdir "$TESTS/sort-args.sh" {} + +bfs_diff -j1 basic -execdir "$TESTS/sort-args.sh" {} + -- cgit v1.2.3 From ef28da1f876cb901f966dc01dd24ac861b3622ec Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 17 Feb 2024 09:33:06 -0500 Subject: tests: Redirect stdin in bfs_pty() Otherwise bfs will think it's interactive. --- tests/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index ccd2ac1..720515d 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -386,7 +386,7 @@ bfs_pty() { bfs_verbose "$@" local ret=0 - "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@"' bash "${BFS[@]}" "$@" || ret=$? + "$UNBUFFER" bash -c 'stty cols 80 rows 24 && "$@" 125)); then exit $ret -- cgit v1.2.3 From 39ce13d45c5881d84e2de98243052811e3648224 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 28 Feb 2024 22:10:06 -0500 Subject: tests/bfs/D_opt: Don't rely on directory link counts Fixes: https://github.com/tavianator/bfs/issues/131 --- tests/bfs/D_opt.out | 11 ----------- tests/bfs/D_opt.sh | 2 +- 2 files changed, 1 insertion(+), 12 deletions(-) (limited to 'tests') diff --git a/tests/bfs/D_opt.out b/tests/bfs/D_opt.out index 3b461cf..6218a0c 100644 --- a/tests/bfs/D_opt.out +++ b/tests/bfs/D_opt.out @@ -1,18 +1,7 @@ basic/a basic/b -basic/c basic/c/d -basic/e basic/e/f -basic/g -basic/g/h -basic/i -basic/j basic/j/foo -basic/k -basic/k/foo basic/k/foo/bar -basic/l -basic/l/foo -basic/l/foo/bar basic/l/foo/bar/baz diff --git a/tests/bfs/D_opt.sh b/tests/bfs/D_opt.sh index d95cf86..c14fe70 100644 --- a/tests/bfs/D_opt.sh +++ b/tests/bfs/D_opt.sh @@ -1 +1 @@ -bfs_diff -D opt -nohidden -not \( -type b -o -type c \) -links -5 -links -10 -not -hidden basic +bfs_diff -D opt -nohidden -not \( -type c -o -type d \) -links -5 -links -10 -not -hidden basic -- cgit v1.2.3 From f0418655db6a344afd5c26efd04a4e4d87128233 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 28 Feb 2024 12:31:11 -0500 Subject: tests/ioq: New unit test --- GNUmakefile | 1 + tests/ioq.c | 73 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/main.c | 1 + tests/tests.h | 3 +++ 4 files changed, 78 insertions(+) create mode 100644 tests/ioq.c (limited to 'tests') diff --git a/GNUmakefile b/GNUmakefile index b0314d8..852621e 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -283,6 +283,7 @@ $(BIN)/tests/units: \ $(OBJ)/tests/alloc.o \ $(OBJ)/tests/bfstd.o \ $(OBJ)/tests/bit.o \ + $(OBJ)/tests/ioq.o \ $(OBJ)/tests/main.o \ $(OBJ)/tests/trie.o \ $(OBJ)/tests/xtime.o \ diff --git a/tests/ioq.c b/tests/ioq.c new file mode 100644 index 0000000..3d35650 --- /dev/null +++ b/tests/ioq.c @@ -0,0 +1,73 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "tests.h" +#include "../src/ioq.h" +#include "../src/bfstd.h" +#include "../src/diag.h" +#include "../src/dir.h" +#include +#include + +/** + * Test for blocking within ioq_slot_push(). + * + * struct ioqq only supports non-blocking reads; if a write encounters a full + * slot, it must block until someone pops from that slot: + * + * Reader Writer + * ────────────────────────── ───────────────────────── + * tail: 0 → 1 + * slots[0]: empty → full + * tail: 1 → 0 + * slots[1]: empty → full + * tail: 0 → 1 + * slots[0]: full → full* (IOQ_BLOCKED) + * ioq_slot_wait() ... + * head: 0 → 1 + * slots[0]: full* → empty + * ioq_slot_wake() + * ... + * slots[0]: empty → full + * + * To reproduce this unlikely scenario, we must fill up the ready queue, then + * call ioq_cancel() which pushes an additional sentinel IOQ_STOP operation. + */ +static void check_ioq_push_block(void) { + // Must be a power of two to fill the entire queue + const size_t depth = 2; + + struct ioq *ioq = ioq_create(depth, 1); + bfs_verify(ioq, "ioq_create(): %s", xstrerror(errno)); + + // Push enough operations to fill the queue + for (size_t i = 0; i < depth; ++i) { + struct bfs_dir *dir = bfs_allocdir(); + bfs_verify(dir, "bfs_allocdir(): %s", xstrerror(errno)); + + int ret = ioq_opendir(ioq, dir, AT_FDCWD, ".", 0, NULL); + bfs_verify(ret == 0, "ioq_opendir(): %s", xstrerror(errno)); + } + bfs_verify(ioq_capacity(ioq) == 0); + + // Now cancel the queue, pushing an additional IOQ_STOP message + ioq_cancel(ioq); + + // Drain the queue + struct ioq_ent *ent; + while ((ent = ioq_pop(ioq, true))) { + bfs_verify(ent->op == IOQ_OPENDIR); + if (ent->result >= 0) { + bfs_closedir(ent->opendir.dir); + } + free(ent->opendir.dir); + ioq_free(ioq, ent); + } + + ioq_destroy(ioq); +} + +bool check_ioq(void) { + check_ioq_push_block(); + return true; +} diff --git a/tests/main.c b/tests/main.c index b7292b4..e69fe35 100644 --- a/tests/main.c +++ b/tests/main.c @@ -98,6 +98,7 @@ int main(int argc, char *argv[]) { run_test(&ctx, "alloc", check_alloc); run_test(&ctx, "bfstd", check_bfstd); run_test(&ctx, "bit", check_bit); + run_test(&ctx, "ioq", check_ioq); run_test(&ctx, "trie", check_trie); run_test(&ctx, "xtime", check_xtime); diff --git a/tests/tests.h b/tests/tests.h index d2f3611..d8adbb9 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -22,6 +22,9 @@ bool check_bfstd(void); /** Bit manipulation tests. */ bool check_bit(void); +/** I/O queue tests. */ +bool check_ioq(void); + /** Trie tests. */ bool check_trie(void); -- cgit v1.2.3 From a9f3cde30426b546ba6e3172e1a7951213a72049 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 28 Feb 2024 15:43:44 -0500 Subject: xtime: Fix some xgetdate() bugs And add some more test cases. --- src/xtime.c | 33 +++++++++++++------ tests/xtime.c | 102 ++++++++++++++++++++++++++++++++++++++++++++++++++++------ 2 files changed, 117 insertions(+), 18 deletions(-) (limited to 'tests') diff --git a/src/xtime.c b/src/xtime.c index e90bdb1..4309289 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -174,19 +174,29 @@ overflow: return -1; } +/** Parse a decimal digit. */ +static int xgetdigit(char c) { + int ret = c - '0'; + if (ret < 0 || ret > 9) { + return -1; + } else { + return ret; + } +} + /** Parse some digits from a timestamp. */ static int xgetpart(const char **str, size_t n, int *result) { - char buf[n + 1]; + *result = 0; + for (size_t i = 0; i < n; ++i, ++*str) { - char c = **str; - if (c < '0' || c > '9') { + int dig = xgetdigit(**str); + if (dig < 0) { return -1; } - buf[i] = c; + *result *= 10; + *result += dig; } - buf[n] = '\0'; - *result = atoi(buf); return 0; } @@ -239,6 +249,8 @@ int xgetdate(const char *str, struct timespec *result) { goto end; } else if (*str == ':') { ++str; + } else if (xgetdigit(*str) < 0) { + goto zone; } if (xgetpart(&str, 2, &tm.tm_min) != 0) { goto invalid; @@ -249,11 +261,14 @@ int xgetdate(const char *str, struct timespec *result) { goto end; } else if (*str == ':') { ++str; + } else if (xgetdigit(*str) < 0) { + goto zone; } if (xgetpart(&str, 2, &tm.tm_sec) != 0) { goto invalid; } +zone: if (!*str) { goto end; } else if (*str == 'Z') { @@ -296,11 +311,11 @@ end: goto error; } - int offset = 60 * tz_hour + tz_min; + int offset = (tz_hour * 60 + tz_min) * 60; if (tz_negative) { - result->tv_sec -= offset; - } else { result->tv_sec += offset; + } else { + result->tv_sec -= offset; } } diff --git a/tests/xtime.c b/tests/xtime.c index 53ecbc4..c2ee8f3 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -3,7 +3,9 @@ #include "tests.h" #include "../src/xtime.h" +#include "../src/bfstd.h" #include "../src/config.h" +#include #include #include #include @@ -49,22 +51,92 @@ static void tm_print(FILE *file, const struct tm *tm) { tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); } -bool check_xtime(void) { - if (setenv("TZ", "UTC0", true) != 0) { - perror("setenv()"); +/** Check one xgetdate() result. */ +static bool compare_xgetdate(const char *str, int error, time_t expected) { + struct timespec ts; + int ret = xgetdate(str, &ts); + + if (error) { + if (ret != -1 || errno != error) { + fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); + return false; + } + } else if (ret != 0) { + fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); + return false; + } else if (ts.tv_sec != expected || ts.tv_nsec != 0) { + fprintf(stderr, "xgetdate('%s'): %jd.%09jd != %jd\n", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); return false; } + return true; +} + +/** xgetdate() tests. */ +static bool check_xgetdate(void) { + return compare_xgetdate("", EINVAL, 0) + & compare_xgetdate("????", EINVAL, 0) + & compare_xgetdate("1991", EINVAL, 0) + & compare_xgetdate("1991-??", EINVAL, 0) + & compare_xgetdate("1991-12", EINVAL, 0) + & compare_xgetdate("1991-12-", EINVAL, 0) + & compare_xgetdate("1991-12-??", EINVAL, 0) + & compare_xgetdate("1991-12-14", 0, 692668800) + & compare_xgetdate("1991-12-14-", EINVAL, 0) + & compare_xgetdate("1991-12-14T", EINVAL, 0) + & compare_xgetdate("1991-12-14T??", EINVAL, 0) + & compare_xgetdate("1991-12-14T10", 0, 692704800) + & compare_xgetdate("1991-12-14T10:??", EINVAL, 0) + & compare_xgetdate("1991-12-14T10:11", 0, 692705460) + & compare_xgetdate("1991-12-14T10:11:??", EINVAL, 0) + & compare_xgetdate("1991-12-14T10:11:12", 0, 692705472) + & compare_xgetdate("1991-12-14T10Z", 0, 692704800) + & compare_xgetdate("1991-12-14T10:11Z", 0, 692705460) + & compare_xgetdate("1991-12-14T10:11:12Z", 0, 692705472) + & compare_xgetdate("1991-12-14T10:11:12?", EINVAL, 0) + & compare_xgetdate("1991-12-14T02-08", 0, 692704800) + & compare_xgetdate("1991-12-14T06:41-03:30", 0, 692705460) + & compare_xgetdate("1991-12-14T02:11:12-08:00", 0, 692705472) + & compare_xgetdate("19911214 021112-0800", 0, 692705472); +} + +/** xmktime() tests. */ +static bool check_xmktime(void) { + for (time_t time = -10; time <= 10; ++time) { + struct tm tm; + if (xlocaltime(&time, &tm) != 0) { + perror("xlocaltime()"); + return false; + } + + time_t made; + if (xmktime(&tm, &made) != 0) { + perror("xmktime()"); + return false; + } + + if (time != made) { + fprintf(stderr, "Mismatch: %jd != %jd\n", (intmax_t)time, (intmax_t)made); + tm_print(stderr, &tm); + return false; + } + } + + return true; +} + +/** xtimegm() tests. */ +static bool check_xtimegm(void) { struct tm tm = { .tm_isdst = -1, }; - for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) - for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) - for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) - for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) - for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) - for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { + for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) + for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) + for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) + for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) + for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) + for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { struct tm tma = tm, tmb = tm; time_t ta, tb; ta = mktime(&tma); @@ -93,3 +165,15 @@ bool check_xtime(void) { return true; } + +bool check_xtime(void) { + if (setenv("TZ", "UTC0", true) != 0) { + perror("setenv()"); + return false; + } + tzset(); + + return check_xgetdate() + & check_xmktime() + & check_xtimegm(); +} -- cgit v1.2.3 From e6d80d04d6928c452b48eae717373bf6a943e355 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 29 Feb 2024 10:12:43 -0500 Subject: tests: Add more datetime parsing integration tests --- tests/common/newermt.sh | 4 +++- tests/xtime.c | 6 +++--- 2 files changed, 6 insertions(+), 4 deletions(-) (limited to 'tests') diff --git a/tests/common/newermt.sh b/tests/common/newermt.sh index 3c5be68..e816b29 100644 --- a/tests/common/newermt.sh +++ b/tests/common/newermt.sh @@ -1 +1,3 @@ -bfs_diff times -newermt 1991-12-14T00:01 +bfs_diff times -newermt 1991-12-14T00:01 \ + -newermt "1991-12-14 01:01+01:00" \ + -newermt "19911213 20:31:00-0330" diff --git a/tests/xtime.c b/tests/xtime.c index c2ee8f3..4a7d55c 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -94,10 +94,10 @@ static bool check_xgetdate(void) { & compare_xgetdate("1991-12-14T10:11Z", 0, 692705460) & compare_xgetdate("1991-12-14T10:11:12Z", 0, 692705472) & compare_xgetdate("1991-12-14T10:11:12?", EINVAL, 0) - & compare_xgetdate("1991-12-14T02-08", 0, 692704800) + & compare_xgetdate("1991-12-14T03-07", 0, 692704800) & compare_xgetdate("1991-12-14T06:41-03:30", 0, 692705460) - & compare_xgetdate("1991-12-14T02:11:12-08:00", 0, 692705472) - & compare_xgetdate("19911214 021112-0800", 0, 692705472); + & compare_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472) + & compare_xgetdate("19911214 031112-0700", 0, 692705472); } /** xmktime() tests. */ -- cgit v1.2.3 From 4e2f094d29ff8140f2b46a059128c780560db0f1 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 29 Feb 2024 13:23:02 -0500 Subject: tests: New bfs_check() macro We now report failures and continue, rather than aborting after the first failure. --- tests/alloc.c | 22 +++--- tests/bfstd.c | 65 +++++++++-------- tests/bit.c | 108 ++++++++++++++-------------- tests/tests.h | 20 ++++++ tests/trie.c | 46 ++++++------ tests/xtime.c | 220 ++++++++++++++++++++++++++-------------------------------- 6 files changed, 247 insertions(+), 234 deletions(-) (limited to 'tests') diff --git a/tests/alloc.c b/tests/alloc.c index e14f131..4ce23d4 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -9,30 +9,32 @@ #include bool check_alloc(void) { + bool ret = true; + // Check sizeof_flex() struct flexible { alignas(64) int foo[8]; int bar[]; }; - bfs_verify(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); - bfs_verify(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); + ret &= bfs_check(sizeof_flex(struct flexible, bar, 0) >= sizeof(struct flexible)); + ret &= bfs_check(sizeof_flex(struct flexible, bar, 16) % alignof(struct flexible) == 0); size_t too_many = SIZE_MAX / sizeof(int) + 1; - bfs_verify(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); + ret &= bfs_check(sizeof_flex(struct flexible, bar, too_many) == align_floor(alignof(struct flexible), SIZE_MAX)); // Corner case: sizeof(type) > align_ceil(alignof(type), offsetof(type, member)) // Doesn't happen in typical ABIs - bfs_verify(flex_size(8, 16, 4, 4, 1) == 16); + ret &= bfs_check(flex_size(8, 16, 4, 4, 1) == 16); // Make sure we detect allocation size overflows #if __GNUC__ && !__clang__ # pragma GCC diagnostic ignored "-Walloc-size-larger-than=" #endif - bfs_verify(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); - bfs_verify(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); - bfs_verify(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); - bfs_verify(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ZALLOC_ARRAY(int, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); + ret &= bfs_check(ZALLOC_FLEX(struct flexible, bar, too_many) == NULL && errno == EOVERFLOW); // varena tests struct varena varena; @@ -41,9 +43,9 @@ bool check_alloc(void) { for (size_t i = 0; i < 256; ++i) { bfs_verify(varena_alloc(&varena, i)); struct arena *arena = &varena.arenas[varena.narenas - 1]; - bfs_verify(arena->size >= sizeof_flex(struct flexible, bar, i)); + ret &= bfs_check(arena->size >= sizeof_flex(struct flexible, bar, i)); } varena_destroy(&varena); - return true; + return ret; } diff --git a/tests/bfstd.c b/tests/bfstd.c index d385c6b..4d0ec23 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -14,61 +14,68 @@ #include /** Check the result of xdirname()/xbasename(). */ -static void check_base_dir(const char *path, const char *dir, const char *base) { +static bool check_base_dir(const char *path, const char *dir, const char *base) { + bool ret = true; + char *xdir = xdirname(path); bfs_verify(xdir, "xdirname(): %s", xstrerror(errno)); - bfs_verify(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); + ret &= bfs_check(strcmp(xdir, dir) == 0, "xdirname('%s') == '%s' (!= '%s')", path, xdir, dir); free(xdir); char *xbase = xbasename(path); bfs_verify(xbase, "xbasename(): %s", xstrerror(errno)); - bfs_verify(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); + ret &= bfs_check(strcmp(xbase, base) == 0, "xbasename('%s') == '%s' (!= '%s')", path, xbase, base); free(xbase); + + return ret; } /** Check the result of wordesc(). */ -static void check_wordesc(const char *str, const char *exp, enum wesc_flags flags) { +static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flags) { char buf[256]; char *end = buf + sizeof(buf); - char *ret = wordesc(buf, end, str, flags); - bfs_verify(ret != end); - bfs_verify(strcmp(buf, exp) == 0, "wordesc(%s) == %s (!= %s)", str, buf, exp); + char *esc = wordesc(buf, end, str, flags); + + return bfs_check(esc != end) + && bfs_check(strcmp(buf, exp) == 0, "wordesc('%s') == '%s' (!= '%s')", str, buf, exp); } bool check_bfstd(void) { + bool ret = true; + // Try to set a UTF-8 locale if (!setlocale(LC_ALL, "C.UTF-8")) { setlocale(LC_ALL, ""); } // From man 3p basename - check_base_dir("usr", ".", "usr"); - check_base_dir("usr/", ".", "usr"); - check_base_dir("", ".", "."); - check_base_dir("/", "/", "/"); + ret &= check_base_dir("usr", ".", "usr"); + ret &= check_base_dir("usr/", ".", "usr"); + ret &= check_base_dir("", ".", "."); + ret &= check_base_dir("/", "/", "/"); // check_base_dir("//", "/" or "//", "/" or "//"); - check_base_dir("///", "/", "/"); - check_base_dir("/usr/", "/", "usr"); - check_base_dir("/usr/lib", "/usr", "lib"); - check_base_dir("//usr//lib//", "//usr", "lib"); - check_base_dir("/home//dwc//test", "/home//dwc", "test"); + ret &= check_base_dir("///", "/", "/"); + ret &= check_base_dir("/usr/", "/", "usr"); + ret &= check_base_dir("/usr/lib", "/usr", "lib"); + ret &= check_base_dir("//usr//lib//", "//usr", "lib"); + ret &= check_base_dir("/home//dwc//test", "/home//dwc", "test"); - check_wordesc("", "\"\"", WESC_SHELL); - check_wordesc("word", "word", WESC_SHELL); - check_wordesc("two words", "\"two words\"", WESC_SHELL); - check_wordesc("word's", "\"word's\"", WESC_SHELL); - check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL); - check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); - check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); - check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("", "\"\"", WESC_SHELL); + ret &= check_wordesc("word", "word", WESC_SHELL); + ret &= check_wordesc("two words", "\"two words\"", WESC_SHELL); + ret &= check_wordesc("word's", "\"word's\"", WESC_SHELL); + ret &= check_wordesc("\"word\"", "'\"word\"'", WESC_SHELL); + ret &= check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); + ret &= check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); const char *charmap = nl_langinfo(CODESET); if (strcmp(charmap, "UTF-8") == 0) { - check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY); - check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); - check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); - check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0", "$'\\xF0'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); } - return true; + return ret; } diff --git a/tests/bit.c b/tests/bit.c index 1c6a4de..b944748 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -52,75 +52,77 @@ bfs_static_assert(UINTMAX_MAX == UWIDTH_MAX(UINTMAX_WIDTH)); bfs_static_assert(INTMAX_MIN == IWIDTH_MIN(INTMAX_WIDTH)); bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); -#define verify_eq(a, b) \ - bfs_verify((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) +#define check_eq(a, b) \ + bfs_check((a) == (b), "(0x%jX) %s != %s (0x%jX)", (uintmax_t)(a), #a, #b, (uintmax_t)(b)) bool check_bit(void) { - verify_eq(bswap((uint8_t)0x12), 0x12); - verify_eq(bswap((uint16_t)0x1234), 0x3412); - verify_eq(bswap((uint32_t)0x12345678), 0x78563412); - verify_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); - - verify_eq(count_ones(0x0), 0); - verify_eq(count_ones(0x1), 1); - verify_eq(count_ones(0x2), 1); - verify_eq(count_ones(0x3), 2); - verify_eq(count_ones(0x137F), 10); - - verify_eq(count_zeros(0), INT_WIDTH); - verify_eq(count_zeros(0L), LONG_WIDTH); - verify_eq(count_zeros(0LL), LLONG_WIDTH); - verify_eq(count_zeros((uint8_t)0), 8); - verify_eq(count_zeros((uint16_t)0), 16); - verify_eq(count_zeros((uint32_t)0), 32); - verify_eq(count_zeros((uint64_t)0), 64); - - verify_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); - verify_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); - verify_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); - verify_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); - - verify_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); - verify_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); - verify_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); - verify_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); + bool ret = true; + + ret &= check_eq(bswap((uint8_t)0x12), 0x12); + ret &= check_eq(bswap((uint16_t)0x1234), 0x3412); + ret &= check_eq(bswap((uint32_t)0x12345678), 0x78563412); + ret &= check_eq(bswap((uint64_t)0x1234567812345678), 0x7856341278563412); + + ret &= check_eq(count_ones(0x0), 0); + ret &= check_eq(count_ones(0x1), 1); + ret &= check_eq(count_ones(0x2), 1); + ret &= check_eq(count_ones(0x3), 2); + ret &= check_eq(count_ones(0x137F), 10); + + ret &= check_eq(count_zeros(0), INT_WIDTH); + ret &= check_eq(count_zeros(0L), LONG_WIDTH); + ret &= check_eq(count_zeros(0LL), LLONG_WIDTH); + ret &= check_eq(count_zeros((uint8_t)0), 8); + ret &= check_eq(count_zeros((uint16_t)0), 16); + ret &= check_eq(count_zeros((uint32_t)0), 32); + ret &= check_eq(count_zeros((uint64_t)0), 64); + + ret &= check_eq(rotate_left((uint8_t)0xA1, 4), 0x1A); + ret &= check_eq(rotate_left((uint16_t)0x1234, 12), 0x4123); + ret &= check_eq(rotate_left((uint32_t)0x12345678, 20), 0x67812345); + ret &= check_eq(rotate_left((uint32_t)0x12345678, 0), 0x12345678); + + ret &= check_eq(rotate_right((uint8_t)0xA1, 4), 0x1A); + ret &= check_eq(rotate_right((uint16_t)0x1234, 12), 0x2341); + ret &= check_eq(rotate_right((uint32_t)0x12345678, 20), 0x45678123); + ret &= check_eq(rotate_right((uint32_t)0x12345678, 0), 0x12345678); for (int i = 0; i < 16; ++i) { uint16_t n = (uint16_t)1 << i; for (int j = i; j < 16; ++j) { uint16_t m = (uint16_t)1 << j; uint16_t nm = n | m; - verify_eq(count_ones(nm), 1 + (n != m)); - verify_eq(count_zeros(nm), 15 - (n != m)); - verify_eq(leading_zeros(nm), 15 - j); - verify_eq(trailing_zeros(nm), i); - verify_eq(first_leading_one(nm), j + 1); - verify_eq(first_trailing_one(nm), i + 1); - verify_eq(bit_width(nm), j + 1); - verify_eq(bit_floor(nm), m); + ret &= check_eq(count_ones(nm), 1 + (n != m)); + ret &= check_eq(count_zeros(nm), 15 - (n != m)); + ret &= check_eq(leading_zeros(nm), 15 - j); + ret &= check_eq(trailing_zeros(nm), i); + ret &= check_eq(first_leading_one(nm), j + 1); + ret &= check_eq(first_trailing_one(nm), i + 1); + ret &= check_eq(bit_width(nm), j + 1); + ret &= check_eq(bit_floor(nm), m); if (n == m) { - verify_eq(bit_ceil(nm), m); - bfs_verify(has_single_bit(nm)); + ret &= check_eq(bit_ceil(nm), m); + ret &= bfs_check(has_single_bit(nm)); } else { if (j < 15) { - verify_eq(bit_ceil(nm), (m << 1)); + ret &= check_eq(bit_ceil(nm), (m << 1)); } - bfs_verify(!has_single_bit(nm)); + ret &= bfs_check(!has_single_bit(nm)); } } } - verify_eq(leading_zeros((uint16_t)0), 16); - verify_eq(trailing_zeros((uint16_t)0), 16); - verify_eq(first_leading_one(0), 0); - verify_eq(first_trailing_one(0), 0); - verify_eq(bit_width(0), 0); - verify_eq(bit_floor(0), 0); - verify_eq(bit_ceil(0), 1); + ret &= check_eq(leading_zeros((uint16_t)0), 16); + ret &= check_eq(trailing_zeros((uint16_t)0), 16); + ret &= check_eq(first_leading_one(0), 0); + ret &= check_eq(first_trailing_one(0), 0); + ret &= check_eq(bit_width(0), 0); + ret &= check_eq(bit_floor(0), 0); + ret &= check_eq(bit_ceil(0), 1); - bfs_verify(!has_single_bit(0)); - bfs_verify(!has_single_bit(UINT32_MAX)); - bfs_verify(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); + ret &= bfs_check(!has_single_bit(0)); + ret &= bfs_check(!has_single_bit(UINT32_MAX)); + ret &= bfs_check(has_single_bit((uint32_t)1 << (UINT_WIDTH - 1))); - return true; + return ret; } diff --git a/tests/tests.h b/tests/tests.h index d8adbb9..6629dcf 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -9,6 +9,7 @@ #define BFS_TESTS_H #include "../src/config.h" +#include "../src/diag.h" /** Unit test function type. */ typedef bool test_fn(void); @@ -31,4 +32,23 @@ bool check_trie(void); /** Time tests. */ bool check_xtime(void); +/** Don't ignore the bfs_check() return value. */ +attr(nodiscard) +static inline bool bfs_check(bool ret) { + return ret; +} + +/** + * Check a condition, logging a message on failure but continuing. + */ +#define bfs_check(...) \ + bfs_check(bfs_check_(#__VA_ARGS__, __VA_ARGS__, "", "")) + +#define bfs_check_(str, cond, format, ...) \ + ((cond) ? true : (bfs_diag( \ + sizeof(format) > 1 \ + ? "%.0s" format "%s%s" \ + : "Check failed: `%s`%s", \ + str, __VA_ARGS__), false)) + #endif // BFS_TESTS_H diff --git a/tests/trie.c b/tests/trie.c index f94d7c8..fec0de2 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -40,11 +40,13 @@ const char *keys[] = { const size_t nkeys = countof(keys); bool check_trie(void) { + bool ret = true; + struct trie trie; trie_init(&trie); for (size_t i = 0; i < nkeys; ++i) { - bfs_verify(!trie_find_str(&trie, keys[i])); + ret &= bfs_check(!trie_find_str(&trie, keys[i])); const char *prefix = NULL; for (size_t j = 0; j < i; ++j) { @@ -58,37 +60,37 @@ bool check_trie(void) { struct trie_leaf *leaf = trie_find_prefix(&trie, keys[i]); if (prefix) { bfs_verify(leaf); - bfs_verify(strcmp(prefix, leaf->key) == 0); + ret &= bfs_check(strcmp(prefix, leaf->key) == 0); } else { - bfs_verify(!leaf); + ret &= bfs_check(!leaf); } leaf = trie_insert_str(&trie, keys[i]); bfs_verify(leaf); - bfs_verify(strcmp(keys[i], leaf->key) == 0); - bfs_verify(leaf->length == strlen(keys[i]) + 1); + ret &= bfs_check(strcmp(keys[i], leaf->key) == 0); + ret &= bfs_check(leaf->length == strlen(keys[i]) + 1); } { size_t i = 0; for_trie (leaf, &trie) { - bfs_verify(leaf == trie_find_str(&trie, keys[i])); - bfs_verify(!leaf->prev || leaf->prev->next == leaf); - bfs_verify(!leaf->next || leaf->next->prev == leaf); + ret &= bfs_check(leaf == trie_find_str(&trie, keys[i])); + ret &= bfs_check(!leaf->prev || leaf->prev->next == leaf); + ret &= bfs_check(!leaf->next || leaf->next->prev == leaf); ++i; } - bfs_verify(i == nkeys); + ret &= bfs_check(i == nkeys); } for (size_t i = 0; i < nkeys; ++i) { struct trie_leaf *leaf = trie_find_str(&trie, keys[i]); bfs_verify(leaf); - bfs_verify(strcmp(keys[i], leaf->key) == 0); - bfs_verify(leaf->length == strlen(keys[i]) + 1); + ret &= bfs_check(strcmp(keys[i], leaf->key) == 0); + ret &= bfs_check(leaf->length == strlen(keys[i]) + 1); trie_remove(&trie, leaf); leaf = trie_find_str(&trie, keys[i]); - bfs_verify(!leaf); + ret &= bfs_check(!leaf); const char *postfix = NULL; for (size_t j = i + 1; j < nkeys; ++j) { @@ -102,14 +104,14 @@ bool check_trie(void) { leaf = trie_find_postfix(&trie, keys[i]); if (postfix) { bfs_verify(leaf); - bfs_verify(strcmp(postfix, leaf->key) == 0); + ret &= bfs_check(strcmp(postfix, leaf->key) == 0); } else { - bfs_verify(!leaf); + ret &= bfs_check(!leaf); } } for_trie (leaf, &trie) { - bfs_verify(false); + ret &= bfs_check(false, "trie should be empty"); } // This tests the "jump" node handling on 32-bit platforms @@ -118,18 +120,18 @@ bool check_trie(void) { bfs_verify(longstr); memset(longstr, 0xAC, longsize); - bfs_verify(!trie_find_mem(&trie, longstr, longsize)); - bfs_verify(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); memset(longstr + longsize / 2, 0xAB, longsize / 2); - bfs_verify(!trie_find_mem(&trie, longstr, longsize)); - bfs_verify(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); memset(longstr, 0xAA, longsize / 2); - bfs_verify(!trie_find_mem(&trie, longstr, longsize)); - bfs_verify(trie_insert_mem(&trie, longstr, longsize)); + ret &= bfs_check(!trie_find_mem(&trie, longstr, longsize)); + ret &= bfs_check(trie_insert_mem(&trie, longstr, longsize)); free(longstr); trie_destroy(&trie); - return true; + return ret; } diff --git a/tests/xtime.c b/tests/xtime.c index 4a7d55c..f853428 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -12,121 +12,122 @@ #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { - if (tma->tm_year != tmb->tm_year) { - return false; - } - if (tma->tm_mon != tmb->tm_mon) { - return false; - } - if (tma->tm_mday != tmb->tm_mday) { - return false; - } - if (tma->tm_hour != tmb->tm_hour) { - return false; - } - if (tma->tm_min != tmb->tm_min) { - return false; - } - if (tma->tm_sec != tmb->tm_sec) { - return false; - } - if (tma->tm_wday != tmb->tm_wday) { - return false; - } - if (tma->tm_yday != tmb->tm_yday) { - return false; - } - if (tma->tm_isdst != tmb->tm_isdst) { - return false; - } - - return true; -} - -static void tm_print(FILE *file, const struct tm *tm) { - fprintf(file, "Y%d M%d D%d h%d m%d s%d wd%d yd%d%s\n", - tm->tm_year, tm->tm_mon, tm->tm_mday, - tm->tm_hour, tm->tm_min, tm->tm_sec, - tm->tm_wday, tm->tm_yday, - tm->tm_isdst ? (tm->tm_isdst < 0 ? " (DST?)" : " (DST)") : ""); + return tma->tm_year == tmb->tm_year + && tma->tm_mon == tmb->tm_mon + && tma->tm_mday == tmb->tm_mday + && tma->tm_hour == tmb->tm_hour + && tma->tm_min == tmb->tm_min + && tma->tm_sec == tmb->tm_sec + && tma->tm_wday == tmb->tm_wday + && tma->tm_yday == tmb->tm_yday + && tma->tm_isdst == tmb->tm_isdst; } /** Check one xgetdate() result. */ -static bool compare_xgetdate(const char *str, int error, time_t expected) { +static bool check_one_xgetdate(const char *str, int error, time_t expected) { struct timespec ts; int ret = xgetdate(str, &ts); if (error) { - if (ret != -1 || errno != error) { - fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); - return false; - } - } else if (ret != 0) { - fprintf(stderr, "xgetdate('%s'): %s\n", str, xstrerror(errno)); - return false; - } else if (ts.tv_sec != expected || ts.tv_nsec != 0) { - fprintf(stderr, "xgetdate('%s'): %jd.%09jd != %jd\n", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); - return false; + return bfs_check(ret == -1 && errno == error, "xgetdate('%s'): %s", str, xstrerror(errno)); + } else { + return bfs_check(ret == 0, "xgetdate('%s'): %s", str, xstrerror(errno)) + && bfs_check(ts.tv_sec == expected && ts.tv_nsec == 0, + "xgetdate('%s'): %jd.%09jd != %jd", + str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); } - - return true; } /** xgetdate() tests. */ static bool check_xgetdate(void) { - return compare_xgetdate("", EINVAL, 0) - & compare_xgetdate("????", EINVAL, 0) - & compare_xgetdate("1991", EINVAL, 0) - & compare_xgetdate("1991-??", EINVAL, 0) - & compare_xgetdate("1991-12", EINVAL, 0) - & compare_xgetdate("1991-12-", EINVAL, 0) - & compare_xgetdate("1991-12-??", EINVAL, 0) - & compare_xgetdate("1991-12-14", 0, 692668800) - & compare_xgetdate("1991-12-14-", EINVAL, 0) - & compare_xgetdate("1991-12-14T", EINVAL, 0) - & compare_xgetdate("1991-12-14T??", EINVAL, 0) - & compare_xgetdate("1991-12-14T10", 0, 692704800) - & compare_xgetdate("1991-12-14T10:??", EINVAL, 0) - & compare_xgetdate("1991-12-14T10:11", 0, 692705460) - & compare_xgetdate("1991-12-14T10:11:??", EINVAL, 0) - & compare_xgetdate("1991-12-14T10:11:12", 0, 692705472) - & compare_xgetdate("1991-12-14T10Z", 0, 692704800) - & compare_xgetdate("1991-12-14T10:11Z", 0, 692705460) - & compare_xgetdate("1991-12-14T10:11:12Z", 0, 692705472) - & compare_xgetdate("1991-12-14T10:11:12?", EINVAL, 0) - & compare_xgetdate("1991-12-14T03-07", 0, 692704800) - & compare_xgetdate("1991-12-14T06:41-03:30", 0, 692705460) - & compare_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472) - & compare_xgetdate("19911214 031112-0700", 0, 692705472); + bool ret = true; + + ret &= check_one_xgetdate("", EINVAL, 0); + ret &= check_one_xgetdate("????", EINVAL, 0); + ret &= check_one_xgetdate("1991", EINVAL, 0); + ret &= check_one_xgetdate("1991-??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14", 0, 692668800); + ret &= check_one_xgetdate("1991-12-14-", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T10:??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10:11", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T10:11:??", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T10:11:12", 0, 692705472); + ret &= check_one_xgetdate("1991-12-14T10Z", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T10:11Z", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T10:11:12Z", 0, 692705472); + ret &= check_one_xgetdate("1991-12-14T10:11:12?", EINVAL, 0); + ret &= check_one_xgetdate("1991-12-14T03-07", 0, 692704800); + ret &= check_one_xgetdate("1991-12-14T06:41-03:30", 0, 692705460); + ret &= check_one_xgetdate("1991-12-14T03:11:12-07:00", 0, 692705472); + ret &= check_one_xgetdate("19911214 031112-0700", 0, 692705472);; + + return ret; +} + +#define TM_FORMAT "%04d-%02d-%02d %02d:%02d:%02d (%d/7, %d/365%s)" + +#define TM_PRINTF(tm) \ + (1900 + (tm).tm_year), (tm).tm_mon, (tm).tm_mday, \ + (tm).tm_hour, (tm).tm_min, (tm).tm_sec, \ + ((tm).tm_wday + 1), ((tm).tm_yday + 1), \ + ((tm).tm_isdst ? ((tm).tm_isdst < 0 ? ", DST?" : ", DST") : "") + +/** Check one xmktime() result. */ +static bool check_one_xmktime(time_t expected) { + struct tm tm; + if (xlocaltime(&expected, &tm) != 0) { + bfs_diag("xlocaltime(%jd): %s", (intmax_t)expected, xstrerror(errno)); + return false; + } + + time_t actual; + return bfs_check(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT "): %s", TM_PRINTF(tm), xstrerror(errno)) + && bfs_check(actual == expected, "xmktime(" TM_FORMAT "): %jd != %jd", TM_PRINTF(tm), (intmax_t)actual, (intmax_t)expected); } /** xmktime() tests. */ static bool check_xmktime(void) { + bool ret = true; + for (time_t time = -10; time <= 10; ++time) { - struct tm tm; - if (xlocaltime(&time, &tm) != 0) { - perror("xlocaltime()"); - return false; - } - - time_t made; - if (xmktime(&tm, &made) != 0) { - perror("xmktime()"); - return false; - } - - if (time != made) { - fprintf(stderr, "Mismatch: %jd != %jd\n", (intmax_t)time, (intmax_t)made); - tm_print(stderr, &tm); - return false; - } + ret &= check_one_xmktime(time); + } + + return ret; +} + +/** Check one xtimegm() result. */ +static bool check_one_xtimegm(const struct tm *tm) { + struct tm tma = *tm, tmb = *tm; + time_t ta, tb; + ta = mktime(&tma); + if (xtimegm(&tmb, &tb) != 0) { + tb = -1; } - return true; + bool ret = true; + ret &= bfs_check(ta == tb, "%jd != %jd", (intmax_t)ta, (intmax_t)tb); + ret &= bfs_check(ta == -1 || tm_equal(&tma, &tmb)); + + if (!ret) { + bfs_diag("mktime(): " TM_FORMAT, TM_PRINTF(tma)); + bfs_diag("xtimegm(): " TM_FORMAT, TM_PRINTF(tmb)); + bfs_diag("(input): " TM_FORMAT, TM_PRINTF(*tm)); + } + + return ret; } /** xtimegm() tests. */ static bool check_xtimegm(void) { + bool ret = true; + struct tm tm = { .tm_isdst = -1, }; @@ -137,33 +138,10 @@ static bool check_xtimegm(void) { for (tm.tm_hour = -1; tm.tm_hour <= 24; tm.tm_hour += 5) for (tm.tm_min = -1; tm.tm_min <= 60; tm.tm_min += 31) for (tm.tm_sec = -60; tm.tm_sec <= 120; tm.tm_sec += 5) { - struct tm tma = tm, tmb = tm; - time_t ta, tb; - ta = mktime(&tma); - if (xtimegm(&tmb, &tb) != 0) { - tb = -1; - } - - bool fail = false; - if (ta != tb) { - printf("Mismatch: %jd != %jd\n", (intmax_t)ta, (intmax_t)tb); - fail = true; - } - if (ta != -1 && !tm_equal(&tma, &tmb)) { - printf("mktime(): "); - tm_print(stdout, &tma); - printf("xtimegm(): "); - tm_print(stdout, &tmb); - fail = true; - } - if (fail) { - printf("Input: "); - tm_print(stdout, &tm); - return false; - } + ret &= check_one_xtimegm(&tm); } - return true; + return ret; } bool check_xtime(void) { @@ -173,7 +151,9 @@ bool check_xtime(void) { } tzset(); - return check_xgetdate() - & check_xmktime() - & check_xtimegm(); + bool ret = true; + ret &= check_xgetdate(); + ret &= check_xmktime(); + ret &= check_xtimegm(); + return ret; } -- cgit v1.2.3 From 10d6f6c849ce2c6bc6275793c17a4a93c73b80de Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 2 Mar 2024 10:25:31 -0500 Subject: tests/ioq: Check that we push and pop the same number of times --- tests/ioq.c | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/ioq.c b/tests/ioq.c index 3d35650..56e1886 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -54,15 +54,17 @@ static void check_ioq_push_block(void) { ioq_cancel(ioq); // Drain the queue - struct ioq_ent *ent; - while ((ent = ioq_pop(ioq, true))) { - bfs_verify(ent->op == IOQ_OPENDIR); + for (size_t i = 0; i < depth; ++i) { + struct ioq_ent *ent = ioq_pop(ioq, true); + bfs_verify(ent && ent->op == IOQ_OPENDIR); + if (ent->result >= 0) { bfs_closedir(ent->opendir.dir); } free(ent->opendir.dir); ioq_free(ioq, ent); } + bfs_verify(!ioq_pop(ioq, true)); ioq_destroy(ioq); } -- cgit v1.2.3 From 416ca3b557055efa5746a4d40d927391c59a9292 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 7 Mar 2024 16:01:46 -0500 Subject: tests: Change the timezone for -daystart tests Otherwise, if the test files are created just before midnight, and the test runs just after midnight, -daystart will consider them one day old. Fixes #132 --- tests/gnu/daystart.sh | 2 +- tests/gnu/daystart_twice.sh | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/gnu/daystart.sh b/tests/gnu/daystart.sh index 9799bca..9c3be1a 100644 --- a/tests/gnu/daystart.sh +++ b/tests/gnu/daystart.sh @@ -1 +1 @@ -bfs_diff basic -daystart -mtime 0 +TZ=WAT-1 bfs_diff basic -daystart -mtime 0 diff --git a/tests/gnu/daystart_twice.sh b/tests/gnu/daystart_twice.sh index 21b2c0f..edbf18d 100644 --- a/tests/gnu/daystart_twice.sh +++ b/tests/gnu/daystart_twice.sh @@ -1 +1 @@ -bfs_diff basic -daystart -daystart -mtime 0 +TZ=WAT-1 bfs_diff basic -daystart -daystart -mtime 0 -- cgit v1.2.3 From 43cd776d7dc8ac573262f8459edeb1c1f5f3cd09 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 7 Mar 2024 16:18:32 -0500 Subject: xtime: Call tzset() from main() instead of lazily POSIX specifies[1] that If a thread accesses tzname, daylight, or timezone directly while another thread is in a call to tzset(), or to any function that is required or allowed to set timezone information as if by calling tzset(), the behavior is undefined. So calling it lazily from arbitrary threads is risky. [1]: https://pubs.opengroup.org/onlinepubs/9699919799/functions/tzset.html --- src/eval.c | 2 +- src/main.c | 4 ++++ src/parse.c | 12 ++++++------ src/printf.c | 4 ++-- src/xtime.c | 34 +--------------------------------- src/xtime.h | 24 ------------------------ tests/bfstd.c | 5 ----- tests/main.c | 14 ++++++++++++++ tests/xtime.c | 10 ++-------- tests/xtouch.c | 2 ++ 10 files changed, 32 insertions(+), 79 deletions(-) (limited to 'tests') diff --git a/src/eval.c b/src/eval.c index 8b71833..1711001 100644 --- a/src/eval.c +++ b/src/eval.c @@ -730,7 +730,7 @@ bool eval_fls(const struct bfs_expr *expr, struct bfs_eval *state) { time_t six_months_ago = now - 6 * 30 * 24 * 60 * 60; time_t tomorrow = now + 24 * 60 * 60; struct tm tm; - if (xlocaltime(&time, &tm) != 0) { + if (!localtime_r(&time, &tm)) { goto error; } char time_str[256]; diff --git a/src/main.c b/src/main.c index 1ddbc54..16a2576 100644 --- a/src/main.c +++ b/src/main.c @@ -55,6 +55,7 @@ #include #include #include +#include #include /** @@ -121,6 +122,9 @@ int main(int argc, char *argv[]) { locale_err = errno; } + // Apply the environment's timezone + tzset(); + // Parse the command line struct bfs_ctx *ctx = bfs_parse_cmdline(argc, argv); if (!ctx) { diff --git a/src/parse.c b/src/parse.c index 5d0f333..b26a50f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -1086,8 +1086,8 @@ static struct bfs_expr *parse_const(struct bfs_parser *parser, int value, int ar */ static struct bfs_expr *parse_daystart(struct bfs_parser *parser, int arg1, int arg2) { struct tm tm; - if (xlocaltime(&parser->now.tv_sec, &tm) != 0) { - parse_perror(parser, "xlocaltime()"); + if (!localtime_r(&parser->now.tv_sec, &tm)) { + parse_perror(parser, "localtime_r()"); return NULL; } @@ -1708,8 +1708,8 @@ static int parse_reftime(const struct bfs_parser *parser, struct bfs_expr *expr) fprintf(stderr, "Supported timestamp formats are ISO 8601-like, e.g.\n\n"); struct tm tm; - if (xlocaltime(&parser->now.tv_sec, &tm) != 0) { - parse_perror(parser, "xlocaltime()"); + if (!localtime_r(&parser->now.tv_sec, &tm)) { + parse_perror(parser, "localtime_r()"); return -1; } @@ -1728,8 +1728,8 @@ static int parse_reftime(const struct bfs_parser *parser, struct bfs_expr *expr) fprintf(stderr, " - %04d-%02d-%02dT%02d:%02d:%02d%+03d:%02d\n", year, month, tm.tm_mday, tm.tm_hour, tm.tm_min, tm.tm_sec, tz_hour, tz_min); - if (xgmtime(&parser->now.tv_sec, &tm) != 0) { - parse_perror(parser, "xgmtime()"); + if (!gmtime_r(&parser->now.tv_sec, &tm)) { + parse_perror(parser, "gmtime_r()"); return -1; } diff --git a/src/printf.c b/src/printf.c index 1fe8cc3..34ed606 100644 --- a/src/printf.c +++ b/src/printf.c @@ -123,7 +123,7 @@ static int bfs_printf_ctime(CFILE *cfile, const struct bfs_fmt *fmt, const struc } struct tm tm; - if (xlocaltime(&ts->tv_sec, &tm) != 0) { + if (!localtime_r(&ts->tv_sec, &tm)) { return -1; } @@ -153,7 +153,7 @@ static int bfs_printf_strftime(CFILE *cfile, const struct bfs_fmt *fmt, const st } struct tm tm; - if (xlocaltime(&ts->tv_sec, &tm) != 0) { + if (!localtime_r(&ts->tv_sec, &tm)) { return -1; } diff --git a/src/xtime.c b/src/xtime.c index 4309289..8100f6c 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -11,38 +11,6 @@ #include #include -/** Call tzset() if necessary. */ -static void xtzset(void) { - static atomic bool is_set = false; - - if (!load(&is_set, relaxed)) { - tzset(); - store(&is_set, true, relaxed); - } -} - -int xlocaltime(const time_t *timep, struct tm *result) { - // Should be called before localtime_r() according to POSIX.1-2004 - xtzset(); - - if (localtime_r(timep, result)) { - return 0; - } else { - return -1; - } -} - -int xgmtime(const time_t *timep, struct tm *result) { - // Should be called before gmtime_r() according to POSIX.1-2004 - xtzset(); - - if (gmtime_r(timep, result)) { - return 0; - } else { - return -1; - } -} - int xmktime(struct tm *tm, time_t *timep) { *timep = mktime(tm); @@ -50,7 +18,7 @@ int xmktime(struct tm *tm, time_t *timep) { int error = errno; struct tm tmp; - if (xlocaltime(timep, &tmp) != 0) { + if (!localtime_r(timep, &tmp)) { return -1; } diff --git a/src/xtime.h b/src/xtime.h index 75d1f4e..fb60ae4 100644 --- a/src/xtime.h +++ b/src/xtime.h @@ -10,30 +10,6 @@ #include -/** - * localtime_r() wrapper that calls tzset() first. - * - * @param[in] timep - * The time_t to convert. - * @param[out] result - * Buffer to hold the result. - * @return - * 0 on success, -1 on failure. - */ -int xlocaltime(const time_t *timep, struct tm *result); - -/** - * gmtime_r() wrapper that calls tzset() first. - * - * @param[in] timep - * The time_t to convert. - * @param[out] result - * Buffer to hold the result. - * @return - * 0 on success, -1 on failure. - */ -int xgmtime(const time_t *timep, struct tm *result); - /** * mktime() wrapper that reports errors more reliably. * diff --git a/tests/bfstd.c b/tests/bfstd.c index 4d0ec23..0ded5de 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -43,11 +43,6 @@ static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flag bool check_bfstd(void) { bool ret = true; - // Try to set a UTF-8 locale - if (!setlocale(LC_ALL, "C.UTF-8")) { - setlocale(LC_ALL, ""); - } - // From man 3p basename ret &= check_base_dir("usr", ".", "usr"); ret &= check_base_dir("usr/", ".", "usr"); diff --git a/tests/main.c b/tests/main.c index e69fe35..38438b2 100644 --- a/tests/main.c +++ b/tests/main.c @@ -11,9 +11,11 @@ #include "../src/config.h" #include "../src/diag.h" #include +#include #include #include #include +#include /** * Test context. @@ -90,6 +92,18 @@ static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) { } int main(int argc, char *argv[]) { + // Try to set a UTF-8 locale + if (!setlocale(LC_ALL, "C.UTF-8")) { + setlocale(LC_ALL, ""); + } + + // Run tests in UTC + if (setenv("TZ", "UTC0", true) != 0) { + perror("setenv()"); + return EXIT_FAILURE; + } + tzset(); + struct test_ctx ctx; if (test_init(&ctx, argc, argv) != 0) { goto done; diff --git a/tests/xtime.c b/tests/xtime.c index f853428..2609c1c 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -81,8 +81,8 @@ static bool check_xgetdate(void) { /** Check one xmktime() result. */ static bool check_one_xmktime(time_t expected) { struct tm tm; - if (xlocaltime(&expected, &tm) != 0) { - bfs_diag("xlocaltime(%jd): %s", (intmax_t)expected, xstrerror(errno)); + if (!localtime_r(&expected, &tm)) { + bfs_diag("localtime_r(%jd): %s", (intmax_t)expected, xstrerror(errno)); return false; } @@ -145,12 +145,6 @@ static bool check_xtimegm(void) { } bool check_xtime(void) { - if (setenv("TZ", "UTC0", true) != 0) { - perror("setenv()"); - return false; - } - tzset(); - bool ret = true; ret &= check_xgetdate(); ret &= check_xmktime(); diff --git a/tests/xtouch.c b/tests/xtouch.c index 6099128..8c5c5f3 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -157,6 +157,8 @@ done: } int main(int argc, char *argv[]) { + tzset(); + mode_t mask = umask(0); struct args args = { -- cgit v1.2.3 From f9e9e5f9013cc2e9d238cbcad9a1dfaf3a529aac Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sun, 10 Mar 2024 11:39:34 -0400 Subject: tests/xtime: Add tests for integer overflow --- src/xtime.c | 4 +++- tests/xtime.c | 28 ++++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/xtime.c b/src/xtime.c index 8100f6c..05f0e1a 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -2,8 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "xtime.h" -#include "atomic.h" +#include "bfstd.h" #include "config.h" +#include "diag.h" #include #include #include @@ -19,6 +20,7 @@ int xmktime(struct tm *tm, time_t *timep) { struct tm tmp; if (!localtime_r(timep, &tmp)) { + bfs_bug("localtime_r(-1): %s", xstrerror(errno)); return -1; } diff --git a/tests/xtime.c b/tests/xtime.c index 2609c1c..3f1fec2 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -99,6 +99,19 @@ static bool check_xmktime(void) { ret &= check_one_xmktime(time); } + // Attempt to trigger overflow (but don't test for it, since it's not mandatory) + struct tm tm = { + .tm_year = INT_MAX, + .tm_mon = INT_MAX, + .tm_mday = INT_MAX, + .tm_hour = INT_MAX, + .tm_min = INT_MAX, + .tm_sec = INT_MAX, + .tm_isdst = -1, + }; + time_t time; + xmktime(&tm, &time); + return ret; } @@ -131,7 +144,9 @@ static bool check_xtimegm(void) { struct tm tm = { .tm_isdst = -1, }; + time_t time; + // Check equivalence with mktime() for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) for (tm.tm_mon = -3; tm.tm_mon <= 15; tm.tm_mon += 3) for (tm.tm_mday = -31; tm.tm_mday <= 61; tm.tm_mday += 4) @@ -141,6 +156,19 @@ static bool check_xtimegm(void) { ret &= check_one_xtimegm(&tm); } + // Check integer overflow cases + tm = (struct tm){ .tm_sec = INT_MAX, .tm_min = INT_MAX }; + ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); + + tm = (struct tm){ .tm_min = INT_MAX, .tm_hour = INT_MAX }; + ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); + + tm = (struct tm){ .tm_hour = INT_MAX, .tm_mday = INT_MAX }; + ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); + + tm = (struct tm){ .tm_mon = INT_MAX, .tm_year = INT_MAX }; + ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); + return ret; } -- cgit v1.2.3 From 2c3ef3a06ee1f951f6d68be6d0d3f6a1822b05b7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 11 Mar 2024 09:51:03 -0400 Subject: Re-run include-what-you-use --- src/alloc.h | 1 + src/bfstd.c | 6 +++--- src/bftw.c | 1 + src/color.h | 1 - src/ctx.c | 1 + src/ctx.h | 2 ++ src/diag.c | 2 +- src/dir.h | 4 ++-- src/dstring.c | 2 ++ src/eval.c | 4 ++-- src/expr.c | 4 ++-- src/expr.h | 1 - src/ioq.c | 5 +++-- src/main.c | 1 + src/opt.c | 3 ++- src/parse.c | 3 +-- src/printf.c | 3 ++- src/trie.c | 2 -- src/trie.h | 1 - src/xspawn.c | 1 - src/xtime.c | 1 - tests/alloc.c | 1 + tests/bfstd.c | 3 --- tests/bit.c | 2 +- tests/ioq.c | 2 ++ tests/main.c | 3 --- tests/xtime.c | 4 ++-- tests/xtouch.c | 1 + 28 files changed, 33 insertions(+), 32 deletions(-) (limited to 'tests') diff --git a/src/alloc.h b/src/alloc.h index 60dd738..ae055bc 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -10,6 +10,7 @@ #include "config.h" #include +#include #include /** Check if a size is properly aligned. */ diff --git a/src/bfstd.c b/src/bfstd.c index ce4aa49..c6c2e7f 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -2,18 +2,19 @@ // SPDX-License-Identifier: 0BSD #include "bfstd.h" -#include "bit.h" #include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" #include "xregex.h" -#include #include #include #include +#include #include #include +#include +#include #include #include #include @@ -24,7 +25,6 @@ #include #include #include -#include #if BFS_USE_SYS_SYSMACROS_H # include diff --git a/src/bftw.c b/src/bftw.c index 6f52299..50b8b02 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -35,6 +35,7 @@ #include #include #include +#include /** Initialize a bftw_stat cache. */ static void bftw_stat_init(struct bftw_stat *bufs, struct bfs_stat *stat_buf, struct bfs_stat *lstat_buf) { diff --git a/src/color.h b/src/color.h index 85633a4..e3e7973 100644 --- a/src/color.h +++ b/src/color.h @@ -10,7 +10,6 @@ #include "config.h" #include "dstring.h" -#include #include /** diff --git a/src/ctx.c b/src/ctx.c index 6c84f75..f5b28c7 100644 --- a/src/ctx.c +++ b/src/ctx.c @@ -6,6 +6,7 @@ #include "color.h" #include "diag.h" #include "expr.h" +#include "list.h" #include "mtab.h" #include "pwcache.h" #include "stat.h" diff --git a/src/ctx.h b/src/ctx.h index aa91f2c..e14db21 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -18,6 +18,8 @@ #include #include +struct CFILE; + /** * The execution context for bfs. */ diff --git a/src/diag.c b/src/diag.c index 656fa89..cb27b92 100644 --- a/src/diag.c +++ b/src/diag.c @@ -11,8 +11,8 @@ #include "expr.h" #include #include +#include #include -#include /** bfs_diagf() implementation. */ attr(printf(2, 0)) diff --git a/src/dir.h b/src/dir.h index b11d454..18d907e 100644 --- a/src/dir.h +++ b/src/dir.h @@ -8,8 +8,6 @@ #ifndef BFS_DIR_H #define BFS_DIR_H -#include "alloc.h" -#include "config.h" #include /** @@ -78,6 +76,8 @@ struct bfs_dirent { */ struct bfs_dir *bfs_allocdir(void); +struct arena; + /** * Initialize an arena for directories. * diff --git a/src/dstring.c b/src/dstring.c index bc18308..10b0fad 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -7,6 +7,8 @@ #include "config.h" #include "diag.h" #include +#include +#include #include #include #include diff --git a/src/eval.c b/src/eval.c index 1711001..9e55964 100644 --- a/src/eval.c +++ b/src/eval.c @@ -25,7 +25,6 @@ #include "stat.h" #include "trie.h" #include "xregex.h" -#include "xtime.h" #include #include #include @@ -36,8 +35,9 @@ #include #include #include +#include #include -#include +#include #include #include #include diff --git a/src/expr.c b/src/expr.c index 3e0033f..5784220 100644 --- a/src/expr.c +++ b/src/expr.c @@ -4,12 +4,12 @@ #include "expr.h" #include "alloc.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "exec.h" +#include "list.h" #include "printf.h" #include "xregex.h" -#include -#include #include struct bfs_expr *bfs_expr_new(struct bfs_ctx *ctx, bfs_eval_fn *eval_fn, size_t argc, char **argv) { diff --git a/src/expr.h b/src/expr.h index 957b04a..75cb5fd 100644 --- a/src/expr.h +++ b/src/expr.h @@ -12,7 +12,6 @@ #include "config.h" #include "eval.h" #include "stat.h" -#include #include #include diff --git a/src/ioq.c b/src/ioq.c index 00c3b86..37eed7d 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -126,13 +126,14 @@ #include "config.h" #include "diag.h" #include "dir.h" -#include "sanity.h" #include "stat.h" #include "thread.h" -#include #include +#include #include +#include #include +#include #if BFS_USE_LIBURING # include diff --git a/src/main.c b/src/main.c index 16a2576..e120f03 100644 --- a/src/main.c +++ b/src/main.c @@ -48,6 +48,7 @@ #include "bfstd.h" #include "config.h" #include "ctx.h" +#include "diag.h" #include "eval.h" #include "parse.h" #include diff --git a/src/opt.c b/src/opt.c index 74145ac..76965de 100644 --- a/src/opt.c +++ b/src/opt.c @@ -26,11 +26,13 @@ */ #include "opt.h" +#include "bftw.h" #include "bit.h" #include "color.h" #include "config.h" #include "ctx.h" #include "diag.h" +#include "dir.h" #include "eval.h" #include "exec.h" #include "expr.h" @@ -40,7 +42,6 @@ #include #include #include -#include #include static char *fake_and_arg = "-and"; diff --git a/src/parse.c b/src/parse.c index b26a50f..3b7386d 100644 --- a/src/parse.c +++ b/src/parse.c @@ -42,8 +42,7 @@ #include #include #include -#include -#include +#include #include #include diff --git a/src/printf.c b/src/printf.c index 34ed606..487f039 100644 --- a/src/printf.c +++ b/src/printf.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "printf.h" +#include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" @@ -14,10 +15,10 @@ #include "mtab.h" #include "pwcache.h" #include "stat.h" -#include "xtime.h" #include #include #include +#include #include #include #include diff --git a/src/trie.c b/src/trie.c index bd5300d..1ffb23a 100644 --- a/src/trie.c +++ b/src/trie.c @@ -87,9 +87,7 @@ #include "config.h" #include "diag.h" #include "list.h" -#include #include -#include #include bfs_static_assert(CHAR_WIDTH == 8); diff --git a/src/trie.h b/src/trie.h index 02088f1..4288d76 100644 --- a/src/trie.h +++ b/src/trie.h @@ -5,7 +5,6 @@ #define BFS_TRIE_H #include "alloc.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.c b/src/xspawn.c index 8d6108b..6a94d3d 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -12,7 +12,6 @@ #include #include #include -#include #include #if BFS_USE_PATHS_H diff --git a/src/xtime.c b/src/xtime.c index 05f0e1a..5b259ab 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -7,7 +7,6 @@ #include "diag.h" #include #include -#include #include #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 4ce23d4..9f08111 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -3,6 +3,7 @@ #include "tests.h" #include "../src/alloc.h" +#include "../src/config.h" #include "../src/diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 0ded5de..26abdb6 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -7,9 +7,6 @@ #include "../src/diag.h" #include #include -#include -#include -#include #include #include diff --git a/tests/bit.c b/tests/bit.c index b944748..3d66ce3 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -3,10 +3,10 @@ #include "tests.h" #include "../src/bit.h" +#include "../src/config.h" #include "../src/diag.h" #include #include -#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); diff --git a/tests/ioq.c b/tests/ioq.c index 56e1886..1ce8f75 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -4,10 +4,12 @@ #include "tests.h" #include "../src/ioq.h" #include "../src/bfstd.h" +#include "../src/config.h" #include "../src/diag.h" #include "../src/dir.h" #include #include +#include /** * Test for blocking within ioq_slot_push(). diff --git a/tests/main.c b/tests/main.c index 38438b2..8849e8c 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,11 +6,8 @@ */ #include "tests.h" -#include "../src/bfstd.h" #include "../src/color.h" #include "../src/config.h" -#include "../src/diag.h" -#include #include #include #include diff --git a/tests/xtime.c b/tests/xtime.c index 3f1fec2..c8dc00b 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -5,10 +5,10 @@ #include "../src/xtime.h" #include "../src/bfstd.h" #include "../src/config.h" +#include "../src/diag.h" #include +#include #include -#include -#include #include static bool tm_equal(const struct tm *tma, const struct tm *tmb) { diff --git a/tests/xtouch.c b/tests/xtouch.c index 8c5c5f3..fad272f 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -11,6 +11,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 912d2b94cf6ff0871c07325af5ed520a2bc97722 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Mar 2024 10:44:34 -0400 Subject: Implement -limit N Closes: https://github.com/tavianator/bfs/issues/133 --- completions/bfs.bash | 3 +-- completions/bfs.fish | 1 + completions/bfs.zsh | 1 + docs/USAGE.md | 34 ++++++++++++++++++++++++++++++++++ docs/bfs.1 | 5 +++++ src/eval.c | 13 +++++++++++++ src/eval.h | 1 + src/opt.c | 1 + src/parse.c | 38 +++++++++++++++++++++++++++++++++++++- tests/bfs/limit.out | 4 ++++ tests/bfs/limit.sh | 1 + tests/bfs/limit_0.sh | 1 + tests/bfs/limit_implicit_print.sh | 1 + tests/bfs/limit_incomplete.sh | 1 + tests/bfs/limit_one.sh | 1 + 15 files changed, 103 insertions(+), 3 deletions(-) create mode 100644 tests/bfs/limit.out create mode 100644 tests/bfs/limit.sh create mode 100644 tests/bfs/limit_0.sh create mode 100644 tests/bfs/limit_implicit_print.sh create mode 100644 tests/bfs/limit_incomplete.sh create mode 100644 tests/bfs/limit_one.sh (limited to 'tests') diff --git a/completions/bfs.bash b/completions/bfs.bash index 2f52e8d..db582da 100644 --- a/completions/bfs.bash +++ b/completions/bfs.bash @@ -37,6 +37,7 @@ _bfs() { -ipath -iregex -iwholename + -limit -links -lname -maxdepth @@ -94,8 +95,6 @@ _bfs() { -depth -follow -ignore_readdir_race - -maxdepth - -mindepth -mount -nocolor -noignore_readdir_race diff --git a/completions/bfs.fish b/completions/bfs.fish index 3f399e7..1303639 100644 --- a/completions/bfs.fish +++ b/completions/bfs.fish @@ -133,6 +133,7 @@ complete -c bfs -o fls -d "Like -ls, but write to specified file" -F complete -c bfs -o fprint -d "Like -print, but write to specified file" -F complete -c bfs -o fprint0 -d "Like -print0, but write to specified file" -F complete -c bfs -o fprintf -d "Like -printf, but write to specified file" -F +complete -c bfs -o limit -d "Limit the number of results" -x complete -c bfs -o ls -d "List files like ls -dils" complete -c bfs -o print -d "Print the path to the found file" complete -c bfs -o print0 -d "Like -print, but use the null character as a separator rather than newlines" diff --git a/completions/bfs.zsh b/completions/bfs.zsh index 3d7dc3a..51b5029 100644 --- a/completions/bfs.zsh +++ b/completions/bfs.zsh @@ -133,6 +133,7 @@ args=( '*-fprint0[print the path to the found file using null character as separator, but write to FILE instead of standard output]:output file:_files' '*-fprintf[print according to format string, but write to FILE instead of standard output]:output file:_files:output format' + '*-limit[quit after N results]:maximum result count' '*-ls[list files like ls -dils]' '*-print[print the path to the found file]' '*-print0[print the path to the found file using null character as separator]' diff --git a/docs/USAGE.md b/docs/USAGE.md index 3efdee0..071c95b 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -130,6 +130,40 @@ Unlike `-prune`, `-exclude` even works in combination with `-depth`/`-delete`. --- +### `-limit` + +The `-limit N` action makes `bfs` quit once it gets evaluated `N` times. +Placing it after an action like `-print` limits the number of results that get printed, for example: + +```console +$ bfs -s -type f -name '*.txt' +./1.txt +./2.txt +./3.txt +./4.txt +$ bfs -s -type f -name '*.txt' -print -limit 2 +./1.txt +./2.txt +``` + +This is similar to + +```console +$ bfs -s -type f -name '*.txt' | head -n2 +``` + +but more powerful because you can apply separate limits to different expressions: + +```console +$ bfs \( -name '*.txt' -print -limit 3 -o -name '*.log' -print -limit 4 \) -limit 5 +[At most 3 .txt files, at most 4 .log files, and at most 5 in total] +``` + +and more efficient because it will quit immediately. +When piping to `head`, `bfs` will only quit *after* it tries to output too many results. + +--- + ### `-hidden`/`-nohidden` `-hidden` matches "hidden" files (dotfiles). diff --git a/docs/bfs.1 b/docs/bfs.1 index 2ecb891..3a4f15a 100644 --- a/docs/bfs.1 +++ b/docs/bfs.1 @@ -725,6 +725,11 @@ but write to instead of standard output. .RE .TP +\fB\-limit \fIN\fR +Quit once this action is evaluated +.I N +times. +.TP .B \-ls List files like .B ls diff --git a/src/eval.c b/src/eval.c index 9e55964..2f06858 100644 --- a/src/eval.c +++ b/src/eval.c @@ -840,6 +840,19 @@ error: return true; } +/** + * -limit action. + */ +bool eval_limit(const struct bfs_expr *expr, struct bfs_eval *state) { + long long evals = expr->evaluations + 1; + if (evals >= expr->num) { + state->action = BFTW_STOP; + state->quit = true; + } + + return true; +} + /** * -prune action. */ diff --git a/src/eval.h b/src/eval.h index 98bbc08..f7f6c77 100644 --- a/src/eval.h +++ b/src/eval.h @@ -88,6 +88,7 @@ bool eval_fprint(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_fprint0(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_fprintf(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_fprintx(const struct bfs_expr *expr, struct bfs_eval *state); +bool eval_limit(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_prune(const struct bfs_expr *expr, struct bfs_eval *state); bool eval_quit(const struct bfs_expr *expr, struct bfs_eval *state); diff --git a/src/opt.c b/src/opt.c index 76965de..a470d25 100644 --- a/src/opt.c +++ b/src/opt.c @@ -1181,6 +1181,7 @@ static struct bfs_expr *annotate_visit(struct bfs_opt *opt, struct bfs_expr *exp eval_fprint0, eval_fprintf, eval_fprintx, + eval_limit, eval_prune, eval_true, // Non-returning diff --git a/src/parse.c b/src/parse.c index 3b7386d..2dfcab2 100644 --- a/src/parse.c +++ b/src/parse.c @@ -97,6 +97,8 @@ struct bfs_parser { char **last_arg; /** A "-depth"-type argument, if any. */ char **depth_arg; + /** A "-limit" argument, if any. */ + char **limit_arg; /** A "-prune" argument, if any. */ char **prune_arg; /** A "-mount" argument, if any. */ @@ -733,7 +735,7 @@ static struct bfs_expr *parse_action(struct bfs_parser *parser, bfs_eval_fn *eva return NULL; } - if (eval_fn != eval_prune && eval_fn != eval_quit) { + if (eval_fn != eval_limit && eval_fn != eval_prune && eval_fn != eval_quit) { parser->implicit_print = false; } @@ -1569,6 +1571,29 @@ static struct bfs_expr *parse_jobs(struct bfs_parser *parser, int arg1, int arg2 return expr; } +/** + * Parse -limit N. + */ +static struct bfs_expr *parse_limit(struct bfs_parser *parser, int arg1, int arg2) { + struct bfs_expr *expr = parse_unary_action(parser, eval_limit); + if (!expr) { + return NULL; + } + + char **arg = &expr->argv[1]; + if (!parse_int(parser, arg, *arg, &expr->num, IF_LONG_LONG)) { + return NULL; + } + + if (expr->num <= 0) { + parse_expr_error(parser, expr, "The ${blu}%s${rs} must be at least ${bld}1${rs}.\n", expr->argv[0]); + return NULL; + } + + parser->limit_arg = expr->argv; + return expr; +} + /** * Parse -links N. */ @@ -2845,6 +2870,8 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 cfprintf(cout, " ${blu}-fprintf${rs} ${bld}FILE${rs} ${bld}FORMAT${rs}\n"); cfprintf(cout, " Like ${blu}-ls${rs}/${blu}-print${rs}/${blu}-print0${rs}/${blu}-printf${rs}, but write to ${bld}FILE${rs} instead of standard\n" " output\n"); + cfprintf(cout, " ${blu}-limit${rs} ${bld}N${rs}\n"); + cfprintf(cout, " Quit after this action is evaluated ${bld}N${rs} times\n"); cfprintf(cout, " ${blu}-ls${rs}\n"); cfprintf(cout, " List files like ${ex}ls${rs} ${bld}-dils${rs}\n"); cfprintf(cout, " ${blu}-print${rs}\n"); @@ -2968,6 +2995,7 @@ static const struct table_entry parse_table[] = { {"-iregex", T_TEST, parse_regex, BFS_REGEX_ICASE}, {"-iwholename", T_TEST, parse_path, true}, {"-j", T_FLAG, parse_jobs, 0, 0, true}, + {"-limit", T_ACTION, parse_limit}, {"-links", T_TEST, parse_links}, {"-lname", T_TEST, parse_lname, false}, {"-ls", T_ACTION, parse_ls}, @@ -3330,6 +3358,14 @@ static struct bfs_expr *parse_whole_expr(struct bfs_parser *parser) { } if (parser->implicit_print) { + char **limit = parser->limit_arg; + if (limit) { + parse_argv_error(parser, parser->limit_arg, 2, + "With ${blu}%s${rs}, you must specify an action explicitly; for example, ${blu}-print${rs} ${blu}%s${rs} ${bld}%s${rs}.\n", + limit[0], limit[0], limit[1]); + return NULL; + } + struct bfs_expr *print = parse_new_expr(parser, eval_fprint, 1, &fake_print_arg); if (!print) { return NULL; diff --git a/tests/bfs/limit.out b/tests/bfs/limit.out new file mode 100644 index 0000000..ea94276 --- /dev/null +++ b/tests/bfs/limit.out @@ -0,0 +1,4 @@ +basic/a +basic/b +basic/c/d +basic/e/f diff --git a/tests/bfs/limit.sh b/tests/bfs/limit.sh new file mode 100644 index 0000000..84b605f --- /dev/null +++ b/tests/bfs/limit.sh @@ -0,0 +1 @@ +bfs_diff -s basic -type f -print -limit 4 diff --git a/tests/bfs/limit_0.sh b/tests/bfs/limit_0.sh new file mode 100644 index 0000000..3ce26de --- /dev/null +++ b/tests/bfs/limit_0.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit 0 diff --git a/tests/bfs/limit_implicit_print.sh b/tests/bfs/limit_implicit_print.sh new file mode 100644 index 0000000..cdb059d --- /dev/null +++ b/tests/bfs/limit_implicit_print.sh @@ -0,0 +1 @@ +! invoke_bfs basic -type f -limit 1 diff --git a/tests/bfs/limit_incomplete.sh b/tests/bfs/limit_incomplete.sh new file mode 100644 index 0000000..2d1e842 --- /dev/null +++ b/tests/bfs/limit_incomplete.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit diff --git a/tests/bfs/limit_one.sh b/tests/bfs/limit_one.sh new file mode 100644 index 0000000..3f8181c --- /dev/null +++ b/tests/bfs/limit_one.sh @@ -0,0 +1 @@ +! invoke_bfs basic -print -limit one -- cgit v1.2.3 From 54490a29006529f7ceb4dc0514a075f9f4175621 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 20 Mar 2024 16:56:46 -0400 Subject: bfstd: Check that wcwidth() is positive wcwidth() returns -1 for non-printable characters, but terminals typically don't print anything for them, so treat them as 0. --- src/bfstd.c | 8 ++++++-- tests/bfstd.c | 3 +++ 2 files changed, 9 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index c6c2e7f..5b64452 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -695,8 +695,12 @@ size_t xstrwidth(const char *str) { if (wc == WEOF) { // Assume a single-width '?' ++ret; - } else { - ret += xwcwidth(wc); + continue; + } + + int width = xwcwidth(wc); + if (width > 0) { + ret += width; } } diff --git a/tests/bfstd.c b/tests/bfstd.c index 26abdb6..1351e11 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -69,5 +69,8 @@ bool check_bfstd(void) { ret &= check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); } + ret &= bfs_check(xstrwidth("Hello world") == 11); + ret &= bfs_check(xstrwidth("Hello\1world") == 10); + return ret; } -- cgit v1.2.3 From ef0aef16961508075249a6206dc0e9a9ac27d81c Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 21 Mar 2024 11:52:31 -0400 Subject: bit: Check __BYTE_ORDER__ for the native endian __ORDER_NATIVE_ENDIAN__ is not a thing. --- src/bit.h | 4 ++-- tests/bit.c | 13 +++++++++++++ 2 files changed, 15 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/src/bit.h b/src/bit.h index c469305..69df21e 100644 --- a/src/bit.h +++ b/src/bit.h @@ -167,8 +167,8 @@ #ifdef __STDC_ENDIAN_NATIVE__ # define ENDIAN_NATIVE __STDC_ENDIAN_NATIVE__ -#elif defined(__ORDER_NATIVE_ENDIAN__) -# define ENDIAN_NATIVE __ORDER_NATIVE_ENDIAN__ +#elif defined(__BYTE_ORDER__) +# define ENDIAN_NATIVE __BYTE_ORDER__ #else # define ENDIAN_NATIVE 0 #endif diff --git a/tests/bit.c b/tests/bit.c index 3d66ce3..6548c30 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -7,6 +7,7 @@ #include "../src/diag.h" #include #include +#include bfs_static_assert(UMAX_WIDTH(0x1) == 1); bfs_static_assert(UMAX_WIDTH(0x3) == 2); @@ -58,6 +59,18 @@ bfs_static_assert(INTMAX_MAX == IWIDTH_MAX(INTMAX_WIDTH)); bool check_bit(void) { bool ret = true; + const char *str = "\x1\x2\x3\x4"; + uint32_t word; + memcpy(&word, str, sizeof(word)); + +#if ENDIAN_NATIVE == ENDIAN_LITTLE + ret &= check_eq(word, 0x04030201); +#elif ENDIAN_NATIVE == ENDIAN_BIG + ret &= check_eq(word, 0x01020304); +#else +# warning "Skipping byte order tests on mixed/unknown-endian machine" +#endif + ret &= check_eq(bswap((uint8_t)0x12), 0x12); ret &= check_eq(bswap((uint16_t)0x1234), 0x3412); ret &= check_eq(bswap((uint32_t)0x12345678), 0x78563412); -- cgit v1.2.3 From a2a6ac8edd5d85398f6edb6afd02e683d9da9e7b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 21 Mar 2024 11:55:38 -0400 Subject: bfstd: New asciilen() function --- src/bfstd.c | 68 +++++++++++++++++++++++++++++++++++++---------------------- src/bfstd.h | 15 +++++++++++++ tests/bfstd.c | 15 +++++++++++++ 3 files changed, 73 insertions(+), 25 deletions(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index 5b64452..43513b8 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -2,6 +2,7 @@ // SPDX-License-Identifier: 0BSD #include "bfstd.h" +#include "bit.h" #include "config.h" #include "diag.h" #include "sanity.h" @@ -670,6 +671,44 @@ int xstrtofflags(const char **str, unsigned long long *set, unsigned long long * #endif } +size_t asciilen(const char *str) { + return asciinlen(str, strlen(str)); +} + +size_t asciinlen(const char *str, size_t n) { + size_t i = 0; + +#if SIZE_WIDTH % 8 == 0 + // Word-at-a-time isascii() + for (size_t word; i + sizeof(word) <= n; i += sizeof(word)) { + memcpy(&word, str + i, sizeof(word)); + + const size_t mask = (SIZE_MAX / 0xFF) << 7; // 0x808080... + word &= mask; + if (!word) { + continue; + } + +#if ENDIAN_NATIVE == ENDIAN_BIG + word = bswap(word); +#elif ENDIAN_NATIVE != ENDIAN_LITTLE + break; +#endif + + size_t first = trailing_zeros(word) / 8; + return i + first; + } +#endif + + for (; i < n; ++i) { + if (!xisascii(str[i])) { + break; + } + } + + return i; +} + wint_t xmbrtowc(const char *str, size_t *i, size_t len, mbstate_t *mb) { wchar_t wc; size_t mblen = mbrtowc(&wc, str + *i, len - *i, mb); @@ -765,36 +804,15 @@ static size_t printable_len(const char *str, size_t len, enum wesc_flags flags) invoke_once(&once, char_cache_init); // Fast path: avoid multibyte checks - size_t i, word; - for (i = 0; i + sizeof(word) <= len;) { - // Word-at-a-time isascii() - memcpy(&word, str + i, sizeof(word)); - // 0xFFFF... / 0xFF == 0x10101... - size_t mask = (SIZE_MAX / 0xFF) << 7; - if (word & mask) { - goto multibyte; - } - - for (size_t j = 0; j < sizeof(word); ++i, ++j) { - if (!wesc_isprint(str[i], flags)) { - return i; - } - } - } - - for (; i < len; ++i) { - char c = str[i]; - if (!xisascii(c)) { - goto multibyte; - } - if (!wesc_isprint(c, flags)) { + size_t asclen = asciinlen(str, len); + size_t i; + for (i = 0; i < asclen; ++i) { + if (!wesc_isprint(str[i], flags)) { return i; } } -multibyte:; mbstate_t mb = {0}; - for (size_t j = i; i < len; i = j) { wint_t wc = xmbrtowc(str, &j, len, &mb); if (wc == WEOF) { diff --git a/src/bfstd.h b/src/bfstd.h index d160c88..fc22971 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -177,6 +177,21 @@ int ynprompt(void); // #include +/** + * Get the length of the pure-ASCII prefix of a string. + */ +size_t asciilen(const char *str); + +/** + * Get the length of the pure-ASCII prefix of a string. + * + * @param str + * The string to check. + * @param n + * The maximum prefix length. + */ +size_t asciinlen(const char *str, size_t n); + /** * Allocate a copy of a region of memory. * diff --git a/tests/bfstd.c b/tests/bfstd.c index 1351e11..3ffab41 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -40,6 +40,21 @@ static bool check_wordesc(const char *str, const char *exp, enum wesc_flags flag bool check_bfstd(void) { bool ret = true; + ret &= bfs_check(asciilen("") == 0); + ret &= bfs_check(asciilen("@") == 1); + ret &= bfs_check(asciilen("@@") == 2); + ret &= bfs_check(asciilen("\xFF@") == 0); + ret &= bfs_check(asciilen("@\xFF") == 1); + ret &= bfs_check(asciilen("@@@@@@@@") == 8); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@") == 16); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@@@@@@@@@") == 24); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@a\xFF@@@@@@@") == 15); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@\xFF@@@@@@@") == 16); + ret &= bfs_check(asciilen("@@@@@@@@@@@@@@@@a\xFF@@@@@@") == 17); + ret &= bfs_check(asciilen("@@@@@@@\xFF@@@@@@a\xFF@@@@@@@") == 7); + ret &= bfs_check(asciilen("@@@@@@@@\xFF@@@@@a\xFF@@@@@@@") == 8); + ret &= bfs_check(asciilen("@@@@@@@@@\xFF@@@@a\xFF@@@@@@@") == 9); + // From man 3p basename ret &= check_base_dir("usr", ".", "usr"); ret &= check_base_dir("usr/", ".", "usr"); -- cgit v1.2.3 From 7e25b9c6e718437ed45aa2592598c63f0f87e70a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 26 Mar 2024 11:42:58 -0400 Subject: xtime: Don't update tm if xtimegm() overflows --- src/xtime.c | 62 +++++++++++++++++++++++++++++++---------------------------- tests/xtime.c | 34 ++++++++++++++++++++------------ 2 files changed, 55 insertions(+), 41 deletions(-) (limited to 'tests') diff --git a/src/xtime.c b/src/xtime.c index 5b259ab..bcf6dd3 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -69,73 +69,77 @@ static int month_length(int year, int month) { } int xtimegm(struct tm *tm, time_t *timep) { - tm->tm_isdst = 0; + struct tm copy = *tm; + copy.tm_isdst = 0; - if (wrap(&tm->tm_sec, 60, &tm->tm_min) != 0) { + if (wrap(©.tm_sec, 60, ©.tm_min) != 0) { goto overflow; } - if (wrap(&tm->tm_min, 60, &tm->tm_hour) != 0) { + if (wrap(©.tm_min, 60, ©.tm_hour) != 0) { goto overflow; } - if (wrap(&tm->tm_hour, 24, &tm->tm_mday) != 0) { + if (wrap(©.tm_hour, 24, ©.tm_mday) != 0) { goto overflow; } // In order to wrap the days of the month, we first need to know what // month it is - if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) { + if (wrap(©.tm_mon, 12, ©.tm_year) != 0) { goto overflow; } - if (tm->tm_mday < 1) { + if (copy.tm_mday < 1) { do { - --tm->tm_mon; - if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) { + --copy.tm_mon; + if (wrap(©.tm_mon, 12, ©.tm_year) != 0) { goto overflow; } - tm->tm_mday += month_length(tm->tm_year, tm->tm_mon); - } while (tm->tm_mday < 1); + copy.tm_mday += month_length(copy.tm_year, copy.tm_mon); + } while (copy.tm_mday < 1); } else { while (true) { - int days = month_length(tm->tm_year, tm->tm_mon); - if (tm->tm_mday <= days) { + int days = month_length(copy.tm_year, copy.tm_mon); + if (copy.tm_mday <= days) { break; } - tm->tm_mday -= days; - ++tm->tm_mon; - if (wrap(&tm->tm_mon, 12, &tm->tm_year) != 0) { + copy.tm_mday -= days; + ++copy.tm_mon; + if (wrap(©.tm_mon, 12, ©.tm_year) != 0) { goto overflow; } } } - tm->tm_yday = 0; - for (int i = 0; i < tm->tm_mon; ++i) { - tm->tm_yday += month_length(tm->tm_year, i); + copy.tm_yday = 0; + for (int i = 0; i < copy.tm_mon; ++i) { + copy.tm_yday += month_length(copy.tm_year, i); } - tm->tm_yday += tm->tm_mday - 1; + copy.tm_yday += copy.tm_mday - 1; int leap_days; // Compute floor((year - 69)/4) - floor((year - 1)/100) + floor((year + 299)/400) without overflows - if (tm->tm_year >= 0) { - leap_days = floor_div(tm->tm_year - 69, 4) - floor_div(tm->tm_year - 1, 100) + floor_div(tm->tm_year - 101, 400) + 1; + if (copy.tm_year >= 0) { + leap_days = floor_div(copy.tm_year - 69, 4) - floor_div(copy.tm_year - 1, 100) + floor_div(copy.tm_year - 101, 400) + 1; } else { - leap_days = floor_div(tm->tm_year + 3, 4) - floor_div(tm->tm_year + 99, 100) + floor_div(tm->tm_year + 299, 400) - 17; + leap_days = floor_div(copy.tm_year + 3, 4) - floor_div(copy.tm_year + 99, 100) + floor_div(copy.tm_year + 299, 400) - 17; } - long long epoch_days = 365LL * (tm->tm_year - 70) + leap_days + tm->tm_yday; - tm->tm_wday = (epoch_days + 4) % 7; - if (tm->tm_wday < 0) { - tm->tm_wday += 7; + long long epoch_days = 365LL * (copy.tm_year - 70) + leap_days + copy.tm_yday; + copy.tm_wday = (epoch_days + 4) % 7; + if (copy.tm_wday < 0) { + copy.tm_wday += 7; } - long long epoch_time = tm->tm_sec + 60 * (tm->tm_min + 60 * (tm->tm_hour + 24 * epoch_days)); - *timep = (time_t)epoch_time; - if ((long long)*timep != epoch_time) { + long long epoch_time = copy.tm_sec + 60 * (copy.tm_min + 60 * (copy.tm_hour + 24 * epoch_days)); + time_t time = (time_t)epoch_time; + if ((long long)time != epoch_time) { goto overflow; } + + *tm = copy; + *timep = time; return 0; overflow: diff --git a/tests/xtime.c b/tests/xtime.c index c8dc00b..ec499d8 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -137,6 +137,24 @@ static bool check_one_xtimegm(const struct tm *tm) { return ret; } +/** Check an overflowing xtimegm() call. */ +static bool check_xtimegm_overflow(const struct tm *tm) { + struct tm copy = *tm; + time_t time = 123; + + bool ret = true; + ret &= bfs_check(xtimegm(©, &time) == -1 && errno == EOVERFLOW); + ret &= bfs_check(tm_equal(©, tm)); + ret &= bfs_check(time == 123); + + if (!ret) { + bfs_diag("xtimegm(): " TM_FORMAT, TM_PRINTF(copy)); + bfs_diag("(input): " TM_FORMAT, TM_PRINTF(*tm)); + } + + return ret; +} + /** xtimegm() tests. */ static bool check_xtimegm(void) { bool ret = true; @@ -144,7 +162,6 @@ static bool check_xtimegm(void) { struct tm tm = { .tm_isdst = -1, }; - time_t time; // Check equivalence with mktime() for (tm.tm_year = 10; tm.tm_year <= 200; tm.tm_year += 10) @@ -157,17 +174,10 @@ static bool check_xtimegm(void) { } // Check integer overflow cases - tm = (struct tm){ .tm_sec = INT_MAX, .tm_min = INT_MAX }; - ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); - - tm = (struct tm){ .tm_min = INT_MAX, .tm_hour = INT_MAX }; - ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); - - tm = (struct tm){ .tm_hour = INT_MAX, .tm_mday = INT_MAX }; - ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); - - tm = (struct tm){ .tm_mon = INT_MAX, .tm_year = INT_MAX }; - ret &= bfs_check(xtimegm(&tm, &time) == -1 && errno == EOVERFLOW); + check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); + check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); + check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); + check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); return ret; } -- cgit v1.2.3 From 310bc847bca9e2457700598be99390867fd2a84e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 26 Mar 2024 12:18:50 -0400 Subject: bfstd: Escape ASCII tildes MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The POSIX spec [1] lists some characters that may need to be escaped. Unfortunately, the document uses Ëœ (U+02DC SMALL TILDE) instead of ~ (U+007E TILDE), and I copy-pasted from it. [1]: https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02 --- src/bfstd.c | 2 +- tests/bfstd.c | 2 ++ 2 files changed, 3 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/src/bfstd.c b/src/bfstd.c index 12af438..2499f00 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -908,7 +908,7 @@ static char *dollar_quote(char *dest, char *end, const char *str, size_t len, en /** How much of this string is safe as a bare word? */ static size_t bare_len(const char *str, size_t len) { // https://pubs.opengroup.org/onlinepubs/9699919799/utilities/V3_chap02.html#tag_18_02 - size_t ret = strcspn(str, "|&;<>()$`\\\"' *?[#Ëœ=%!{}"); + size_t ret = strcspn(str, "|&;<>()$`\\\"' *?[#~=%!{}"); return ret < len ? ret : len; } diff --git a/tests/bfstd.c b/tests/bfstd.c index 3ffab41..dc5ceaa 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -75,6 +75,7 @@ bool check_bfstd(void) { ret &= check_wordesc("\"word's\"", "'\"word'\\''s\"'", WESC_SHELL); ret &= check_wordesc("\033[1mbold's\033[0m", "$'\\e[1mbold\\'s\\e[0m'", WESC_SHELL | WESC_TTY); ret &= check_wordesc("\x7F", "$'\\x7F'", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("~user", "\"~user\"", WESC_SHELL); const char *charmap = nl_langinfo(CODESET); if (strcmp(charmap, "UTF-8") == 0) { @@ -82,6 +83,7 @@ bool check_bfstd(void) { ret &= check_wordesc("\xF0\x9F", "$'\\xF0\\x9F'", WESC_SHELL | WESC_TTY); ret &= check_wordesc("\xF0\x9F\x98", "$'\\xF0\\x9F\\x98'", WESC_SHELL | WESC_TTY); ret &= check_wordesc("\xF0\x9F\x98\x80", "\xF0\x9F\x98\x80", WESC_SHELL | WESC_TTY); + ret &= check_wordesc("\xCB\x9Cuser", "\xCB\x9Cuser", WESC_SHELL); } ret &= bfs_check(xstrwidth("Hello world") == 11); -- cgit v1.2.3 From 25769288f62816d733004c9e8347c35dd0e4ce2a Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Mar 2024 11:20:20 -0400 Subject: tests: New bfs_pcheck() macro to report xstrerror(errno) --- tests/main.c | 6 ++++++ tests/tests.h | 16 ++++++++++++++++ tests/xtime.c | 6 +++--- 3 files changed, 25 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/main.c b/tests/main.c index 8849e8c..7dec320 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,8 +6,10 @@ */ #include "tests.h" +#include "../src/bfstd.h" #include "../src/color.h" #include "../src/config.h" +#include #include #include #include @@ -88,6 +90,10 @@ static void run_test(struct test_ctx *ctx, const char *test, test_fn *fn) { } } +const char *bfs_errstr(void) { + return xstrerror(errno); +} + int main(int argc, char *argv[]) { // Try to set a UTF-8 locale if (!setlocale(LC_ALL, "C.UTF-8")) { diff --git a/tests/tests.h b/tests/tests.h index 6629dcf..b644450 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -51,4 +51,20 @@ static inline bool bfs_check(bool ret) { : "Check failed: `%s`%s", \ str, __VA_ARGS__), false)) +/** Get a string description of the last error. */ +const char *bfs_errstr(void); + +/** + * Check a condition, logging the current error string on failure. + */ +#define bfs_pcheck(...) \ + bfs_pcheck_(#__VA_ARGS__, __VA_ARGS__, "", "") + +#define bfs_pcheck_(str, cond, format, ...) \ + ((cond) ? true : (bfs_diag( \ + sizeof(format) > 1 \ + ? "%.0s" format "%s%s: %s" \ + : "Check failed: `%s`%s: %s", \ + str, __VA_ARGS__, bfs_errstr()), false)) + #endif // BFS_TESTS_H diff --git a/tests/xtime.c b/tests/xtime.c index ec499d8..f85402e 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -29,9 +29,9 @@ static bool check_one_xgetdate(const char *str, int error, time_t expected) { int ret = xgetdate(str, &ts); if (error) { - return bfs_check(ret == -1 && errno == error, "xgetdate('%s'): %s", str, xstrerror(errno)); + return bfs_pcheck(ret == -1 && errno == error, "xgetdate('%s')", str); } else { - return bfs_check(ret == 0, "xgetdate('%s'): %s", str, xstrerror(errno)) + return bfs_pcheck(ret == 0, "xgetdate('%s')", str) && bfs_check(ts.tv_sec == expected && ts.tv_nsec == 0, "xgetdate('%s'): %jd.%09jd != %jd", str, (intmax_t)ts.tv_sec, (intmax_t)ts.tv_nsec, (intmax_t)expected); @@ -87,7 +87,7 @@ static bool check_one_xmktime(time_t expected) { } time_t actual; - return bfs_check(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT "): %s", TM_PRINTF(tm), xstrerror(errno)) + return bfs_pcheck(xmktime(&tm, &actual) == 0, "xmktime(" TM_FORMAT ")", TM_PRINTF(tm)) && bfs_check(actual == expected, "xmktime(" TM_FORMAT "): %jd != %jd", TM_PRINTF(tm), (intmax_t)actual, (intmax_t)expected); } -- cgit v1.2.3 From bca78d2597c78b0166d76d023eee1354f5abd04e Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Mar 2024 11:27:01 -0400 Subject: tests/xspawn: New unit test --- GNUmakefile | 10 +++- tests/main.c | 1 + tests/tests.h | 3 ++ tests/xspawn.c | 154 +++++++++++++++++++++++++++++++++++++++++++++++++++++++ tests/xspawnee.c | 17 ++++++ 5 files changed, 183 insertions(+), 2 deletions(-) create mode 100644 tests/xspawn.c create mode 100644 tests/xspawnee.c (limited to 'tests') diff --git a/GNUmakefile b/GNUmakefile index 7dcd76f..080fb17 100644 --- a/GNUmakefile +++ b/GNUmakefile @@ -270,10 +270,15 @@ LIBBFS := \ $(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) # Testing utilities -TEST_UTILS := $(BIN)/tests/mksock $(BIN)/tests/xtouch +TEST_UTILS := \ + $(BIN)/tests/mksock \ + $(BIN)/tests/xspawnee \ + $(BIN)/tests/xtouch $(BIN)/tests/mksock: $(OBJ)/tests/mksock.o $(LIBBFS) +$(BIN)/tests/xspawnee: $(OBJ)/tests/xspawnee.o + $(BIN)/tests/xtouch: $(OBJ)/tests/xtouch.o $(LIBBFS) # All test binaries @@ -286,6 +291,7 @@ $(BIN)/tests/units: \ $(OBJ)/tests/ioq.o \ $(OBJ)/tests/main.o \ $(OBJ)/tests/trie.o \ + $(OBJ)/tests/xspawn.o \ $(OBJ)/tests/xtime.o \ $(LIBBFS) @@ -294,7 +300,7 @@ tests: $(TESTS) .PHONY: tests # Run the unit tests -unit-tests: $(BIN)/tests/units +unit-tests: $(BIN)/tests/units $(BIN)/tests/xspawnee $< .PHONY: unit-tests diff --git a/tests/main.c b/tests/main.c index 7dec320..69903d4 100644 --- a/tests/main.c +++ b/tests/main.c @@ -117,6 +117,7 @@ int main(int argc, char *argv[]) { run_test(&ctx, "bit", check_bit); run_test(&ctx, "ioq", check_ioq); run_test(&ctx, "trie", check_trie); + run_test(&ctx, "xspawn", check_xspawn); run_test(&ctx, "xtime", check_xtime); done: diff --git a/tests/tests.h b/tests/tests.h index b644450..351badb 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -29,6 +29,9 @@ bool check_ioq(void); /** Trie tests. */ bool check_trie(void); +/** Process spawning tests. */ +bool check_xspawn(void); + /** Time tests. */ bool check_xtime(void); diff --git a/tests/xspawn.c b/tests/xspawn.c new file mode 100644 index 0000000..e356842 --- /dev/null +++ b/tests/xspawn.c @@ -0,0 +1,154 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "tests.h" +#include "../src/alloc.h" +#include "../src/bfstd.h" +#include "../src/config.h" +#include "../src/dstring.h" +#include "../src/xspawn.h" +#include +#include +#include + +/** Duplicate the current environment. */ +static char **envdup(void) { + extern char **environ; + + char **envp = NULL; + size_t envc = 0; + + for (char **var = environ; ; ++var) { + char *copy = NULL; + if (*var) { + copy = strdup(*var); + if (!copy) { + goto fail; + } + } + + char **dest = RESERVE(char *, &envp, &envc); + if (!dest) { + free(copy); + goto fail; + } + *dest = copy; + + if (!*var) { + break; + } + } + + return envp; + +fail: + for (size_t i = 0; i < envc; ++i) { + free(envp[i]); + } + free(envp); + return NULL; +} + +/** Check that we resolve executables in $PATH correctly. */ +static bool check_path_resolution(bool use_posix) { + bool ret = true; + + struct bfs_spawn spawn; + ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0); + if (!ret) { + goto out; + } + + spawn.flags |= BFS_SPAWN_USE_PATH; + if (!use_posix) { + spawn.flags &= ~BFS_SPAWN_USE_POSIX; + } + + const char *builddir = getenv("BUILDDIR"); + dchar *bin = dstrprintf("%s/bin", builddir ? builddir : "."); + ret &= bfs_pcheck(bin, "dstrprintf()"); + if (!ret) { + goto destroy; + } + + ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0); + ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 10) == 0); + ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0); + if (!ret) { + goto bin; + } + + // Check that $PATH is resolved in the parent's environment + char **envp; + ret &= bfs_pcheck(envp = envdup()); + if (!ret) { + goto bin; + } + + // Check that $PATH is resolved after the file actions + char *old_path = getenv("PATH"); + dchar *new_path = NULL; + if (old_path) { + ret &= bfs_pcheck(old_path = strdup(old_path)); + if (!ret) { + goto env; + } + new_path = dstrprintf("tests:%s", old_path); + } else { + new_path = dstrdup("tests"); + } + ret &= bfs_check(new_path); + if (!ret) { + goto path; + } + + ret &= bfs_pcheck(setenv("PATH", new_path, true) == 0); + if (!ret) { + goto path; + } + + char *argv[] = {"xspawnee", old_path, NULL}; + pid_t pid = bfs_spawn("xspawnee", &spawn, argv, envp); + ret &= bfs_pcheck(pid >= 0, "bfs_spawn()"); + if (!ret) { + goto unset; + } + + int wstatus; + ret &= bfs_pcheck(xwaitpid(pid, &wstatus, 0) == pid) + && bfs_check(WIFEXITED(wstatus)); + if (ret) { + int wexit = WEXITSTATUS(wstatus); + ret &= bfs_check(wexit == EXIT_SUCCESS, "xspawnee: exit(%d)", wexit); + } + +unset: + if (old_path) { + ret &= bfs_pcheck(setenv("PATH", old_path, true) == 0); + } else { + ret &= bfs_pcheck(unsetenv("PATH") == 0); + } +path: + dstrfree(new_path); + free(old_path); +env: + for (char **var = envp; *var; ++var) { + free(*var); + } + free(envp); +bin: + dstrfree(bin); +destroy: + ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); +out: + return ret; +} + +bool check_xspawn(void) { + bool ret = true; + + ret &= check_path_resolution(true); + ret &= check_path_resolution(false); + + return ret; +} diff --git a/tests/xspawnee.c b/tests/xspawnee.c new file mode 100644 index 0000000..b0a76ca --- /dev/null +++ b/tests/xspawnee.c @@ -0,0 +1,17 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +/** Child binary for bfs_spawn() tests. */ +int main(int argc, char *argv[]) { + if (argc >= 2) { + const char *path = getenv("PATH"); + if (!path || strcmp(path, argv[1]) != 0) { + return EXIT_FAILURE; + } + } + + return EXIT_SUCCESS; +} -- cgit v1.2.3 From 5b58f0223c18aed1b2bd3e8224a3f2f3760e9ada Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Mar 2024 15:15:12 -0400 Subject: tests/xspawn: Also test dup2() --- tests/xspawn.c | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) (limited to 'tests') diff --git a/tests/xspawn.c b/tests/xspawn.c index e356842..d1d5473 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -72,8 +72,10 @@ static bool check_path_resolution(bool use_posix) { } ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0); - ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 10) == 0); + ret &= bfs_pcheck(bfs_spawn_adddup2(&spawn, 10, 11) == 0); ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0); + ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 11) == 0); + ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 11) == 0); if (!ret) { goto bin; } -- cgit v1.2.3 From 943581a4d59b42a684666f146324d908b0dac428 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Mar 2024 15:27:26 -0400 Subject: tests/xspawn: Test path resolution failure --- tests/xspawn.c | 33 ++++++++++++++++++++++++++++++--- 1 file changed, 30 insertions(+), 3 deletions(-) (limited to 'tests') diff --git a/tests/xspawn.c b/tests/xspawn.c index d1d5473..5f80e76 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -50,7 +50,7 @@ fail: } /** Check that we resolve executables in $PATH correctly. */ -static bool check_path_resolution(bool use_posix) { +static bool check_use_path(bool use_posix) { bool ret = true; struct bfs_spawn spawn; @@ -146,11 +146,38 @@ out: return ret; } +/** Check path resolution of non-existent executables. */ +static bool check_enoent(bool use_posix) { + bool ret = true; + + struct bfs_spawn spawn; + ret &= bfs_pcheck(bfs_spawn_init(&spawn) == 0); + if (!ret) { + goto out; + } + + spawn.flags |= BFS_SPAWN_USE_PATH; + if (!use_posix) { + spawn.flags &= ~BFS_SPAWN_USE_POSIX; + } + + char *argv[] = {"eW6f5RM9Qi", NULL}; + pid_t pid = bfs_spawn("eW6f5RM9Qi", &spawn, argv, NULL); + ret &= bfs_pcheck(pid < 0 && errno == ENOENT, "bfs_spawn()"); + + ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); +out: + return ret; +} + bool check_xspawn(void) { bool ret = true; - ret &= check_path_resolution(true); - ret &= check_path_resolution(false); + ret &= check_use_path(true); + ret &= check_use_path(false); + + ret &= check_enoent(true); + ret &= check_enoent(false); return ret; } -- cgit v1.2.3 From cd929976f8beb208c0c3524ae11de6ceed941324 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 27 Mar 2024 15:38:01 -0400 Subject: xspawn: Fix bfs_resolve_late() error reporting --- src/xspawn.c | 10 ++-------- tests/xspawn.c | 23 +++++++++++++++++++++++ 2 files changed, 25 insertions(+), 8 deletions(-) (limited to 'tests') diff --git a/src/xspawn.c b/src/xspawn.c index f67adec..065fbae 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -364,16 +364,10 @@ static int bfs_resolve_late(struct bfs_resolver *res) { res->done = true; return 0; } - - if (end) { - path = end + 1; - } else { - errno = ENOENT; - return -1; - } } - return 0; + errno = ENOENT; + return -1; } /** Check if we can skip path resolution entirely. */ diff --git a/tests/xspawn.c b/tests/xspawn.c index 5f80e76..c1bac36 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -170,6 +170,27 @@ out: return ret; } +static bool check_resolve(void) { + bool ret = true; + char *exe; + + exe = bfs_spawn_resolve("sh"); + ret &= bfs_pcheck(exe, "bfs_spawn_resolve('sh')"); + free(exe); + + exe = bfs_spawn_resolve("/bin/sh"); + ret &= bfs_pcheck(exe && strcmp(exe, "/bin/sh") == 0); + free(exe); + + exe = bfs_spawn_resolve("bin/tests/xspawnee"); + ret &= bfs_pcheck(exe && strcmp(exe, "bin/tests/xspawnee") == 0); + free(exe); + + ret &= bfs_pcheck(!bfs_spawn_resolve("eW6f5RM9Qi") && errno == ENOENT); + + return ret; +} + bool check_xspawn(void) { bool ret = true; @@ -179,5 +200,7 @@ bool check_xspawn(void) { ret &= check_enoent(true); ret &= check_enoent(false); + ret &= check_resolve(); + return ret; } -- cgit v1.2.3 From 99a362d04b507c7c0436c06f087f3d0d3abdc665 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 28 Mar 2024 10:16:03 -0400 Subject: tests/common: Add a -delete error handling test --- tests/common/delete_error.out | 8 ++++++++ tests/common/delete_error.sh | 9 +++++++++ 2 files changed, 17 insertions(+) create mode 100644 tests/common/delete_error.out create mode 100644 tests/common/delete_error.sh (limited to 'tests') diff --git a/tests/common/delete_error.out b/tests/common/delete_error.out new file mode 100644 index 0000000..b6b6505 --- /dev/null +++ b/tests/common/delete_error.out @@ -0,0 +1,8 @@ +. +. +./baz +./baz +./baz/qux +./baz/qux +./foo +./foo/bar diff --git a/tests/common/delete_error.sh b/tests/common/delete_error.sh new file mode 100644 index 0000000..e6327f3 --- /dev/null +++ b/tests/common/delete_error.sh @@ -0,0 +1,9 @@ +cd "$TEST" + +"$XTOUCH" -p foo/bar baz/qux +chmod -w foo +defer chmod +w foo + +! invoke_bfs . -print -delete -print >"$OUT" || fail +sort_output +diff_output -- cgit v1.2.3 From 2fc0b4425197099ba5575d3b50ac77b51c0e0a95 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 28 Mar 2024 11:38:33 -0400 Subject: tests/gnu: Add a -used test --- tests/gnu/used.out | 4 ++++ tests/gnu/used.sh | 40 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 44 insertions(+) create mode 100644 tests/gnu/used.out create mode 100644 tests/gnu/used.sh (limited to 'tests') diff --git a/tests/gnu/used.out b/tests/gnu/used.out new file mode 100644 index 0000000..647621b --- /dev/null +++ b/tests/gnu/used.out @@ -0,0 +1,4 @@ +-used +7: ./nextyear +-used 1: ./tomorrow +-used 2: ./dayafter +-used 7: ./nextweek diff --git a/tests/gnu/used.sh b/tests/gnu/used.sh new file mode 100644 index 0000000..5e5d4e9 --- /dev/null +++ b/tests/gnu/used.sh @@ -0,0 +1,40 @@ +iso8601() { + printf '%04d-%02d-%02dT%02d:%02d:%02d\n' "$@" +} + +cd "$TEST" + +now=$(date '+%Y-%m-%dT%H:%M:%S') + +# Parse the current date +[[ "$now" =~ ^([0-9]{4})-([0-9]{2})-([0-9]{2})T([0-9]{2}):([0-9]{2}):([0-9]{2})$ ]] || fail +# Treat leading zeros as decimal, not octal +YYYY=$((10#${BASH_REMATCH[1]})) +MM=$((10#${BASH_REMATCH[2]})) +DD=$((10#${BASH_REMATCH[3]})) +hh=$((10#${BASH_REMATCH[4]})) +mm=$((10#${BASH_REMATCH[5]})) +ss=$((10#${BASH_REMATCH[6]})) + +# -used is always false if atime < ctime +yesterday=$(iso8601 $YYYY $MM $((DD - 1)) $hh $mm $ss) +"$XTOUCH" -at "$yesterday" yesterday + +# -used rounds up +tomorrow=$(iso8601 $YYYY $MM $DD $((hh + 1)) $mm $ss) +"$XTOUCH" -at "$tomorrow" tomorrow + +dayafter=$(iso8601 $YYYY $MM $((DD + 1)) $((hh + 1)) $mm $ss) +"$XTOUCH" -at "$dayafter" dayafter + +nextweek=$(iso8601 $YYYY $MM $((DD + 6)) $((hh + 1)) $mm $ss) +"$XTOUCH" -at "$nextweek" nextweek + +nextyear=$(iso8601 $((YYYY + 1)) $MM $DD $hh $mm $ss) +"$XTOUCH" -at "$nextyear" nextyear + +bfs_diff -mindepth 1 \ + -a -used 1 -printf '-used 1: %p\n' \ + -o -used 2 -printf '-used 2: %p\n' \ + -o -used 7 -printf '-used 7: %p\n' \ + -o -used +7 -printf '-used +7: %p\n' -- cgit v1.2.3 From ff1b3d12cb798477b64c09cc0e3b9b2b6bc3e9da Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 28 Mar 2024 12:24:50 -0400 Subject: tests/bsd: Add a -sparse test --- tests/bsd/sparse.out | 1 + tests/bsd/sparse.sh | 12 ++++++++++++ 2 files changed, 13 insertions(+) create mode 100644 tests/bsd/sparse.out create mode 100644 tests/bsd/sparse.sh (limited to 'tests') diff --git a/tests/bsd/sparse.out b/tests/bsd/sparse.out new file mode 100644 index 0000000..52dcf49 --- /dev/null +++ b/tests/bsd/sparse.out @@ -0,0 +1 @@ +mnt/sparse diff --git a/tests/bsd/sparse.sh b/tests/bsd/sparse.sh new file mode 100644 index 0000000..7fcdeed --- /dev/null +++ b/tests/bsd/sparse.sh @@ -0,0 +1,12 @@ +test "$UNAME" = "Linux" || skip + +cd "$TEST" +mkdir mnt + +bfs_sudo mount -t tmpfs tmpfs mnt || skip +defer bfs_sudo umount mnt + +truncate -s 1M mnt/sparse +dd if=/dev/zero of=mnt/dense bs=1M count=1 + +bfs_diff mnt -type f -sparse -- cgit v1.2.3 From c31577d102d87455f3f12086be4c0e2159fa5d35 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 8 Apr 2024 11:27:11 -0400 Subject: build: Add a separate configuration step --- .github/workflows/ci.yml | 23 +- .github/workflows/codecov.yml | 3 +- .github/workflows/codeql.yml | 1 + .gitignore | 2 + GNUmakefile | 372 ------------------------------ Makefile | 517 ++++++++++++++++++++++++++++++++++++++++++ README.md | 4 +- config/cc.sh | 12 + config/empty.c | 6 + config/libacl.c | 6 + config/libcap.c | 6 + config/liburing.c | 6 + config/oniguruma.c | 6 + config/pkg.sh | 44 ++++ config/pkgconf.sh | 64 ++++++ docs/BUILDING.md | 83 +++---- docs/USAGE.md | 2 +- src/config.h | 11 +- src/main.c | 1 + src/parse.c | 2 +- src/version.c | 6 + tests/run.sh | 2 +- tests/tests.mk | 10 +- 23 files changed, 757 insertions(+), 432 deletions(-) delete mode 100644 GNUmakefile create mode 100644 Makefile create mode 100755 config/cc.sh create mode 100644 config/empty.c create mode 100644 config/libacl.c create mode 100644 config/libcap.c create mode 100644 config/liburing.c create mode 100644 config/oniguruma.c create mode 100755 config/pkg.sh create mode 100755 config/pkgconf.sh create mode 100644 src/version.c (limited to 'tests') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 51d06fb..78aa196 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -53,12 +53,13 @@ jobs: run: | brew install \ bash \ - expect + expect \ + make - name: Run tests run: | jobs=$(sysctl -n hw.ncpu) - make -j$jobs distcheck + gmake -j$jobs distcheck freebsd: name: FreeBSD @@ -79,8 +80,8 @@ jobs: pkg install -y \ bash \ expect \ - gmake \ oniguruma \ + pkgconf \ sudo \ tcl-wrapper pw useradd -n action -m -G wheel -s /usr/local/bin/bash @@ -89,7 +90,7 @@ jobs: run: | chown -R action:action . - sudo -u action gmake -j$(nproc) distcheck + sudo -u action make -j$(nproc) distcheck openbsd: name: OpenBSD @@ -119,6 +120,7 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) + doas -u action gmake config doas -u action gmake -j$jobs check TEST_FLAGS="--sudo=doas --verbose=skipped" netbsd: @@ -141,8 +143,8 @@ jobs: pkg_add \ bash \ clang \ - gmake \ oniguruma \ + pkgconf \ sudo \ tcl-expect useradd -m -G wheel -g =uid action @@ -152,7 +154,8 @@ jobs: PATH="/sbin:/usr/sbin:$PATH" chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action gmake -j$jobs check CC=clang LDFLAGS="-rpath /usr/pkg/lib" TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action make config CC=clang + sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" dragonflybsd: name: DragonFly BSD @@ -173,8 +176,8 @@ jobs: pkg install -y \ bash \ expect \ - gmake \ oniguruma \ + pkgconf \ sudo \ tcl-wrapper pw useradd -n action -m -G wheel -s /usr/local/bin/bash @@ -183,7 +186,8 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action make config + sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" omnios: name: OmniOS @@ -215,4 +219,5 @@ jobs: PATH="/usr/xpg4/bin:$PATH" chown -R action:staff . jobs=$(getconf NPROCESSORS_ONLN) - sudo -u action gmake -j$jobs check LDFLAGS="-Wl,-rpath,/opt/ooce/lib/amd64" TEST_FLAGS="--sudo --verbose=skipped" + sudo -u action gmake config + sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index d1dc351..2abe531 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -25,7 +25,8 @@ jobs: - name: Generate coverage run: | - make -j$(nproc) gcov check TEST_FLAGS="--sudo" + make config GCOV=y + make -j$(nproc) check TEST_FLAGS="--sudo" gcov -abcfpu obj/*/*.o - uses: codecov/codecov-action@v3 diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c50b266..a2c224a 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -47,6 +47,7 @@ jobs: - name: Build run: | + make config make -j$(nproc) all - name: Perform CodeQL Analysis diff --git a/.gitignore b/.gitignore index 4ded7c4..84e47fc 100644 --- a/.gitignore +++ b/.gitignore @@ -1,2 +1,4 @@ /bin/ +/gen/ /obj/ +/distcheck-*/ diff --git a/GNUmakefile b/GNUmakefile deleted file mode 100644 index 8685ca3..0000000 --- a/GNUmakefile +++ /dev/null @@ -1,372 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -ifneq ($(wildcard .git),) -VERSION := $(shell git describe --always 2>/dev/null) -endif - -ifndef VERSION -VERSION := 3.1.3 -endif - -ifndef OS -OS := $(shell uname) -endif - -ifndef ARCH -ARCH := $(shell uname -m) -endif - -CC ?= gcc -INSTALL ?= install -MKDIR ?= mkdir -p -RM ?= rm -f - -export BUILDDIR ?= . -DESTDIR ?= -PREFIX ?= /usr -MANDIR ?= $(PREFIX)/share/man - -BIN := $(BUILDDIR)/bin -OBJ := $(BUILDDIR)/obj - -DEFAULT_CFLAGS := \ - -g \ - -Wall \ - -Wformat=2 \ - -Werror=implicit \ - -Wimplicit-fallthrough \ - -Wmissing-declarations \ - -Wshadow \ - -Wsign-compare \ - -Wstrict-prototypes - -CFLAGS ?= $(DEFAULT_CFLAGS) -LDFLAGS ?= -DEPFLAGS ?= -MD -MP -MF $(@:.o=.d) - -LOCAL_CPPFLAGS := \ - -D__EXTENSIONS__ \ - -D_ATFILE_SOURCE \ - -D_BSD_SOURCE \ - -D_DARWIN_C_SOURCE \ - -D_DEFAULT_SOURCE \ - -D_GNU_SOURCE \ - -D_LARGEFILE64_SOURCE \ - -D_POSIX_PTHREAD_SEMANTICS \ - -D_FILE_OFFSET_BITS=64 \ - -D_TIME_BITS=64 \ - -DBFS_VERSION=\"$(VERSION)\" - -LOCAL_CFLAGS := -std=c17 -pthread -LOCAL_LDFLAGS := -LOCAL_LDLIBS := - -ASAN := $(filter asan,$(MAKECMDGOALS)) -LSAN := $(filter lsan,$(MAKECMDGOALS)) -MSAN := $(filter msan,$(MAKECMDGOALS)) -TSAN := $(filter tsan,$(MAKECMDGOALS)) -UBSAN := $(filter ubsan,$(MAKECMDGOALS)) - -ifdef ASAN -LOCAL_CFLAGS += -fsanitize=address -SANITIZE := y -endif - -ifdef LSAN -LOCAL_CFLAGS += -fsanitize=leak -SANITIZE := y -endif - -ifdef MSAN -# msan needs all code instrumented -NOLIBS := y -LOCAL_CFLAGS += -fsanitize=memory -fsanitize-memory-track-origins -SANITIZE := y -endif - -ifdef TSAN -# tsan needs all code instrumented -NOLIBS := y -# https://github.com/google/sanitizers/issues/342 -LOCAL_CPPFLAGS += -DBFS_USE_TARGET_CLONES=0 -LOCAL_CFLAGS += -fsanitize=thread -SANITIZE := y -endif - -ifdef UBSAN -LOCAL_CFLAGS += -fsanitize=undefined -SANITIZE := y -endif - -ifdef SANITIZE -LOCAL_CFLAGS += -fno-sanitize-recover=all -endif - -ifndef NOLIBS -USE_ONIGURUMA := y -endif - -ifdef USE_ONIGURUMA -LOCAL_CPPFLAGS += -DBFS_USE_ONIGURUMA=1 - -ONIG_CONFIG := $(shell command -v onig-config 2>/dev/null) -ifdef ONIG_CONFIG -ONIG_CFLAGS := $(shell $(ONIG_CONFIG) --cflags) -ONIG_LDLIBS := $(shell $(ONIG_CONFIG) --libs) -else -ONIG_LDLIBS := -lonig -endif - -LOCAL_CFLAGS += $(ONIG_CFLAGS) -LOCAL_LDLIBS += $(ONIG_LDLIBS) -endif # USE_ONIGURUMA - -ifeq ($(OS),Linux) -ifndef NOLIBS -USE_ACL := y -USE_LIBCAP := y -USE_LIBURING := y -endif - -ifdef USE_ACL -LOCAL_LDLIBS += -lacl -else -LOCAL_CPPFLAGS += -DBFS_USE_SYS_ACL_H=0 -endif - -ifdef USE_LIBCAP -LOCAL_LDLIBS += -lcap -else -LOCAL_CPPFLAGS += -DBFS_USE_SYS_CAPABILITY_H=0 -endif - -ifdef USE_LIBURING -LOCAL_CPPFLAGS += -DBFS_USE_LIBURING=1 -LOCAL_LDLIBS += -luring -endif - -LOCAL_LDFLAGS += -Wl,--as-needed -LOCAL_LDLIBS += -lrt -endif # Linux - -ifeq ($(OS),NetBSD) -LOCAL_LDLIBS += -lutil -endif - -ifeq ($(OS),DragonFly) -LOCAL_LDLIBS += -lposix1e -endif - -ifeq ($(OS),SunOS) -LOCAL_LDLIBS += -lsocket -lnsl -endif - -ifneq ($(filter gcov,$(MAKECMDGOALS)),) -LOCAL_CFLAGS += --coverage -# gcov only intercepts fork()/exec() with -std=gnu* -LOCAL_CFLAGS := $(patsubst -std=c%,-std=gnu%,$(LOCAL_CFLAGS)) -endif - -ifneq ($(filter lint,$(MAKECMDGOALS)),) -LOCAL_CPPFLAGS += \ - -D_FORTIFY_SOURCE=3 \ - -DBFS_LINT -LOCAL_CFLAGS += -Werror -O2 -endif - -ifneq ($(filter release,$(MAKECMDGOALS)),) -LOCAL_CPPFLAGS += -DNDEBUG -CFLAGS := $(DEFAULT_CFLAGS) -O3 -flto=auto -endif - -ALL_CPPFLAGS = $(LOCAL_CPPFLAGS) $(CPPFLAGS) $(EXTRA_CPPFLAGS) -ALL_CFLAGS = $(ALL_CPPFLAGS) $(LOCAL_CFLAGS) $(CFLAGS) $(EXTRA_CFLAGS) $(DEPFLAGS) -ALL_LDFLAGS = $(ALL_CFLAGS) $(LOCAL_LDFLAGS) $(LDFLAGS) $(EXTRA_LDFLAGS) -ALL_LDLIBS = $(LOCAL_LDLIBS) $(LDLIBS) $(EXTRA_LDLIBS) - -# Default make target -bfs: $(BIN)/bfs -.PHONY: bfs - -# Goals that are treated like flags by this makefile -FLAG_GOALS := asan lsan msan tsan ubsan gcov lint release - -# These are the remaining non-flag goals -GOALS := $(filter-out $(FLAG_GOALS),$(MAKECMDGOALS)) - -# Build the default goal if only flag goals are specified -FLAG_PREREQS := -ifndef GOALS -FLAG_PREREQS += bfs -endif - -# Make sure that "make release" builds everything, but "make release obj/src/main.o" doesn't -$(FLAG_GOALS): $(FLAG_PREREQS) - @: -.PHONY: $(FLAG_GOALS) - -all: bfs tests -.PHONY: all - -$(BIN)/%: - @$(MKDIR) $(@D) - +$(CC) $(ALL_LDFLAGS) $^ $(ALL_LDLIBS) -o $@ -ifeq ($(OS) $(SANITIZE),FreeBSD y) - elfctl -e +noaslr $@ -endif - -$(OBJ)/%.o: %.c $(OBJ)/FLAGS - @$(MKDIR) $(@D) - $(CC) $(ALL_CFLAGS) -c $< -o $@ - -# Save the full set of flags to rebuild everything when they change -$(OBJ)/FLAGS.new: - @$(MKDIR) $(@D) - @echo $(CC) : $(ALL_CFLAGS) : $(ALL_LDFLAGS) : $(ALL_LDLIBS) >$@ -.PHONY: $(OBJ)/FLAGS.new - -# Only update obj/FLAGS if obj/FLAGS.new is different -$(OBJ)/FLAGS: $(OBJ)/FLAGS.new - @test -e $@ && cmp -s $@ $< && rm $< || mv $< $@ - -# All object files except the entry point -LIBBFS := \ - $(OBJ)/src/alloc.o \ - $(OBJ)/src/bar.o \ - $(OBJ)/src/bfstd.o \ - $(OBJ)/src/bftw.o \ - $(OBJ)/src/color.o \ - $(OBJ)/src/ctx.o \ - $(OBJ)/src/diag.o \ - $(OBJ)/src/dir.o \ - $(OBJ)/src/dstring.o \ - $(OBJ)/src/eval.o \ - $(OBJ)/src/exec.o \ - $(OBJ)/src/expr.o \ - $(OBJ)/src/fsade.o \ - $(OBJ)/src/ioq.o \ - $(OBJ)/src/mtab.o \ - $(OBJ)/src/opt.o \ - $(OBJ)/src/parse.o \ - $(OBJ)/src/printf.o \ - $(OBJ)/src/pwcache.o \ - $(OBJ)/src/stat.o \ - $(OBJ)/src/thread.o \ - $(OBJ)/src/trie.o \ - $(OBJ)/src/typo.o \ - $(OBJ)/src/xregex.o \ - $(OBJ)/src/xspawn.o \ - $(OBJ)/src/xtime.o - -# The main executable -$(BIN)/bfs: $(OBJ)/src/main.o $(LIBBFS) - -# Testing utilities -TEST_UTILS := \ - $(BIN)/tests/mksock \ - $(BIN)/tests/xspawnee \ - $(BIN)/tests/xtouch - -$(BIN)/tests/mksock: $(OBJ)/tests/mksock.o $(LIBBFS) - -$(BIN)/tests/xspawnee: $(OBJ)/tests/xspawnee.o - -$(BIN)/tests/xtouch: $(OBJ)/tests/xtouch.o $(LIBBFS) - -# All test binaries -TESTS := $(BIN)/tests/units $(TEST_UTILS) - -$(BIN)/tests/units: \ - $(OBJ)/tests/alloc.o \ - $(OBJ)/tests/bfstd.o \ - $(OBJ)/tests/bit.o \ - $(OBJ)/tests/ioq.o \ - $(OBJ)/tests/main.o \ - $(OBJ)/tests/trie.o \ - $(OBJ)/tests/xspawn.o \ - $(OBJ)/tests/xtime.o \ - $(LIBBFS) - -# Build all the test binaries -tests: $(TESTS) -.PHONY: tests - -# Run the unit tests -unit-tests: $(BIN)/tests/units $(BIN)/tests/xspawnee - $< -.PHONY: unit-tests - -# The different flag combinations we check -INTEGRATIONS := default dfs ids eds j1 j2 j3 s -INTEGRATION_TESTS := $(INTEGRATIONS:%=check-%) - -check-default: $(BIN)/bfs $(TEST_UTILS) - +./tests/tests.sh --make="$(MAKE)" --bfs="$<" $(TEST_FLAGS) - -check-dfs check-ids check-eds: check-%: $(BIN)/bfs $(TEST_UTILS) - +./tests/tests.sh --make="$(MAKE)" --bfs="$< -S $*" $(TEST_FLAGS) - -check-j1 check-j2 check-j3 check-s: check-%: $(BIN)/bfs $(TEST_UTILS) - +./tests/tests.sh --make="$(MAKE)" --bfs="$< -$*" $(TEST_FLAGS) - -# Run the integration tests -integration-tests: $(INTEGRATION_TESTS) -.PHONY: integration-tests - -# Run all the tests -check: unit-tests integration-tests -.PHONY: check - -# Custom test flags for distcheck -DISTCHECK_FLAGS := -s TEST_FLAGS="--sudo --verbose=skipped" - -distcheck: - +$(MAKE) -B asan ubsan check $(DISTCHECK_FLAGS) -ifneq ($(OS),Darwin) - +$(MAKE) -B msan ubsan check CC=clang $(DISTCHECK_FLAGS) -endif - +$(MAKE) -B tsan ubsan check CC=clang $(DISTCHECK_FLAGS) -ifeq ($(OS) $(ARCH),Linux x86_64) - +$(MAKE) -B check EXTRA_CFLAGS="-m32" ONIG_CONFIG= USE_LIBURING= $(DISTCHECK_FLAGS) -endif - +$(MAKE) -B release check $(DISTCHECK_FLAGS) - +$(MAKE) -B check $(DISTCHECK_FLAGS) - +$(MAKE) check-install $(DISTCHECK_FLAGS) -.PHONY: distcheck - -clean: - $(RM) -r $(BIN) $(OBJ) -.PHONY: clean - -install: - $(MKDIR) $(DESTDIR)$(PREFIX)/bin - $(INSTALL) -m755 $(BIN)/bfs $(DESTDIR)$(PREFIX)/bin/bfs - $(MKDIR) $(DESTDIR)$(MANDIR)/man1 - $(INSTALL) -m644 docs/bfs.1 $(DESTDIR)$(MANDIR)/man1/bfs.1 - $(MKDIR) $(DESTDIR)$(PREFIX)/share/bash-completion/completions - $(INSTALL) -m644 completions/bfs.bash $(DESTDIR)$(PREFIX)/share/bash-completion/completions/bfs - $(MKDIR) $(DESTDIR)$(PREFIX)/share/zsh/site-functions - $(INSTALL) -m644 completions/bfs.zsh $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_bfs - $(MKDIR) $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d - $(INSTALL) -m644 completions/bfs.fish $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/bfs.fish -.PHONY: install - -uninstall: - $(RM) $(DESTDIR)$(PREFIX)/share/bash-completion/completions/bfs - $(RM) $(DESTDIR)$(PREFIX)/share/zsh/site-functions/_bfs - $(RM) $(DESTDIR)$(PREFIX)/share/fish/vendor_completions.d/bfs.fish - $(RM) $(DESTDIR)$(MANDIR)/man1/bfs.1 - $(RM) $(DESTDIR)$(PREFIX)/bin/bfs -.PHONY: uninstall - -check-install: - +$(MAKE) install DESTDIR=$(BUILDDIR)/pkg - +$(MAKE) uninstall DESTDIR=$(BUILDDIR)/pkg - $(BIN)/bfs $(BUILDDIR)/pkg -not -type d -print -exit 1 - $(RM) -r $(BUILDDIR)/pkg -.PHONY: check-install - -.SUFFIXES: - --include $(wildcard $(OBJ)/*/*.d) diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..326ee87 --- /dev/null +++ b/Makefile @@ -0,0 +1,517 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# This Makefile implements the configuration and build steps for bfs. It is +# portable to both GNU make and the BSD make implementations (how that works +# is documented below). To build bfs, run +# +# $ make config +# $ make + +# The default build target +default: bfs +.PHONY: default + +# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to +.OBJDIR: . + +# We don't use any suffix rules +.SUFFIXES: + +# GNU make has $^ for the full list of targets, while BSD make has $> and the +# long-form ${.ALLSRC}. We could write $^ $> to get them both, but that would +# break if one of them implemented support for the other. So instead, bring +# BSD's ${.ALLSRC} to GNU. +.ALLSRC ?= $^ + +# Platform detection +OS != uname +ARCH != uname -m + +# For out-of-tree builds, e.g. +# +# $ make config BUILDDIR=/path/to/build/dir +# $ make BUILDDIR=/path/to/build/dir +BUILDDIR ?= . + +# Shorthand for build subdirectories +BIN := ${BUILDDIR}/bin +GEN := ${BUILDDIR}/gen +OBJ := ${BUILDDIR}/obj + +# GNU make strips a leading ./ from target names, so do the same for BSD make +BIN := ${BIN:./%=%} +GEN := ${GEN:./%=%} +OBJ := ${OBJ:./%=%} + +# Installation paths +DESTDIR ?= +PREFIX ?= /usr +MANDIR ?= ${PREFIX}/share/man + +# Configurable executables; can be overridden with +# +# $ make config CC=clang +CC ?= cc +INSTALL ?= install +MKDIR ?= mkdir -p +PKG_CONFIG ?= pkg-config +RM ?= rm -f + +# Configurable flags + +CPPFLAGS ?= +CFLAGS ?= \ + -g \ + -Wall \ + -Wformat=2 \ + -Werror=implicit \ + -Wimplicit-fallthrough \ + -Wmissing-declarations \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-prototypes +LDFLAGS ?= +LDLIBS ?= + +EXTRA_CPPFLAGS ?= +EXTRA_CFLAGS ?= +EXTRA_LDFLAGS ?= +EXTRA_LDLIBS ?= + +GIT_VERSION != test -d .git && command -v git >/dev/null 2>&1 && git describe --always --dirty || echo 3.1.3 +VERSION ?= ${GIT_VERSION} + +# Immutable flags +export BFS_CPPFLAGS= \ + -D__EXTENSIONS__ \ + -D_ATFILE_SOURCE \ + -D_BSD_SOURCE \ + -D_DARWIN_C_SOURCE \ + -D_DEFAULT_SOURCE \ + -D_GNU_SOURCE \ + -D_LARGEFILE64_SOURCE \ + -D_POSIX_PTHREAD_SEMANTICS \ + -D_FILE_OFFSET_BITS=64 \ + -D_TIME_BITS=64 +export BFS_CFLAGS= -std=c17 -pthread + +# Platform-specific system libraries +LDLIBS,DragonFly := -lposix1e +LDLIBS,Linux := -lrt +LDLIBS,NetBSD := -lutil +LDLIBS,SunOS := -lsocket -lnsl +_BFS_LDLIBS := ${LDLIBS,${OS}} +export BFS_LDLIBS=${_BFS_LDLIBS} + +# Build profiles +ASAN ?= n +LSAN ?= n +MSAN ?= n +TSAN ?= n +UBSAN ?= n +GCOV ?= n +LINT ?= n +RELEASE ?= n + +export ASAN_CFLAGS= -fsanitize=address +export LSAN_CFLAGS= -fsanitize=leak +export MSAN_CFLAGS= -fsanitize=memory -fsanitize-memory-track-origins +export UBSAN_CFLAGS= -fsanitize=undefined + +# https://github.com/google/sanitizers/issues/342 +export TSAN_CPPFLAGS= -DBFS_USE_TARGET_CLONES=0 +export TSAN_CFLAGS= -fsanitize=thread + +SAN := ${ASAN}${LSAN}${MSAN}${TSAN}${UBSAN} +export SAN_CFLAGS= -fno-sanitize-recover=all + +# MSAN and TSAN both need all code to be instrumented +export NOLIBS= ${MSAN}${TSAN} + +# gcov only intercepts fork()/exec() with -std=gnu* +export GCOV_CFLAGS= --coverage -std=gnu17 + +export LINT_CPPFLAGS= -D_FORTIFY_SOURCE=3 -DBFS_LINT +export LINT_CFLAGS= -Werror -O2 + +export RELEASE_CPPFLAGS= -DNDEBUG +export RELEASE_CFLAGS= -O3 -flto=auto + +# Auto-detected library dependencies. Can be set manually with +# +# $ make config USE_LIBURING=n USE_ONIGURUMA=y +USE_LIBACL ?= +USE_LIBCAP ?= +USE_LIBURING ?= +USE_ONIGURUMA ?= + +# Save the new value of these variables, before they potentially get overridden +# by `-include ${CONFIG}` below + +_XPREFIX := ${PREFIX} +_XMANDIR := ${MANDIR} + +_XOS := ${OS} +_XARCH := ${ARCH} + +_XCC := ${CC} +_XINSTALL := ${INSTALL} +_XMKDIR := ${MKDIR} +_XRM := ${RM} + +_XCPPFLAGS := ${CPPFLAGS} +_XCFLAGS := ${CFLAGS} +_XLDFLAGS := ${LDFLAGS} +_XLDLIBS := ${LDLIBS} + +_XUSE_LIBACL := ${USE_LIBACL} +_XUSE_LIBCAP := ${USE_LIBCAP} +_XUSE_LIBURING := ${USE_LIBURING} +_XUSE_ONIGURUMA := ${USE_ONIGURUMA} + +# GNU make supports `export VAR`, but BSD make requires `export VAR=value`. +# Sadly, GNU make gives a recursion error on `export VAR=${VAR}`. + +_BUILDDIR := ${BUILDDIR} +_PKG_CONFIG := ${PKG_CONFIG} + +export BUILDDIR=${_BUILDDIR} +export PKG_CONFIG=${_PKG_CONFIG} + +export XPREFIX=${_XPREFIX} +export XMANDIR=${_XMANDIR} + +export XOS=${_XOS} +export XARCH=${_XARCH} + +export XCC=${_XCC} +export XINSTALL=${_XINSTALL} +export XMKDIR=${_XMKDIR} +export XRM=${_XRM} + +export XCPPFLAGS=${_XCPPFLAGS} +export XCFLAGS=${_XCFLAGS} +export XLDFLAGS=${_XLDFLAGS} +export XLDLIBS=${_XLDLIBS} + +export XUSE_LIBACL=${_XUSE_LIBACL} +export XUSE_LIBCAP=${_XUSE_LIBCAP} +export XUSE_LIBURING=${_XUSE_LIBURING} +export XUSE_ONIGURUMA=${_XUSE_ONIGURUMA} + +# The configuration file generated by `make config` +CONFIG := ${GEN}/config.mk +-include ${CONFIG} + +## Configuration phase (`make config`) + +# External dependencies +PKGS := \ + ${GEN}/libacl.mk \ + ${GEN}/libcap.mk \ + ${GEN}/liburing.mk \ + ${GEN}/oniguruma.mk + +# Makefile fragments generated by `make config` +MKS := \ + ${GEN}/vars.mk \ + ${GEN}/deps.mk \ + ${GEN}/objs.mk \ + ${PKGS} + +# The configuration goal itself +config: ${MKS} + @printf 'include $${GEN}/%s\n' ${MKS:${GEN}/%=%} >${CONFIG} +.PHONY: config + +# Saves the configurable variables +${GEN}/vars.mk:: + @${XMKDIR} ${@D} + @printf 'PREFIX := %s\n' "$$XPREFIX" >$@ + @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@ + @printf 'OS := %s\n' "$$XOS" >>$@ + @printf 'ARCH := %s\n' "$$XARCH" >>$@ + @printf 'CC := %s\n' "$$XCC" >>$@ + @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@ + @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@ + @printf 'RM := %s\n' "$$XRM" >>$@ + @printf 'CPPFLAGS := %s\n' "$$BFS_CPPFLAGS" >>$@ + @test "${TSAN}" != y || printf 'CPPFLAGS += %s\n' "$$TSAN_CPPFLAGS" >>$@ + @test "${LINT}" != y || printf 'CPPFLAGS += %s\n' "$$LINT_CPPFLAGS" >>$@ + @test "${RELEASE}" != y || printf 'CPPFLAGS += %s\n' "$$RELEASE_CPPFLAGS" >>$@ + @test -z "$$XCPPFLAGS" || printf 'CPPFLAGS += %s\n' "$$XCPPFLAGS" >>$@ + @test -z "$$EXTRA_CPPFLAGS" || printf 'CPPFLAGS += %s\n' "$$EXTRA_CPPFLAGS" >>$@ + @printf 'CFLAGS := %s\n' "$$BFS_CFLAGS" >>$@ + @test "${ASAN}" != y || printf 'CFLAGS += %s\n' "$$ASAN_CFLAGS" >>$@ + @test "${LSAN}" != y || printf 'CFLAGS += %s\n' "$$LSAN_CFLAGS" >>$@ + @test "${MSAN}" != y || printf 'CFLAGS += %s\n' "$$MSAN_CFLAGS" >>$@ + @test "${TSAN}" != y || printf 'CFLAGS += %s\n' "$$TSAN_CFLAGS" >>$@ + @test "${UBSAN}" != y || printf 'CFLAGS += %s\n' "$$UBSAN_CFLAGS" >>$@ + @case "${SAN}" in *y*) printf 'CFLAGS += %s\n' "$$SAN_CFLAGS" >>$@ ;; esac + @test "${GCOV}" != y || printf 'CFLAGS += %s\n' "$$GCOV_CFLAGS" >>$@ + @test "${LINT}" != y || printf 'CFLAGS += %s\n' "$$LINT_CFLAGS" >>$@ + @test "${RELEASE}" != y || printf 'CFLAGS += %s\n' "$$RELEASE_CFLAGS" >>$@ + @test -z "$$XCFLAGS" || printf 'CFLAGS += %s\n' "$$XCFLAGS" >>$@ + @test -z "$$EXTRA_CFLAGS" || printf 'CFLAGS += %s\n' "$$EXTRA_CFLAGS" >>$@ + @printf 'LDFLAGS := %s\n' "$$XLDFLAGS" >>$@ + @test -z "$$EXTRA_LDFLAGS" || printf 'LDFLAGS += %s\n' "$$EXTRA_LDFLAGS" >>$@ + @printf 'LDLIBS := %s\n' "$$XLDLIBS" >>$@ + @test -z "$$EXTRA_LDLIBS" || printf 'LDLIBS += %s\n' "$$EXTRA_LDLIBS" >>$@ + @test -z "$$BFS_LDLIBS" || printf 'LDLIBS += %s\n' "$$BFS_LDLIBS" >>$@ + @case "${OS}-${SAN}" in FreeBSD-*y*) printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@ ;; esac + @cat $@ + +# Check for dependency generation support +${GEN}/deps.mk:: + @${MKDIR} ${@D} + @if config/cc.sh -MD -MP -MF /dev/null config/empty.c; then \ + echo 'DEPFLAGS = -MD -MP -MF $${@:.o=.d}'; \ + fi 2>$@.log | tee $@ + @printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ + +# Lists file.o: file.c dependencies +${GEN}/objs.mk:: + @${MKDIR} ${@D} + @for obj in ${OBJS:${OBJ}/%.o=%}; do printf '$${OBJ}/%s.o: %s.c\n' "$$obj" "$$obj"; done >$@ + +# Auto-detect dependencies and their build flags +${PKGS}:: + @${MKDIR} ${@D} + @config/pkg.sh ${@:${GEN}/%.mk=%} >$@ 2>$@.log + @cat $@ + +# bfs used to have flag-like targets (`make release`, `make asan ubsan`, etc.). +# Direct users to the new configuration system. +asan lsan msan tsan ubsan gcov lint release:: + @printf 'error: `make %s` is no longer supported. ' $@ >&2 + @printf 'Use `make config %s=y` instead.\n' $$(echo $@ | tr '[a-z]' '[A-Z]') >&2 + @false + +## Build phase (`make`) + +# The main binary +bfs: ${BIN}/bfs +.PHONY: bfs + +# All binaries +BINS := \ + ${BIN}/bfs \ + ${BIN}/tests/mksock \ + ${BIN}/tests/units \ + ${BIN}/tests/xspawnee \ + ${BIN}/tests/xtouch + +all: ${BINS} +.PHONY: all + +# All object files except the entry point +LIBBFS := \ + ${OBJ}/src/alloc.o \ + ${OBJ}/src/bar.o \ + ${OBJ}/src/bfstd.o \ + ${OBJ}/src/bftw.o \ + ${OBJ}/src/color.o \ + ${OBJ}/src/ctx.o \ + ${OBJ}/src/diag.o \ + ${OBJ}/src/dir.o \ + ${OBJ}/src/dstring.o \ + ${OBJ}/src/eval.o \ + ${OBJ}/src/exec.o \ + ${OBJ}/src/expr.o \ + ${OBJ}/src/fsade.o \ + ${OBJ}/src/ioq.o \ + ${OBJ}/src/mtab.o \ + ${OBJ}/src/opt.o \ + ${OBJ}/src/parse.o \ + ${OBJ}/src/printf.o \ + ${OBJ}/src/pwcache.o \ + ${OBJ}/src/stat.o \ + ${OBJ}/src/thread.o \ + ${OBJ}/src/trie.o \ + ${OBJ}/src/typo.o \ + ${OBJ}/src/version.o \ + ${OBJ}/src/xregex.o \ + ${OBJ}/src/xspawn.o \ + ${OBJ}/src/xtime.o + +# Group relevant flags together +ALL_CFLAGS = ${CPPFLAGS} ${CFLAGS} ${DEPFLAGS} +ALL_LDFLAGS = ${CFLAGS} ${LDFLAGS} + +# The main binary +${BIN}/bfs: ${LIBBFS} ${OBJ}/src/main.o + +${BINS}: + @${MKDIR} ${@D} + +${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@ + ${POSTLINK} + +# All object files +OBJS := \ + ${OBJ}/src/main.o \ + ${OBJ}/tests/alloc.o \ + ${OBJ}/tests/bfstd.o \ + ${OBJ}/tests/bit.o \ + ${OBJ}/tests/ioq.o \ + ${OBJ}/tests/main.o \ + ${OBJ}/tests/mksock.o \ + ${OBJ}/tests/trie.o \ + ${OBJ}/tests/xspawn.o \ + ${OBJ}/tests/xspawnee.o \ + ${OBJ}/tests/xtime.o \ + ${OBJ}/tests/xtouch.o \ + ${LIBBFS} + +# Depend on ${CONFIG} to make sure `make config` runs first, and to rebuild when +# the configuration changes +${OBJS}: ${CONFIG} + @${MKDIR} ${@D} + ${CC} ${ALL_CFLAGS} -c ${@:${OBJ}/%.o=%.c} -o $@ + +# Save the version number to this file, but only update VERSION if it changes +${GEN}/NEWVERSION:: + @${MKDIR} ${@D} + @printf '%s\n' '${VERSION}' >$@ + +${GEN}/VERSION: ${GEN}/NEWVERSION + @test -e $@ && cmp -s $@ ${.ALLSRC} && rm ${.ALLSRC} || mv ${.ALLSRC} $@ + +# Rebuild version.c whenever the version number changes +${OBJ}/src/version.o: ${GEN}/VERSION +${OBJ}/src/version.o: CPPFLAGS := ${CPPFLAGS} -DBFS_VERSION='"${VERSION}"' + +# Clean all build products +clean:: + ${RM} -r ${BIN} ${OBJ} + +# Clean everything, including generated files +distclean: clean + ${RM} -r ${GEN} +.PHONY: distclean + +## Test phase (`make check`) + +# Unit test binaries +UTEST_BINS := \ + ${BIN}/tests/units \ + ${BIN}/tests/xspawnee + +# Integration test binaries +ITEST_BINS := \ + ${BIN}/tests/mksock \ + ${BIN}/tests/xtouch + +# Build (but don't run) test binaries +tests: ${UTEST_BINS} ${ITEST_BINS} +.PHONY: tests + +# Run all the tests +check: unit-tests integration-tests +.PHONY: check + +# Run the unit tests +unit-tests: ${UTEST_BINS} + ${BIN}/tests/units +.PHONY: unit-tests + +${BIN}/tests/units: \ + ${OBJ}/tests/alloc.o \ + ${OBJ}/tests/bfstd.o \ + ${OBJ}/tests/bit.o \ + ${OBJ}/tests/ioq.o \ + ${OBJ}/tests/main.o \ + ${OBJ}/tests/trie.o \ + ${OBJ}/tests/xspawn.o \ + ${OBJ}/tests/xtime.o \ + ${LIBBFS} + +${BIN}/tests/xspawnee: \ + ${OBJ}/tests/xspawnee.o + +# The different flag combinations we check +INTEGRATIONS := default dfs ids eds j1 j2 j3 s +INTEGRATION_TESTS := ${INTEGRATIONS:%=check-%} + +# Check just `bfs` +check-default: ${BIN}/bfs ${ITEST_BINS} + +./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs" ${TEST_FLAGS} + +# Check the different search strategies +check-dfs check-ids check-eds: ${BIN}/bfs ${ITEST_BINS} + +./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -S ${@:check-%=%}" ${TEST_FLAGS} + +# Check various flags +check-j1 check-j2 check-j3 check-s: ${BIN}/bfs ${ITEST_BINS} + +./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -${@:check-%=%}" ${TEST_FLAGS} + +# Run the integration tests +integration-tests: ${INTEGRATION_TESTS} +.PHONY: integration-tests + +${BIN}/tests/mksock: \ + ${OBJ}/tests/mksock.o \ + ${LIBBFS} + +${BIN}/tests/xtouch: \ + ${OBJ}/tests/xtouch.o \ + ${LIBBFS} + +# `make distcheck` configurations +DISTCHECKS := distcheck-asan distcheck-tsan distcheck-release + +# Don't use msan on macOS +IS_DARWIN,Darwin := y +IS_DARWIN := ${IS_DARWIN,${OS}} +DISTCHECK_MSAN, := distcheck-msan +DISTCHECKS += ${DISTCHECK_MSAN,${IS_DARWIN}} + +# Only add a 32-bit build on 64-bit Linux +DISTCHECK_M32,Linux,x86_64 := distcheck-m32 +DISTCHECKS += ${DISTCHECK_M32,${OS},${ARCH}} + +# Test multiple configurations +distcheck: ${DISTCHECKS} +.PHONY: distcheck + +# Per-distcheck configuration +DISTCHECK_CONFIG_asan := ASAN=y UBSAN=y +DISTCHECK_CONFIG_msan := MSAN=y UBSAN=y CC=clang +DISTCHECK_CONFIG_tsan := TSAN=y UBSAN=y CC=clang +DISTCHECK_CONFIG_m32 := EXTRA_CFLAGS="-m32" PKG_CONFIG_PATH=/usr/lib32/pkgconfig USE_LIBURING=n +DISTCHECK_CONFIG_release := RELEASE=y + +${DISTCHECKS}:: + +${MAKE} -rs BUILDDIR=${BUILDDIR}/$@ config ${DISTCHECK_CONFIG_${@:distcheck-%=%}} + +${MAKE} -s BUILDDIR=${BUILDDIR}/$@ check TEST_FLAGS="--sudo --verbose=skipped" + +## Packaging (`make install`) + +DEST_PREFIX := ${DESTDIR}${PREFIX} +DEST_MANDIR := ${DESTDIR}${MANDIR} + +install:: + ${MKDIR} ${DEST_PREFIX}/bin + ${INSTALL} -m755 ${BIN}/bfs ${DEST_PREFIX}/bin/bfs + ${MKDIR} ${DEST_MANDIR}/man1 + ${INSTALL} -m644 docs/bfs.1 ${DEST_MANDIR}/man1/bfs.1 + ${MKDIR} ${DEST_PREFIX}/share/bash-completion/completions + ${INSTALL} -m644 completions/bfs.bash ${DEST_PREFIX}/share/bash-completion/completions/bfs + ${MKDIR} ${DEST_PREFIX}/share/zsh/site-functions + ${INSTALL} -m644 completions/bfs.zsh ${DEST_PREFIX}/share/zsh/site-functions/_bfs + ${MKDIR} ${DEST_PREFIX}/share/fish/vendor_completions.d + ${INSTALL} -m644 completions/bfs.fish ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish + +uninstall:: + ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs + ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs + ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish + ${RM} ${DEST_MANDIR}/man1/bfs.1 + ${RM} ${DEST_PREFIX}/bin/bfs + +# Check that `make install` works and `make uninstall` removes everything +check-install:: + +${MAKE} install DESTDIR=${BUILDDIR}/pkg + +${MAKE} uninstall DESTDIR=${BUILDDIR}/pkg + ${BIN}/bfs ${BUILDDIR}/pkg -not -type d -print -exit 1 + ${RM} -r ${BUILDDIR}/pkg diff --git a/README.md b/README.md index 17226fd..b95c16b 100644 --- a/README.md +++ b/README.md @@ -333,6 +333,7 @@ Once you have the dependencies, you can build bfs. Download one of the [releases](https://github.com/tavianator/bfs/releases) or clone the [git repo](https://github.com/tavianator/bfs). Then run + $ make config $ make This will build the `./bin/bfs` binary. @@ -342,7 +343,8 @@ Run the test suite to make sure it works correctly: If you're interested in speed, you may want to build the release version instead: - $ make release + $ make config RELEASE=y + $ make Finally, if you want to install it globally, run diff --git a/config/cc.sh b/config/cc.sh new file mode 100755 index 0000000..04b142a --- /dev/null +++ b/config/cc.sh @@ -0,0 +1,12 @@ +#!/usr/bin/env bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Run the compiler and check if it succeeded + +printf '$ %s' "$XCC" >&2 +printf ' %q' "$@" >&2 +printf ' -o /dev/null\n' >&2 + +$XCC "$@" -o /dev/null diff --git a/config/empty.c b/config/empty.c new file mode 100644 index 0000000..4fa9a5b --- /dev/null +++ b/config/empty.c @@ -0,0 +1,6 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +int main(void) { + return 0; +} diff --git a/config/libacl.c b/config/libacl.c new file mode 100644 index 0000000..877cb69 --- /dev/null +++ b/config/libacl.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + acl_free(0); + return 0; +} diff --git a/config/libcap.c b/config/libcap.c new file mode 100644 index 0000000..64188ac --- /dev/null +++ b/config/libcap.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + cap_free(0); + return 0; +} diff --git a/config/liburing.c b/config/liburing.c new file mode 100644 index 0000000..456059c --- /dev/null +++ b/config/liburing.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + io_uring_free_probe(0); + return 0; +} diff --git a/config/oniguruma.c b/config/oniguruma.c new file mode 100644 index 0000000..b834fac --- /dev/null +++ b/config/oniguruma.c @@ -0,0 +1,6 @@ +#include + +int main(void) { + onig_free(0); + return 0; +} diff --git a/config/pkg.sh b/config/pkg.sh new file mode 100755 index 0000000..6335b4b --- /dev/null +++ b/config/pkg.sh @@ -0,0 +1,44 @@ +#!/usr/bin/env bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# pkg-config wrapper that outputs a makefile fragment + +set -eu + +NAME="${1^^}" +declare -n XUSE="XUSE_$NAME" + +if [ "$XUSE" ]; then + USE="$XUSE" +elif [[ "$NOLIBS" == *y* ]]; then + USE=n +elif config/pkgconf.sh "$1"; then + USE=y +else + USE=n +fi + +printf '%s := %s\n' "USE_$NAME" "$USE" + +if [ "$USE" = y ]; then + printf 'CPPFLAGS += -DBFS_USE_%s=1\n' "$NAME" + + CFLAGS=$(config/pkgconf.sh --cflags "$1") + if [ "$CFLAGS" ]; then + printf 'CFLAGS += %s\n' "$CFLAGS" + fi + + LDFLAGS=$(config/pkgconf.sh --ldflags "$1") + if [ "$LDFLAGS" ]; then + printf 'LDFLAGS += %s\n' "$LDFLAGS" + fi + + LDLIBS=$(config/pkgconf.sh --ldlibs "$1") + if [ "$LDLIBS" ]; then + printf 'LDLIBS += %s\n' "$LDLIBS" + fi +else + printf 'CPPFLAGS += -DBFS_USE_%s=0\n' "$NAME" +fi diff --git a/config/pkgconf.sh b/config/pkgconf.sh new file mode 100755 index 0000000..070fad6 --- /dev/null +++ b/config/pkgconf.sh @@ -0,0 +1,64 @@ +#!/usr/bin/env bash + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# pkg-config wrapper with hardcoded fallbacks + +set -eu + +MODE= +if [[ "$1" == --* ]]; then + MODE="$1" + shift +fi + +if command -v "${PKG_CONFIG:-}" &>/dev/null; then + case "$MODE" in + --cflags) + "$PKG_CONFIG" --cflags "$@" + ;; + --ldflags) + "$PKG_CONFIG" --libs-only-L --libs-only-other "$@" + ;; + --ldlibs) + "$PKG_CONFIG" --libs-only-l "$@" + ;; + "") + "$PKG_CONFIG" "$@" + ;; + esac +else + for lib; do + case "$lib" in + libacl) + LDLIB=-lacl + ;; + libcap) + LDLIB=-lcap + ;; + liburing) + LDLIB=-luring + ;; + oniguruma) + LDLIB=-lonig + ;; + *) + printf 'error: Unknown package %s\n' "$lib" >&2 + exit 1 + esac + + case "$MODE" in + --ldlibs) + printf ' %s' "$LDLIB" + ;; + "") + config/cc.sh "config/$lib.c" "$LDLIB" || exit $? + ;; + esac + done + + if [ "$MODE" = "--ldlibs" ]; then + printf '\n' + fi +fi diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 7eb3a37..abf6185 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -7,9 +7,10 @@ Compiling `bfs` uses [GNU Make](https://www.gnu.org/software/make/) as its build system. A simple invocation of + $ make config $ make -should build `bfs` successfully, with no additional steps necessary. +should build `bfs` successfully. As usual with `make`, you can run a [parallel build](https://www.gnu.org/software/make/manual/html_node/Parallel.html) with `-j`. For example, to use all your cores, run `make -j$(nproc)`. @@ -17,25 +18,28 @@ For example, to use all your cores, run `make -j$(nproc)`. | Command | Description | |------------------|---------------------------------------------------------------| +| `make config` | Configures the build system | | `make` | Builds just the `bfs` binary | | `make all` | Builds everything, including the tests (but doesn't run them) | | `make check` | Builds everything, and runs the tests | | `make install` | Installs `bfs` (with man page, shell completions, etc.) | | `make uninstall` | Uninstalls `bfs` | +| `make clean` | Delete the build products | +| `make mrclean` | Delete all generated files, including the build configuration | -### Flag-like targets +### Build profiles -The build system provides a few shorthand targets for handy configurations: +The configuration system provides a few shorthand flags for handy configurations: -| Command | Description | -|----------------|-------------------------------------------------------------| -| `make release` | Build `bfs` with optimizations, LTO, and without assertions | -| `make asan` | Enable [AddressSanitizer] | -| `make lsan` | Enable [LeakSanitizer] | -| `make msan` | Enable [MemorySanitizer] | -| `make tsan` | Enable [ThreadSanitizer] | -| `make ubsan` | Enable [UndefinedBehaviorSanitizer] | -| `make gcov` | Enable [code coverage] | +| Command | Description | +|-------------------------|-------------------------------------------------------------| +| `make config RELEASE=y` | Build `bfs` with optimizations, LTO, and without assertions | +| `make config ASAN=y` | Enable [AddressSanitizer] | +| `make config LSAN=y` | Enable [LeakSanitizer] | +| `make config MSAN=y` | Enable [MemorySanitizer] | +| `make config TSAN=y` | Enable [ThreadSanitizer] | +| `make config UBSAN=y` | Enable [UndefinedBehaviorSanitizer] | +| `make config GCOV=y` | Enable [code coverage] | [AddressSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizer [LeakSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#stand-alone-mode @@ -44,38 +48,38 @@ The build system provides a few shorthand targets for handy configurations: [UndefinedBehaviorSanitizer]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html [code coverage]: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html -You can combine multiple flags and other targets (e.g. `make asan ubsan check`), but not all of them will work together. +You can combine multiple profiles (e.g. `make config ASAN=y UBSAN=y`), but not all of them will work together. ### Flags -Other flags are controlled with `make` variables and/or environment variables. -Here are some of the common ones; check the [`GNUmakefile`](/GNUmakefile) for more. - -| Flag | Description | -|----------------------------------|---------------------------------------------| -| `CC` | The C compiler to use, e.g. `make CC=clang` | -| `CFLAGS`
`EXTRA_CFLAGS` | Override/add to the default compiler flags | -| `LDFLAGS`
`EXTRA_LDFLAGS` | Override/add to the linker flags | -| `USE_ACL`
`USE_LIBCAP`
... | Enable/disable [optional dependencies] | -| `TEST_FLAGS` | `tests.sh` flags for `make check` | -| `BUILDDIR` | The build output directory (default: `.`) | -| `DESTDIR` | The root directory for `make install` | -| `PREFIX` | The installation prefix (default: `/usr`) | -| `MANDIR` | The man page installation directory | +Other flags can be specified on the `make config` command line or in the environment. +Here are some of the common ones; check the [`Makefile`](/Makefile) for more. + +| Flag | Description | +|----------------------------------|----------------------------------------------------| +| `CC` | The C compiler to use, e.g. `make config CC=clang` | +| `CFLAGS`
`EXTRA_CFLAGS` | Override/add to the default compiler flags | +| `LDFLAGS`
`EXTRA_LDFLAGS` | Override/add to the linker flags | +| `USE_ACL`
`USE_LIBCAP`
... | Enable/disable [optional dependencies] | +| `TEST_FLAGS` | `tests.sh` flags for `make check` | +| `BUILDDIR` | The build output directory (default: `.`) | +| `DESTDIR` | The root directory for `make install` | +| `PREFIX` | The installation prefix (default: `/usr`) | +| `MANDIR` | The man page installation directory | [optional dependencies]: #dependencies ### Dependencies `bfs` depends on some system libraries for some of its features. -These dependencies are optional, and can be turned off at build time if necessary by setting the appropriate variable to the empty string (e.g. `make USE_ONIGURUMA=`). +These dependencies are optional, and can be turned off in `make config` if necessary by setting the appropriate variable to `n` (e.g. `make config USE_ONIGURUMA=n`). -| Dependency | Platforms | `make` flag | -|-------------|------------|-----------------| -| [acl] | Linux only | `USE_ACL` | -| [libcap] | Linux only | `USE_LIBCAP` | -| [liburing] | Linux only | `USE_LIBURING` | -| [Oniguruma] | All | `USE_ONIGURUMA` | +| Dependency | Platforms | `make config` flag | +|-------------|------------|--------------------| +| [acl] | Linux only | `USE_ACL` | +| [libcap] | Linux only | `USE_LIBCAP` | +| [liburing] | Linux only | `USE_LIBURING` | +| [Oniguruma] | All | `USE_ONIGURUMA` | [acl]: https://savannah.nongnu.org/projects/acl [libcap]: https://sites.google.com/site/fullycapable/ @@ -84,21 +88,22 @@ These dependencies are optional, and can be turned off at build time if necessar ### Dependency tracking -The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in the [`GNUmakefile`](/GNUmakefile)). +The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in the [`Makefile`](/Makefile)). So if you edit a header file, `make` will rebuild the necessary object files ensuring they don't go out of sync. +We also add a dependency on the current configuration, so you can change configurations and rebuild without having to `make clean`. + We go one step further than most build systems by tracking the flags that were used for the previous compilation. That means you can change configurations without having to `make clean`. For example, + $ make config + $ make + $ make config RELEASE=y $ make - $ make release will build the project in debug mode and then rebuild it in release mode. -A side effect of this may be surprising: `make check` by itself will rebuild the project in the default configuration. -To test a different configuration, you'll have to repeat it (e.g. `make release check`). - Testing ------- diff --git a/docs/USAGE.md b/docs/USAGE.md index 071c95b..70f8475 100644 --- a/docs/USAGE.md +++ b/docs/USAGE.md @@ -7,8 +7,8 @@ When invoked with no arguments, `bfs` will list everything under the current dir ```console $ bfs . -./GNUmakefile ./LICENSE +./Makefile ./README.md ./completions ./docs diff --git a/src/config.h b/src/config.h index 506ad3e..2eff1fc 100644 --- a/src/config.h +++ b/src/config.h @@ -29,13 +29,14 @@ #ifndef BFS_COMMAND # define BFS_COMMAND "bfs" #endif -#ifndef BFS_VERSION -# define BFS_VERSION "3.1.3" -#endif #ifndef BFS_HOMEPAGE # define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" #endif +// This is a symbol instead of a literal so we don't have to rebuild everything +// when the version number changes +extern const char bfs_version[]; + // Check for system headers #ifdef __has_include @@ -97,10 +98,10 @@ # define BFS_USE_PATHS_H BFS_HAS_PATHS_H #endif #ifndef BFS_USE_SYS_ACL_H -# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__) +# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) #endif #ifndef BFS_USE_SYS_CAPABILITY_H -# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__) +# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) #endif #ifndef BFS_USE_SYS_EXTATTR_H # define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) diff --git a/src/main.c b/src/main.c index e120f03..b4d65ce 100644 --- a/src/main.c +++ b/src/main.c @@ -40,6 +40,7 @@ * - thread.h (multi-threading) * - trie.[ch] (a trie set/map implementation) * - typo.[ch] (fuzzy matching for typos) + * - version.c (defines the version number) * - xregex.[ch] (regular expression support) * - xspawn.[ch] (spawns processes) * - xtime.[ch] (date/time handling utilities) diff --git a/src/parse.c b/src/parse.c index 2dfcab2..38ebf3f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -2909,7 +2909,7 @@ static struct bfs_expr *parse_help(struct bfs_parser *parser, int arg1, int arg2 * "Parse" -version. */ static struct bfs_expr *parse_version(struct bfs_parser *parser, int arg1, int arg2) { - cfprintf(parser->ctx->cout, "${ex}%s${rs} ${bld}%s${rs}\n\n", BFS_COMMAND, BFS_VERSION); + cfprintf(parser->ctx->cout, "${ex}%s${rs} ${bld}%s${rs}\n\n", BFS_COMMAND, bfs_version); printf("%s\n", BFS_HOMEPAGE); diff --git a/src/version.c b/src/version.c new file mode 100644 index 0000000..736f3d5 --- /dev/null +++ b/src/version.c @@ -0,0 +1,6 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include "config.h" + +const char bfs_version[] = BFS_VERSION; diff --git a/tests/run.sh b/tests/run.sh index 720515d..ab1ed6d 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -156,7 +156,7 @@ comake() { -f "$TESTS/tests.mk" \ DONE=$DONE_PIPE \ READY=$READY_PIPE \ - "${TEST_CASES[@]/#/tests/}" \ + "${!TEST_CASES[@]}" \ /dev/null } diff --git a/tests/tests.mk b/tests/tests.mk index 5bf4f6c..035ca79 100644 --- a/tests/tests.mk +++ b/tests/tests.mk @@ -1,7 +1,13 @@ # Copyright © Tavian Barnes # SPDX-License-Identifier: 0BSD -# GNU makefile that exposes make's job control to tests.sh +# Makefile that exposes make's job control to tests.sh -tests/%: +# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to +.OBJDIR: . + +# Turn off implicit rules +.SUFFIXES: + +.DEFAULT:: bash -c 'printf . >&$(READY) && read -r -N1 -u$(DONE)' -- cgit v1.2.3 From d0804fcf6b690de364c35d8354b21f72422df079 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 9 Apr 2024 20:47:11 -0400 Subject: tests: Move newer_link out of posix/ POSIX has clarified that it's unspecified whether -newer uses times from stat() or lstat(), because implementations vary. It does specify that it must fall back to lstat() for broken links, so test that. Link: https://austingroupbugs.net/view.php?id=1776 --- tests/gnu/newer_link.out | 1 + tests/gnu/newer_link.sh | 1 + tests/posix/newer_broken.out | 1 + tests/posix/newer_broken.sh | 4 ++++ tests/posix/newer_link.out | 1 - tests/posix/newer_link.sh | 1 - 6 files changed, 7 insertions(+), 2 deletions(-) create mode 100644 tests/gnu/newer_link.out create mode 100644 tests/gnu/newer_link.sh create mode 100644 tests/posix/newer_broken.out create mode 100644 tests/posix/newer_broken.sh delete mode 100644 tests/posix/newer_link.out delete mode 100644 tests/posix/newer_link.sh (limited to 'tests') diff --git a/tests/gnu/newer_link.out b/tests/gnu/newer_link.out new file mode 100644 index 0000000..d2dcdd1 --- /dev/null +++ b/tests/gnu/newer_link.out @@ -0,0 +1 @@ +times diff --git a/tests/gnu/newer_link.sh b/tests/gnu/newer_link.sh new file mode 100644 index 0000000..685ac78 --- /dev/null +++ b/tests/gnu/newer_link.sh @@ -0,0 +1 @@ +bfs_diff times -newer times/l diff --git a/tests/posix/newer_broken.out b/tests/posix/newer_broken.out new file mode 100644 index 0000000..d2dcdd1 --- /dev/null +++ b/tests/posix/newer_broken.out @@ -0,0 +1 @@ +times diff --git a/tests/posix/newer_broken.sh b/tests/posix/newer_broken.sh new file mode 100644 index 0000000..dccaa73 --- /dev/null +++ b/tests/posix/newer_broken.sh @@ -0,0 +1,4 @@ +ln -s nowhere "$TEST/broken" +"$XTOUCH" -h -t "1991-12-14 00:03" "$TEST/broken" + +bfs_diff times -newer "$TEST/broken" diff --git a/tests/posix/newer_link.out b/tests/posix/newer_link.out deleted file mode 100644 index d2dcdd1..0000000 --- a/tests/posix/newer_link.out +++ /dev/null @@ -1 +0,0 @@ -times diff --git a/tests/posix/newer_link.sh b/tests/posix/newer_link.sh deleted file mode 100644 index 685ac78..0000000 --- a/tests/posix/newer_link.sh +++ /dev/null @@ -1 +0,0 @@ -bfs_diff times -newer times/l -- cgit v1.2.3 From 98c539eeda8f9adfd22a3b2b6ece4fe1ca06b3b4 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Tue, 16 Apr 2024 11:52:44 -0400 Subject: tests: Quiet diff On Solaris/Illumos, `diff` prints "No differences encountered" if the files are the same. Guard it with `cmp -s` so we get no output for passing tests. --- tests/run.sh | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'tests') diff --git a/tests/run.sh b/tests/run.sh index ab1ed6d..ad9c0be 100644 --- a/tests/run.sh +++ b/tests/run.sh @@ -430,7 +430,7 @@ make_xattrs() { EX_DIFF=20 # Detect colored diff support -if diff --color /dev/null /dev/null 2>/dev/null; then +if diff --color /dev/null /dev/null &>/dev/null; then DIFF="diff --color" else DIFF="diff" @@ -447,7 +447,7 @@ diff_output() { if ((UPDATE)); then cp "$OUT" "$GOLD" - else + elif ! cmp -s "$GOLD" "$OUT"; then $DIFF -u "$GOLD" "$OUT" >&$DUPERR fi } -- cgit v1.2.3 From 1501910dd0d09c50057c8358901663a87333bd8f Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 Apr 2024 14:51:42 -0400 Subject: tests: Add ../src to the include path --- config/flags.mk | 1 + tests/alloc.c | 6 +++--- tests/bfstd.c | 6 +++--- tests/bit.c | 6 +++--- tests/ioq.c | 10 +++++----- tests/main.c | 6 +++--- tests/mksock.c | 2 +- tests/tests.h | 4 ++-- tests/trie.c | 6 +++--- tests/xspawn.c | 10 +++++----- tests/xtime.c | 8 ++++---- tests/xtouch.c | 8 ++++---- 12 files changed, 37 insertions(+), 36 deletions(-) (limited to 'tests') diff --git a/config/flags.mk b/config/flags.mk index 1043779..d02cc8b 100644 --- a/config/flags.mk +++ b/config/flags.mk @@ -28,6 +28,7 @@ export XLDLIBS=${LDLIBS} # Immutable flags export BFS_CPPFLAGS= \ + -Isrc \ -D__EXTENSIONS__ \ -D_ATFILE_SOURCE \ -D_BSD_SOURCE \ diff --git a/tests/alloc.c b/tests/alloc.c index 9f08111..54b84ba 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -2,9 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/alloc.h" -#include "../src/config.h" -#include "../src/diag.h" +#include "alloc.h" +#include "config.h" +#include "diag.h" #include #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index dc5ceaa..5e408ca 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -2,9 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/bfstd.h" -#include "../src/config.h" -#include "../src/diag.h" +#include "bfstd.h" +#include "config.h" +#include "diag.h" #include #include #include diff --git a/tests/bit.c b/tests/bit.c index 6548c30..b444e50 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -2,9 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/bit.h" -#include "../src/config.h" -#include "../src/diag.h" +#include "bit.h" +#include "config.h" +#include "diag.h" #include #include #include diff --git a/tests/ioq.c b/tests/ioq.c index 1ce8f75..a69f2bf 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -2,11 +2,11 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/ioq.h" -#include "../src/bfstd.h" -#include "../src/config.h" -#include "../src/diag.h" -#include "../src/dir.h" +#include "ioq.h" +#include "bfstd.h" +#include "config.h" +#include "diag.h" +#include "dir.h" #include #include #include diff --git a/tests/main.c b/tests/main.c index 69903d4..281c417 100644 --- a/tests/main.c +++ b/tests/main.c @@ -6,9 +6,9 @@ */ #include "tests.h" -#include "../src/bfstd.h" -#include "../src/color.h" -#include "../src/config.h" +#include "bfstd.h" +#include "color.h" +#include "config.h" #include #include #include diff --git a/tests/mksock.c b/tests/mksock.c index f3b61da..5786cb6 100644 --- a/tests/mksock.c +++ b/tests/mksock.c @@ -6,7 +6,7 @@ * program does the job. */ -#include "../src/bfstd.h" +#include "bfstd.h" #include #include #include diff --git a/tests/tests.h b/tests/tests.h index 351badb..d61ffd7 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -8,8 +8,8 @@ #ifndef BFS_TESTS_H #define BFS_TESTS_H -#include "../src/config.h" -#include "../src/diag.h" +#include "config.h" +#include "diag.h" /** Unit test function type. */ typedef bool test_fn(void); diff --git a/tests/trie.c b/tests/trie.c index fec0de2..2a6eb48 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -2,9 +2,9 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/trie.h" -#include "../src/config.h" -#include "../src/diag.h" +#include "trie.h" +#include "config.h" +#include "diag.h" #include #include diff --git a/tests/xspawn.c b/tests/xspawn.c index c1bac36..fd8362e 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -2,11 +2,11 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/alloc.h" -#include "../src/bfstd.h" -#include "../src/config.h" -#include "../src/dstring.h" -#include "../src/xspawn.h" +#include "alloc.h" +#include "bfstd.h" +#include "config.h" +#include "dstring.h" +#include "xspawn.h" #include #include #include diff --git a/tests/xtime.c b/tests/xtime.c index f85402e..fd7aa0f 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -2,10 +2,10 @@ // SPDX-License-Identifier: 0BSD #include "tests.h" -#include "../src/xtime.h" -#include "../src/bfstd.h" -#include "../src/config.h" -#include "../src/diag.h" +#include "xtime.h" +#include "bfstd.h" +#include "config.h" +#include "diag.h" #include #include #include diff --git a/tests/xtouch.c b/tests/xtouch.c index fad272f..b1daec7 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD -#include "../src/bfstd.h" -#include "../src/config.h" -#include "../src/sanity.h" -#include "../src/xtime.h" +#include "bfstd.h" +#include "config.h" +#include "sanity.h" +#include "xtime.h" #include #include #include -- cgit v1.2.3 From c66379749f423413913b406609dfe9311ba6e555 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Thu, 18 Apr 2024 14:53:56 -0400 Subject: Rename config.h to prelude.h --- src/alloc.c | 2 +- src/alloc.h | 2 +- src/bar.c | 2 +- src/bfstd.c | 2 +- src/bfstd.h | 2 +- src/bftw.c | 2 +- src/bit.h | 2 +- src/color.c | 2 +- src/color.h | 2 +- src/config.h | 377 --------------------------------------------------------- src/ctx.h | 2 +- src/diag.c | 2 +- src/diag.h | 2 +- src/dir.c | 2 +- src/dstring.c | 2 +- src/dstring.h | 2 +- src/eval.c | 2 +- src/eval.h | 2 +- src/exec.c | 2 +- src/expr.h | 2 +- src/fsade.c | 2 +- src/fsade.h | 2 +- src/ioq.c | 2 +- src/ioq.h | 2 +- src/main.c | 4 +- src/mtab.c | 2 +- src/mtab.h | 2 +- src/opt.c | 2 +- src/parse.c | 2 +- src/prelude.h | 377 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ src/printf.c | 2 +- src/pwcache.c | 2 +- src/sanity.h | 2 +- src/stat.c | 2 +- src/stat.h | 2 +- src/thread.c | 2 +- src/thread.h | 2 +- src/trie.c | 2 +- src/xregex.c | 2 +- src/xspawn.c | 2 +- src/xspawn.h | 2 +- src/xtime.c | 2 +- tests/alloc.c | 2 +- tests/bfstd.c | 2 +- tests/bit.c | 2 +- tests/ioq.c | 2 +- tests/main.c | 2 +- tests/tests.h | 2 +- tests/trie.c | 2 +- tests/xspawn.c | 2 +- tests/xtime.c | 2 +- tests/xtouch.c | 2 +- 52 files changed, 428 insertions(+), 428 deletions(-) delete mode 100644 src/config.h create mode 100644 src/prelude.h (limited to 'tests') diff --git a/src/alloc.c b/src/alloc.c index b65d0c5..ec8608f 100644 --- a/src/alloc.c +++ b/src/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include diff --git a/src/alloc.h b/src/alloc.h index ae055bc..095134a 100644 --- a/src/alloc.h +++ b/src/alloc.h @@ -8,7 +8,7 @@ #ifndef BFS_ALLOC_H #define BFS_ALLOC_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/bar.c b/src/bar.c index 8ab4112..184d9a0 100644 --- a/src/bar.c +++ b/src/bar.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bar.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "dstring.h" #include #include diff --git a/src/bfstd.c b/src/bfstd.c index 2499f00..e1b4804 100644 --- a/src/bfstd.c +++ b/src/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/bfstd.h b/src/bfstd.h index fc22971..42f5d5b 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -8,7 +8,7 @@ #ifndef BFS_BFSTD_H #define BFS_BFSTD_H -#include "config.h" +#include "prelude.h" #include "sanity.h" #include diff --git a/src/bftw.c b/src/bftw.c index 6130c44..c4d3c17 100644 --- a/src/bftw.c +++ b/src/bftw.c @@ -18,10 +18,10 @@ * various helper functions to take fewer parameters. */ +#include "prelude.h" #include "bftw.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/bit.h b/src/bit.h index 69df21e..17cfbcf 100644 --- a/src/bit.h +++ b/src/bit.h @@ -8,7 +8,7 @@ #ifndef BFS_BIT_H #define BFS_BIT_H -#include "config.h" +#include "prelude.h" #include #include diff --git a/src/color.c b/src/color.c index 8c32a68..f004bf2 100644 --- a/src/color.c +++ b/src/color.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "color.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "dstring.h" diff --git a/src/color.h b/src/color.h index e3e7973..3278cd6 100644 --- a/src/color.h +++ b/src/color.h @@ -8,7 +8,7 @@ #ifndef BFS_COLOR_H #define BFS_COLOR_H -#include "config.h" +#include "prelude.h" #include "dstring.h" #include diff --git a/src/config.h b/src/config.h deleted file mode 100644 index 2eff1fc..0000000 --- a/src/config.h +++ /dev/null @@ -1,377 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -/** - * Configuration and feature/platform detection. - */ - -#ifndef BFS_CONFIG_H -#define BFS_CONFIG_H - -// Possible __STDC_VERSION__ values - -#define C95 199409L -#define C99 199901L -#define C11 201112L -#define C17 201710L -#define C23 202311L - -#include - -#if __STDC_VERSION__ < C23 -# include -# include -# include -#endif - -// bfs packaging configuration - -#ifndef BFS_COMMAND -# define BFS_COMMAND "bfs" -#endif -#ifndef BFS_HOMEPAGE -# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" -#endif - -// This is a symbol instead of a literal so we don't have to rebuild everything -// when the version number changes -extern const char bfs_version[]; - -// Check for system headers - -#ifdef __has_include - -#if __has_include() -# define BFS_HAS_MNTENT_H true -#endif -#if __has_include() -# define BFS_HAS_PATHS_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_ACL_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_CAPABILITY_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_EXTATTR_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_MKDEV_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_PARAM_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_SYSMACROS_H true -#endif -#if __has_include() -# define BFS_HAS_SYS_XATTR_H true -#endif -#if __has_include() -# define BFS_HAS_THREADS_H true -#endif -#if __has_include() -# define BFS_HAS_UTIL_H true -#endif - -#else // !__has_include - -#define BFS_HAS_MNTENT_H __GLIBC__ -#define BFS_HAS_PATHS_H true -#define BFS_HAS_SYS_ACL_H true -#define BFS_HAS_SYS_CAPABILITY_H __linux__ -#define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ -#define BFS_HAS_SYS_MKDEV_H false -#define BFS_HAS_SYS_PARAM_H true -#define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ -#define BFS_HAS_SYS_XATTR_H __linux__ -#define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) -#define BFS_HAS_UTIL_H __NetBSD__ - -#endif // !__has_include - -#ifndef BFS_USE_MNTENT_H -# define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H -#endif -#ifndef BFS_USE_PATHS_H -# define BFS_USE_PATHS_H BFS_HAS_PATHS_H -#endif -#ifndef BFS_USE_SYS_ACL_H -# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) -#endif -#ifndef BFS_USE_SYS_CAPABILITY_H -# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) -#endif -#ifndef BFS_USE_SYS_EXTATTR_H -# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) -#endif -#ifndef BFS_USE_SYS_MKDEV_H -# define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H -#endif -#ifndef BFS_USE_SYS_PARAM_H -# define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H -#endif -#ifndef BFS_USE_SYS_SYSMACROS_H -# define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H -#endif -#ifndef BFS_USE_SYS_XATTR_H -# define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H -#endif -#ifndef BFS_USE_THREADS_H -# define BFS_USE_THREADS_H BFS_HAS_THREADS_H -#endif -#ifndef BFS_USE_UTIL_H -# define BFS_USE_UTIL_H BFS_HAS_UTIL_H -#endif - -// Stub out feature detection on old/incompatible compilers - -#ifndef __has_feature -# define __has_feature(feat) false -#endif - -#ifndef __has_c_attribute -# define __has_c_attribute(attr) false -#endif - -#ifndef __has_attribute -# define __has_attribute(attr) false -#endif - -// Platform detection - -// Get the definition of BSD if available -#if BFS_USE_SYS_PARAM_H -# include -#endif - -#ifndef __GLIBC_PREREQ -# define __GLIBC_PREREQ(maj, min) false -#endif - -#ifndef __NetBSD_Prereq__ -# define __NetBSD_Prereq__(maj, min, patch) false -#endif - -// Fundamental utilities - -/** - * Get the length of an array. - */ -#define countof(array) (sizeof(array) / sizeof(0[array])) - -/** - * False sharing/destructive interference/largest cache line size. - */ -#ifdef __GCC_DESTRUCTIVE_SIZE -# define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE -#else -# define FALSE_SHARING_SIZE 64 -#endif - -/** - * True sharing/constructive interference/smallest cache line size. - */ -#ifdef __GCC_CONSTRUCTIVE_SIZE -# define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE -#else -# define TRUE_SHARING_SIZE 64 -#endif - -/** - * Alignment specifier that avoids false sharing. - */ -#define cache_align alignas(FALSE_SHARING_SIZE) - -#if __COSMOPOLITAN__ -typedef long double max_align_t; -#endif - -// Wrappers for attributes - -/** - * Silence warnings about switch/case fall-throughs. - */ -#if __has_attribute(fallthrough) -# define fallthru __attribute__((fallthrough)) -#else -# define fallthru ((void)0) -#endif - -/** - * Silence warnings about unused declarations. - */ -#if __has_attribute(unused) -# define attr_maybe_unused __attribute__((unused)) -#else -# define attr_maybe_unused -#endif - -/** - * Warn if a value is unused. - */ -#if __has_attribute(warn_unused_result) -# define attr_nodiscard __attribute__((warn_unused_result)) -#else -# define attr_nodiscard -#endif - -/** - * Hint to avoid inlining a function. - */ -#if __has_attribute(noinline) -# define attr_noinline __attribute__((noinline)) -#else -# define attr_noinline -#endif - -/** - * Hint that a function is unlikely to be called. - */ -#if __has_attribute(cold) -# define attr_cold attr_noinline __attribute__((cold)) -#else -# define attr_cold attr_noinline -#endif - -/** - * Adds compiler warnings for bad printf()-style function calls, if supported. - */ -#if __has_attribute(format) -# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) -#else -# define attr_printf(fmt, args) -#endif - -/** - * Annotates allocator-like functions. - */ -#if __has_attribute(malloc) -# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC -# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) -# else -# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) -# endif -#else -# define attr_malloc(...) attr_nodiscard -#endif - -/** - * Specifies that a function returns allocations with a given alignment. - */ -#if __has_attribute(alloc_align) -# define attr_alloc_align(param) __attribute__((alloc_align(param))) -#else -# define attr_alloc_align(param) -#endif - -/** - * Specifies that a function returns allocations with a given size. - */ -#if __has_attribute(alloc_size) -# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) -#else -# define attr_alloc_size(...) -#endif - -/** - * Shorthand for attr_alloc_align() and attr_alloc_size(). - */ -#define attr_aligned_alloc(align, ...) \ - attr_alloc_align(align) \ - attr_alloc_size(__VA_ARGS__) - -/** - * Check if function multiversioning via GNU indirect functions (ifunc) is supported. - */ -#ifndef BFS_USE_TARGET_CLONES -# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) -# define BFS_USE_TARGET_CLONES true -# endif -#endif - -/** - * Apply the target_clones attribute, if available. - */ -#if BFS_USE_TARGET_CLONES -# define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) -#else -# define attr_target_clones(...) -#endif - -/** - * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to - * - * attr_a - * attr_b(c) - * attr_d - */ -#define attr(...) \ - attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) - -/** - * attr() helper. For exposition, pretend we support only 2 args, instead of 9. - * There are a few cases: - * - * attr() - * => attr__(attr_, none, none) - * => attr_ => - * attr_none => - * attr_too_many_none() => - * - * attr(a) - * => attr__(attr_a, none, none) - * => attr_a => __attribute__((a)) - * attr_none => - * attr_too_many_none() => - * - * attr(a, b(c)) - * => attr__(attr_a, b(c), none, none) - * => attr_a => __attribute__((a)) - * attr_b(c) => __attribute__((b(c))) - * attr_too_many_none(none) => - * - * attr(a, b(c), d) - * => attr__(attr_a, b(c), d, none, none) - * => attr_a => __attribute__((a)) - * attr_b(c) => __attribute__((b(c))) - * attr_too_many_d(none, none) => error - * - * Some attribute names are the same as standard library functions, e.g. printf. - * Standard libraries are permitted to define these functions as macros, like - * - * #define printf(...) __builtin_printf(__VA_ARGS__) - * - * The token paste in - * - * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) - * - * is necessary to prevent macro expansion before evaluating attr__(). - * Otherwise, we could get - * - * attr(printf(1, 2)) - * => attr__(__builtin_printf(1, 2), none, none) - * => attr____builtin_printf(1, 2) - * => error - */ -#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ - a1 \ - attr_##a2 \ - attr_##a3 \ - attr_##a4 \ - attr_##a5 \ - attr_##a6 \ - attr_##a7 \ - attr_##a8 \ - attr_##a9 \ - attr_too_many_##none(__VA_ARGS__) - -// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) -#define attr_none -// Ignore `attr_` from expanding 0-argument attr() -#define attr_ -// Only trigger an error on more than 9 arguments -#define attr_too_many_none(...) - -#endif // BFS_CONFIG_H diff --git a/src/ctx.h b/src/ctx.h index e14db21..fc3020c 100644 --- a/src/ctx.h +++ b/src/ctx.h @@ -8,9 +8,9 @@ #ifndef BFS_CTX_H #define BFS_CTX_H +#include "prelude.h" #include "alloc.h" #include "bftw.h" -#include "config.h" #include "diag.h" #include "expr.h" #include "trie.h" diff --git a/src/diag.c b/src/diag.c index cb27b92..deb6f26 100644 --- a/src/diag.c +++ b/src/diag.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "diag.h" #include "alloc.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "dstring.h" #include "expr.h" diff --git a/src/diag.h b/src/diag.h index 4054c48..2b13609 100644 --- a/src/diag.h +++ b/src/diag.h @@ -8,7 +8,7 @@ #ifndef BFS_DIAG_H #define BFS_DIAG_H -#include "config.h" +#include "prelude.h" #include /** diff --git a/src/dir.c b/src/dir.c index 98518f2..53c9be3 100644 --- a/src/dir.c +++ b/src/dir.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dir.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "trie.h" diff --git a/src/dstring.c b/src/dstring.c index 10b0fad..913dda8 100644 --- a/src/dstring.c +++ b/src/dstring.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "dstring.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include #include diff --git a/src/dstring.h b/src/dstring.h index 6006199..9ea7eb9 100644 --- a/src/dstring.h +++ b/src/dstring.h @@ -8,8 +8,8 @@ #ifndef BFS_DSTRING_H #define BFS_DSTRING_H +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include #include diff --git a/src/eval.c b/src/eval.c index d0112c2..b103912 100644 --- a/src/eval.c +++ b/src/eval.c @@ -5,12 +5,12 @@ * Implementation of all the primary expressions. */ +#include "prelude.h" #include "eval.h" #include "bar.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/eval.h b/src/eval.h index ae43628..4dd7996 100644 --- a/src/eval.h +++ b/src/eval.h @@ -9,7 +9,7 @@ #ifndef BFS_EVAL_H #define BFS_EVAL_H -#include "config.h" +#include "prelude.h" struct bfs_ctx; struct bfs_expr; diff --git a/src/exec.c b/src/exec.c index 60bfd28..e782d49 100644 --- a/src/exec.c +++ b/src/exec.c @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "exec.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dstring.h" diff --git a/src/expr.h b/src/expr.h index 75cb5fd..7bcace7 100644 --- a/src/expr.h +++ b/src/expr.h @@ -8,8 +8,8 @@ #ifndef BFS_EXPR_H #define BFS_EXPR_H +#include "prelude.h" #include "color.h" -#include "config.h" #include "eval.h" #include "stat.h" #include diff --git a/src/fsade.c b/src/fsade.c index 0810c7f..34a4d57 100644 --- a/src/fsade.c +++ b/src/fsade.c @@ -1,11 +1,11 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "fsade.h" #include "atomic.h" #include "bfstd.h" #include "bftw.h" -#include "config.h" #include "dir.h" #include "dstring.h" #include "sanity.h" diff --git a/src/fsade.h b/src/fsade.h index 1f1dbfc..6300852 100644 --- a/src/fsade.h +++ b/src/fsade.h @@ -9,7 +9,7 @@ #ifndef BFS_FSADE_H #define BFS_FSADE_H -#include "config.h" +#include "prelude.h" #define BFS_CAN_CHECK_ACL BFS_USE_SYS_ACL_H diff --git a/src/ioq.c b/src/ioq.c index b936681..189bdac 100644 --- a/src/ioq.c +++ b/src/ioq.c @@ -118,12 +118,12 @@ * [1]: https://arxiv.org/abs/2201.02179 */ +#include "prelude.h" #include "ioq.h" #include "alloc.h" #include "atomic.h" #include "bfstd.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "dir.h" #include "stat.h" diff --git a/src/ioq.h b/src/ioq.h index 818eea6..d8e1573 100644 --- a/src/ioq.h +++ b/src/ioq.h @@ -8,7 +8,7 @@ #ifndef BFS_IOQ_H #define BFS_IOQ_H -#include "config.h" +#include "prelude.h" #include "dir.h" #include "stat.h" #include diff --git a/src/main.c b/src/main.c index e120f03..9d8b206 100644 --- a/src/main.c +++ b/src/main.c @@ -26,7 +26,7 @@ * - bit.h (bit manipulation) * - bfstd.[ch] (standard library wrappers/polyfills) * - color.[ch] (for pretty terminal colors) - * - config.h (configuration and feature/platform detection) + * - prelude.h (configuration and feature/platform detection) * - diag.[ch] (formats diagnostic messages) * - dir.[ch] (a directory API facade) * - dstring.[ch] (a dynamic string library) @@ -45,8 +45,8 @@ * - xtime.[ch] (date/time handling utilities) */ +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "eval.h" diff --git a/src/mtab.c b/src/mtab.c index 86ae151..7905d14 100644 --- a/src/mtab.c +++ b/src/mtab.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "mtab.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "stat.h" #include "trie.h" #include diff --git a/src/mtab.h b/src/mtab.h index d99d78f..67290c2 100644 --- a/src/mtab.h +++ b/src/mtab.h @@ -8,7 +8,7 @@ #ifndef BFS_MTAB_H #define BFS_MTAB_H -#include "config.h" +#include "prelude.h" struct bfs_stat; diff --git a/src/opt.c b/src/opt.c index b74b4e1..ffc795b 100644 --- a/src/opt.c +++ b/src/opt.c @@ -25,11 +25,11 @@ * effects are reachable at all, skipping the traversal if not. */ +#include "prelude.h" #include "opt.h" #include "bftw.h" #include "bit.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/parse.c b/src/parse.c index a3e32fe..c2ae58f 100644 --- a/src/parse.c +++ b/src/parse.c @@ -8,12 +8,12 @@ * flags like always-true options, and skipping over paths wherever they appear. */ +#include "prelude.h" #include "parse.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/prelude.h b/src/prelude.h new file mode 100644 index 0000000..c3a0752 --- /dev/null +++ b/src/prelude.h @@ -0,0 +1,377 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +/** + * Configuration and feature/platform detection. + */ + +#ifndef BFS_PRELUDE_H +#define BFS_PRELUDE_H + +// Possible __STDC_VERSION__ values + +#define C95 199409L +#define C99 199901L +#define C11 201112L +#define C17 201710L +#define C23 202311L + +#include + +#if __STDC_VERSION__ < C23 +# include +# include +# include +#endif + +// bfs packaging configuration + +#ifndef BFS_COMMAND +# define BFS_COMMAND "bfs" +#endif +#ifndef BFS_HOMEPAGE +# define BFS_HOMEPAGE "https://tavianator.com/projects/bfs.html" +#endif + +// This is a symbol instead of a literal so we don't have to rebuild everything +// when the version number changes +extern const char bfs_version[]; + +// Check for system headers + +#ifdef __has_include + +#if __has_include() +# define BFS_HAS_MNTENT_H true +#endif +#if __has_include() +# define BFS_HAS_PATHS_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_ACL_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_CAPABILITY_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_EXTATTR_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_MKDEV_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_PARAM_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_SYSMACROS_H true +#endif +#if __has_include() +# define BFS_HAS_SYS_XATTR_H true +#endif +#if __has_include() +# define BFS_HAS_THREADS_H true +#endif +#if __has_include() +# define BFS_HAS_UTIL_H true +#endif + +#else // !__has_include + +#define BFS_HAS_MNTENT_H __GLIBC__ +#define BFS_HAS_PATHS_H true +#define BFS_HAS_SYS_ACL_H true +#define BFS_HAS_SYS_CAPABILITY_H __linux__ +#define BFS_HAS_SYS_EXTATTR_H __FreeBSD__ +#define BFS_HAS_SYS_MKDEV_H false +#define BFS_HAS_SYS_PARAM_H true +#define BFS_HAS_SYS_SYSMACROS_H __GLIBC__ +#define BFS_HAS_SYS_XATTR_H __linux__ +#define BFS_HAS_THREADS_H (!__STDC_NO_THREADS__) +#define BFS_HAS_UTIL_H __NetBSD__ + +#endif // !__has_include + +#ifndef BFS_USE_MNTENT_H +# define BFS_USE_MNTENT_H BFS_HAS_MNTENT_H +#endif +#ifndef BFS_USE_PATHS_H +# define BFS_USE_PATHS_H BFS_HAS_PATHS_H +#endif +#ifndef BFS_USE_SYS_ACL_H +# define BFS_USE_SYS_ACL_H (BFS_HAS_SYS_ACL_H && !__illumos__ && (!__linux__ || BFS_USE_LIBACL)) +#endif +#ifndef BFS_USE_SYS_CAPABILITY_H +# define BFS_USE_SYS_CAPABILITY_H (BFS_HAS_SYS_CAPABILITY_H && !__FreeBSD__ && (!__linux__ || BFS_USE_LIBCAP)) +#endif +#ifndef BFS_USE_SYS_EXTATTR_H +# define BFS_USE_SYS_EXTATTR_H (BFS_HAS_SYS_EXTATTR_H && !__DragonFly__) +#endif +#ifndef BFS_USE_SYS_MKDEV_H +# define BFS_USE_SYS_MKDEV_H BFS_HAS_SYS_MKDEV_H +#endif +#ifndef BFS_USE_SYS_PARAM_H +# define BFS_USE_SYS_PARAM_H BFS_HAS_SYS_PARAM_H +#endif +#ifndef BFS_USE_SYS_SYSMACROS_H +# define BFS_USE_SYS_SYSMACROS_H BFS_HAS_SYS_SYSMACROS_H +#endif +#ifndef BFS_USE_SYS_XATTR_H +# define BFS_USE_SYS_XATTR_H BFS_HAS_SYS_XATTR_H +#endif +#ifndef BFS_USE_THREADS_H +# define BFS_USE_THREADS_H BFS_HAS_THREADS_H +#endif +#ifndef BFS_USE_UTIL_H +# define BFS_USE_UTIL_H BFS_HAS_UTIL_H +#endif + +// Stub out feature detection on old/incompatible compilers + +#ifndef __has_feature +# define __has_feature(feat) false +#endif + +#ifndef __has_c_attribute +# define __has_c_attribute(attr) false +#endif + +#ifndef __has_attribute +# define __has_attribute(attr) false +#endif + +// Platform detection + +// Get the definition of BSD if available +#if BFS_USE_SYS_PARAM_H +# include +#endif + +#ifndef __GLIBC_PREREQ +# define __GLIBC_PREREQ(maj, min) false +#endif + +#ifndef __NetBSD_Prereq__ +# define __NetBSD_Prereq__(maj, min, patch) false +#endif + +// Fundamental utilities + +/** + * Get the length of an array. + */ +#define countof(array) (sizeof(array) / sizeof(0[array])) + +/** + * False sharing/destructive interference/largest cache line size. + */ +#ifdef __GCC_DESTRUCTIVE_SIZE +# define FALSE_SHARING_SIZE __GCC_DESTRUCTIVE_SIZE +#else +# define FALSE_SHARING_SIZE 64 +#endif + +/** + * True sharing/constructive interference/smallest cache line size. + */ +#ifdef __GCC_CONSTRUCTIVE_SIZE +# define TRUE_SHARING_SIZE __GCC_CONSTRUCTIVE_SIZE +#else +# define TRUE_SHARING_SIZE 64 +#endif + +/** + * Alignment specifier that avoids false sharing. + */ +#define cache_align alignas(FALSE_SHARING_SIZE) + +#if __COSMOPOLITAN__ +typedef long double max_align_t; +#endif + +// Wrappers for attributes + +/** + * Silence warnings about switch/case fall-throughs. + */ +#if __has_attribute(fallthrough) +# define fallthru __attribute__((fallthrough)) +#else +# define fallthru ((void)0) +#endif + +/** + * Silence warnings about unused declarations. + */ +#if __has_attribute(unused) +# define attr_maybe_unused __attribute__((unused)) +#else +# define attr_maybe_unused +#endif + +/** + * Warn if a value is unused. + */ +#if __has_attribute(warn_unused_result) +# define attr_nodiscard __attribute__((warn_unused_result)) +#else +# define attr_nodiscard +#endif + +/** + * Hint to avoid inlining a function. + */ +#if __has_attribute(noinline) +# define attr_noinline __attribute__((noinline)) +#else +# define attr_noinline +#endif + +/** + * Hint that a function is unlikely to be called. + */ +#if __has_attribute(cold) +# define attr_cold attr_noinline __attribute__((cold)) +#else +# define attr_cold attr_noinline +#endif + +/** + * Adds compiler warnings for bad printf()-style function calls, if supported. + */ +#if __has_attribute(format) +# define attr_printf(fmt, args) __attribute__((format(printf, fmt, args))) +#else +# define attr_printf(fmt, args) +#endif + +/** + * Annotates allocator-like functions. + */ +#if __has_attribute(malloc) +# if __GNUC__ >= 11 && !__OPTIMIZE__ // malloc(deallocator) disables inlining on GCC +# define attr_malloc(...) attr_nodiscard __attribute__((malloc(__VA_ARGS__))) +# else +# define attr_malloc(...) attr_nodiscard __attribute__((malloc)) +# endif +#else +# define attr_malloc(...) attr_nodiscard +#endif + +/** + * Specifies that a function returns allocations with a given alignment. + */ +#if __has_attribute(alloc_align) +# define attr_alloc_align(param) __attribute__((alloc_align(param))) +#else +# define attr_alloc_align(param) +#endif + +/** + * Specifies that a function returns allocations with a given size. + */ +#if __has_attribute(alloc_size) +# define attr_alloc_size(...) __attribute__((alloc_size(__VA_ARGS__))) +#else +# define attr_alloc_size(...) +#endif + +/** + * Shorthand for attr_alloc_align() and attr_alloc_size(). + */ +#define attr_aligned_alloc(align, ...) \ + attr_alloc_align(align) \ + attr_alloc_size(__VA_ARGS__) + +/** + * Check if function multiversioning via GNU indirect functions (ifunc) is supported. + */ +#ifndef BFS_USE_TARGET_CLONES +# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) +# define BFS_USE_TARGET_CLONES true +# endif +#endif + +/** + * Apply the target_clones attribute, if available. + */ +#if BFS_USE_TARGET_CLONES +# define attr_target_clones(...) __attribute__((target_clones(__VA_ARGS__))) +#else +# define attr_target_clones(...) +#endif + +/** + * Shorthand for multiple attributes at once. attr(a, b(c), d) is equivalent to + * + * attr_a + * attr_b(c) + * attr_d + */ +#define attr(...) \ + attr__(attr_##__VA_ARGS__, none, none, none, none, none, none, none, none, none, ) + +/** + * attr() helper. For exposition, pretend we support only 2 args, instead of 9. + * There are a few cases: + * + * attr() + * => attr__(attr_, none, none) + * => attr_ => + * attr_none => + * attr_too_many_none() => + * + * attr(a) + * => attr__(attr_a, none, none) + * => attr_a => __attribute__((a)) + * attr_none => + * attr_too_many_none() => + * + * attr(a, b(c)) + * => attr__(attr_a, b(c), none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_none(none) => + * + * attr(a, b(c), d) + * => attr__(attr_a, b(c), d, none, none) + * => attr_a => __attribute__((a)) + * attr_b(c) => __attribute__((b(c))) + * attr_too_many_d(none, none) => error + * + * Some attribute names are the same as standard library functions, e.g. printf. + * Standard libraries are permitted to define these functions as macros, like + * + * #define printf(...) __builtin_printf(__VA_ARGS__) + * + * The token paste in + * + * #define attr(...) attr__(attr_##__VA_ARGS__, none, none) + * + * is necessary to prevent macro expansion before evaluating attr__(). + * Otherwise, we could get + * + * attr(printf(1, 2)) + * => attr__(__builtin_printf(1, 2), none, none) + * => attr____builtin_printf(1, 2) + * => error + */ +#define attr__(a1, a2, a3, a4, a5, a6, a7, a8, a9, none, ...) \ + a1 \ + attr_##a2 \ + attr_##a3 \ + attr_##a4 \ + attr_##a5 \ + attr_##a6 \ + attr_##a7 \ + attr_##a8 \ + attr_##a9 \ + attr_too_many_##none(__VA_ARGS__) + +// Ignore `attr_none` from expanding 1-9 argument attr(a1, a2, ...) +#define attr_none +// Ignore `attr_` from expanding 0-argument attr() +#define attr_ +// Only trigger an error on more than 9 arguments +#define attr_too_many_none(...) + +#endif // BFS_PRELUDE_H diff --git a/src/printf.c b/src/printf.c index 3b8269e..4df399b 100644 --- a/src/printf.c +++ b/src/printf.c @@ -1,12 +1,12 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "printf.h" #include "alloc.h" #include "bfstd.h" #include "bftw.h" #include "color.h" -#include "config.h" #include "ctx.h" #include "diag.h" #include "dir.h" diff --git a/src/pwcache.c b/src/pwcache.c index 79437d8..af8c237 100644 --- a/src/pwcache.c +++ b/src/pwcache.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "pwcache.h" #include "alloc.h" -#include "config.h" #include "trie.h" #include #include diff --git a/src/sanity.h b/src/sanity.h index 423e6ff..e168b8f 100644 --- a/src/sanity.h +++ b/src/sanity.h @@ -8,7 +8,7 @@ #ifndef BFS_SANITY_H #define BFS_SANITY_H -#include "config.h" +#include "prelude.h" #include #if __has_feature(address_sanitizer) || defined(__SANITIZE_ADDRESS__) diff --git a/src/stat.c b/src/stat.c index 2f2743b..eca5bab 100644 --- a/src/stat.c +++ b/src/stat.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "stat.h" #include "atomic.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include diff --git a/src/stat.h b/src/stat.h index 856a2ca..1fdd263 100644 --- a/src/stat.h +++ b/src/stat.h @@ -12,7 +12,7 @@ #ifndef BFS_STAT_H #define BFS_STAT_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/thread.c b/src/thread.c index 200d8c3..3793896 100644 --- a/src/thread.c +++ b/src/thread.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "thread.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/src/thread.h b/src/thread.h index 8174fe4..db11bd8 100644 --- a/src/thread.h +++ b/src/thread.h @@ -8,7 +8,7 @@ #ifndef BFS_THREAD_H #define BFS_THREAD_H -#include "config.h" +#include "prelude.h" #include #if __STDC_VERSION__ < C23 && !defined(thread_local) diff --git a/src/trie.c b/src/trie.c index f275064..808953e 100644 --- a/src/trie.c +++ b/src/trie.c @@ -81,10 +81,10 @@ * and insert intermediate singleton "jump" nodes when necessary. */ +#include "prelude.h" #include "trie.h" #include "alloc.h" #include "bit.h" -#include "config.h" #include "diag.h" #include "list.h" #include diff --git a/src/xregex.c b/src/xregex.c index 3df27f0..c2711bc 100644 --- a/src/xregex.c +++ b/src/xregex.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xregex.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "sanity.h" #include "thread.h" diff --git a/src/xspawn.c b/src/xspawn.c index 347625d..113d7ec 100644 --- a/src/xspawn.c +++ b/src/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xspawn.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "list.h" #include #include diff --git a/src/xspawn.h b/src/xspawn.h index a20cbd0..6a8f54a 100644 --- a/src/xspawn.h +++ b/src/xspawn.h @@ -8,7 +8,7 @@ #ifndef BFS_XSPAWN_H #define BFS_XSPAWN_H -#include "config.h" +#include "prelude.h" #include #include #include diff --git a/src/xtime.c b/src/xtime.c index bcf6dd3..91ed915 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/alloc.c b/tests/alloc.c index 54b84ba..6c0defd 100644 --- a/tests/alloc.c +++ b/tests/alloc.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "alloc.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/bfstd.c b/tests/bfstd.c index 5e408ca..07b68b0 100644 --- a/tests/bfstd.c +++ b/tests/bfstd.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/bit.c b/tests/bit.c index b444e50..674d1b2 100644 --- a/tests/bit.c +++ b/tests/bit.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "bit.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/ioq.c b/tests/ioq.c index a69f2bf..ef5ee3b 100644 --- a/tests/ioq.c +++ b/tests/ioq.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "ioq.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include "dir.h" #include diff --git a/tests/main.c b/tests/main.c index 281c417..429772b 100644 --- a/tests/main.c +++ b/tests/main.c @@ -5,10 +5,10 @@ * Entry point for unit tests. */ +#include "prelude.h" #include "tests.h" #include "bfstd.h" #include "color.h" -#include "config.h" #include #include #include diff --git a/tests/tests.h b/tests/tests.h index d61ffd7..9078938 100644 --- a/tests/tests.h +++ b/tests/tests.h @@ -8,7 +8,7 @@ #ifndef BFS_TESTS_H #define BFS_TESTS_H -#include "config.h" +#include "prelude.h" #include "diag.h" /** Unit test function type. */ diff --git a/tests/trie.c b/tests/trie.c index 2a6eb48..4667322 100644 --- a/tests/trie.c +++ b/tests/trie.c @@ -1,9 +1,9 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "trie.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/xspawn.c b/tests/xspawn.c index fd8362e..7362aa5 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "alloc.h" #include "bfstd.h" -#include "config.h" #include "dstring.h" #include "xspawn.h" #include diff --git a/tests/xtime.c b/tests/xtime.c index fd7aa0f..a7c63d2 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -1,10 +1,10 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "tests.h" #include "xtime.h" #include "bfstd.h" -#include "config.h" #include "diag.h" #include #include diff --git a/tests/xtouch.c b/tests/xtouch.c index b1daec7..82d749d 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -1,8 +1,8 @@ // Copyright © Tavian Barnes // SPDX-License-Identifier: 0BSD +#include "prelude.h" #include "bfstd.h" -#include "config.h" #include "sanity.h" #include "xtime.h" #include -- cgit v1.2.3 From 48d91c62cd27c15fe0e928abf6d023d17e9c41f7 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Fri, 19 Apr 2024 14:50:48 -0400 Subject: config: Check for struct stat::st_{a,c,m,birth}{tim,timespec} --- config/header.mk | 4 ++++ config/st-acmtim.c | 12 ++++++++++++ config/st-acmtimespec.c | 12 ++++++++++++ config/st-birthtim.c | 9 +++++++++ config/st-birthtimespec.c | 9 +++++++++ src/bfstd.h | 20 +++++++++++++++----- src/stat.c | 11 +++++++---- tests/xtouch.c | 4 ++-- 8 files changed, 70 insertions(+), 11 deletions(-) create mode 100644 config/st-acmtim.c create mode 100644 config/st-acmtimespec.c create mode 100644 config/st-birthtim.c create mode 100644 config/st-birthtimespec.c (limited to 'tests') diff --git a/config/header.mk b/config/header.mk index 38f1277..5b6ff11 100644 --- a/config/header.mk +++ b/config/header.mk @@ -20,6 +20,10 @@ HEADERS := \ ${GEN}/pipe2.h \ ${GEN}/posix-spawn-addfchdir.h \ ${GEN}/posix-spawn-addfchdir-np.h \ + ${GEN}/st-acmtim.h \ + ${GEN}/st-acmtimespec.h \ + ${GEN}/st-birthtim.h \ + ${GEN}/st-birthtimespec.h \ ${GEN}/statx.h \ ${GEN}/statx-syscall.h \ ${GEN}/strerror-l.h \ diff --git a/config/st-acmtim.c b/config/st-acmtim.c new file mode 100644 index 0000000..d687ab0 --- /dev/null +++ b/config/st-acmtim.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + unsigned int a = sb.st_atim.tv_sec; + unsigned int c = sb.st_ctim.tv_sec; + unsigned int m = sb.st_mtim.tv_sec; + return a + c + m; +} diff --git a/config/st-acmtimespec.c b/config/st-acmtimespec.c new file mode 100644 index 0000000..f747bc0 --- /dev/null +++ b/config/st-acmtimespec.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + unsigned int a = sb.st_atimespec.tv_sec; + unsigned int c = sb.st_ctimespec.tv_sec; + unsigned int m = sb.st_mtimespec.tv_sec; + return a + c + m; +} diff --git a/config/st-birthtim.c b/config/st-birthtim.c new file mode 100644 index 0000000..4964571 --- /dev/null +++ b/config/st-birthtim.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_birthtim.tv_sec; +} diff --git a/config/st-birthtimespec.c b/config/st-birthtimespec.c new file mode 100644 index 0000000..91a613f --- /dev/null +++ b/config/st-birthtimespec.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_birthtimespec.tv_sec; +} diff --git a/src/bfstd.h b/src/bfstd.h index 42f5d5b..f91e380 100644 --- a/src/bfstd.h +++ b/src/bfstd.h @@ -283,11 +283,21 @@ int xminor(dev_t dev); // #include -#if __APPLE__ -# define st_atim st_atimespec -# define st_ctim st_ctimespec -# define st_mtim st_mtimespec -# define st_birthtim st_birthtimespec +/** + * Get the access/change/modification time from a struct stat. + */ +#if BFS_HAS_ST_ACMTIM +# define ST_ATIM(sb) (sb).st_atim +# define ST_CTIM(sb) (sb).st_ctim +# define ST_MTIM(sb) (sb).st_mtim +#elif BFS_HAS_ST_ACMTIMESPEC +# define ST_ATIM(sb) (sb).st_atimespec +# define ST_CTIM(sb) (sb).st_ctimespec +# define ST_MTIM(sb) (sb).st_mtimespec +#else +# define ST_ATIM(sb) ((struct timespec) { .tv_sec = (sb).st_atime }) +# define ST_CTIM(sb) ((struct timespec) { .tv_sec = (sb).st_ctime }) +# define ST_MTIM(sb) ((struct timespec) { .tv_sec = (sb).st_mtime }) #endif // #include diff --git a/src/stat.c b/src/stat.c index e525f24..a32dbaa 100644 --- a/src/stat.c +++ b/src/stat.c @@ -104,18 +104,21 @@ void bfs_stat_convert(struct bfs_stat *dest, const struct stat *src) { dest->mask |= BFS_STAT_ATTRS; #endif - dest->atime = src->st_atim; + dest->atime = ST_ATIM(*src); dest->mask |= BFS_STAT_ATIME; - dest->ctime = src->st_ctim; + dest->ctime = ST_CTIM(*src); dest->mask |= BFS_STAT_CTIME; - dest->mtime = src->st_mtim; + dest->mtime = ST_MTIM(*src); dest->mask |= BFS_STAT_MTIME; -#if __APPLE__ || __FreeBSD__ || __NetBSD__ +#if BFS_HAS_ST_BIRTHTIM dest->btime = src->st_birthtim; dest->mask |= BFS_STAT_BTIME; +#elif BFS_HAS_ST_BIRTHTIMESPEC + dest->btime = src->st_birthtimespec; + dest->mask |= BFS_STAT_BTIME; #endif } diff --git a/tests/xtouch.c b/tests/xtouch.c index 82d749d..cd41842 100644 --- a/tests/xtouch.c +++ b/tests/xtouch.c @@ -237,8 +237,8 @@ int main(int argc, char *argv[]) { fprintf(stderr, "%s: '%s': %s\n", cmd, rarg, xstrerror(errno)); return EXIT_FAILURE; } - times[0] = buf.st_atim; - times[1] = buf.st_mtim; + times[0] = ST_ATIM(buf); + times[1] = ST_MTIM(buf); } else if (darg) { if (xgetdate(darg, ×[0]) != 0) { fprintf(stderr, "%s: Parsing time '%s' failed: %s\n", cmd, darg, xstrerror(errno)); -- cgit v1.2.3 From d597be5bc1ee0e76117037b7d9a52c52fd4531ad Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Wed, 24 Apr 2024 11:07:33 -0400 Subject: eval: Plug memory leak if bfs_opendir() fails --- src/eval.c | 40 ++++++++++++++++++++++------------------ tests/common/empty.out | 8 ++++++++ tests/common/empty.sh | 1 + tests/common/empty_error.out | 3 +++ tests/common/empty_error.sh | 7 +++++++ tests/common/empty_special.out | 20 ++++++++++++++++++++ tests/common/empty_special.sh | 1 + tests/gnu/empty.out | 8 -------- tests/gnu/empty.sh | 1 - tests/gnu/empty_special.out | 20 -------------------- tests/gnu/empty_special.sh | 1 - 11 files changed, 62 insertions(+), 48 deletions(-) create mode 100644 tests/common/empty.out create mode 100644 tests/common/empty.sh create mode 100644 tests/common/empty_error.out create mode 100644 tests/common/empty_error.sh create mode 100644 tests/common/empty_special.out create mode 100644 tests/common/empty_special.sh delete mode 100644 tests/gnu/empty.out delete mode 100644 tests/gnu/empty.sh delete mode 100644 tests/gnu/empty_special.out delete mode 100644 tests/gnu/empty_special.sh (limited to 'tests') diff --git a/src/eval.c b/src/eval.c index b103912..49028b7 100644 --- a/src/eval.c +++ b/src/eval.c @@ -445,38 +445,42 @@ bool eval_depth(const struct bfs_expr *expr, struct bfs_eval *state) { * -empty test. */ bool eval_empty(const struct bfs_expr *expr, struct bfs_eval *state) { - bool ret = false; const struct BFTW *ftwbuf = state->ftwbuf; + const struct bfs_stat *statbuf; + struct bfs_dir *dir; + + switch (ftwbuf->type) { + case BFS_REG: + statbuf = eval_stat(state); + return statbuf && statbuf->size == 0; - if (ftwbuf->type == BFS_DIR) { - struct bfs_dir *dir = bfs_allocdir(); + case BFS_DIR: + dir = bfs_allocdir(); if (!dir) { - eval_report_error(state); - return ret; + goto error; } if (bfs_opendir(dir, ftwbuf->at_fd, ftwbuf->at_path, 0) != 0) { - eval_report_error(state); - return ret; + goto error; } int did_read = bfs_readdir(dir, NULL); + bfs_closedir(dir); + if (did_read < 0) { - eval_report_error(state); - } else { - ret = !did_read; + goto error; } - bfs_closedir(dir); free(dir); - } else if (ftwbuf->type == BFS_REG) { - const struct bfs_stat *statbuf = eval_stat(state); - if (statbuf) { - ret = statbuf->size == 0; - } - } + return did_read == 0; + error: + eval_report_error(state); + free(dir); + return false; - return ret; + default: + return false; + } } /** diff --git a/tests/common/empty.out b/tests/common/empty.out new file mode 100644 index 0000000..a0f4b76 --- /dev/null +++ b/tests/common/empty.out @@ -0,0 +1,8 @@ +basic/a +basic/b +basic/c/d +basic/e/f +basic/g/h +basic/i +basic/j/foo +basic/k/foo/bar diff --git a/tests/common/empty.sh b/tests/common/empty.sh new file mode 100644 index 0000000..95ee988 --- /dev/null +++ b/tests/common/empty.sh @@ -0,0 +1 @@ +bfs_diff basic -empty diff --git a/tests/common/empty_error.out b/tests/common/empty_error.out new file mode 100644 index 0000000..da45e23 --- /dev/null +++ b/tests/common/empty_error.out @@ -0,0 +1,3 @@ +./bar +./baz +./qux diff --git a/tests/common/empty_error.sh b/tests/common/empty_error.sh new file mode 100644 index 0000000..7c8049c --- /dev/null +++ b/tests/common/empty_error.sh @@ -0,0 +1,7 @@ +cd "$TEST" + +"$XTOUCH" -p foo/ bar/ baz qux +chmod -r foo baz +defer chmod +r foo baz + +! bfs_diff . -empty diff --git a/tests/common/empty_special.out b/tests/common/empty_special.out new file mode 100644 index 0000000..fa35478 --- /dev/null +++ b/tests/common/empty_special.out @@ -0,0 +1,20 @@ +rainbow// +rainbow/exec.sh +rainbow/file.dat +rainbow/file.txt +rainbow/lower.gz +rainbow/lower.tar +rainbow/lower.tar.gz +rainbow/lu.tar.GZ +rainbow/mh1 +rainbow/mh2 +rainbow/ow +rainbow/sgid +rainbow/sticky +rainbow/sticky_ow +rainbow/sugid +rainbow/suid +rainbow/ul.TAR.gz +rainbow/upper.GZ +rainbow/upper.TAR +rainbow/upper.TAR.GZ diff --git a/tests/common/empty_special.sh b/tests/common/empty_special.sh new file mode 100644 index 0000000..31e9d2e --- /dev/null +++ b/tests/common/empty_special.sh @@ -0,0 +1 @@ +bfs_diff rainbow -empty diff --git a/tests/gnu/empty.out b/tests/gnu/empty.out deleted file mode 100644 index a0f4b76..0000000 --- a/tests/gnu/empty.out +++ /dev/null @@ -1,8 +0,0 @@ -basic/a -basic/b -basic/c/d -basic/e/f -basic/g/h -basic/i -basic/j/foo -basic/k/foo/bar diff --git a/tests/gnu/empty.sh b/tests/gnu/empty.sh deleted file mode 100644 index 95ee988..0000000 --- a/tests/gnu/empty.sh +++ /dev/null @@ -1 +0,0 @@ -bfs_diff basic -empty diff --git a/tests/gnu/empty_special.out b/tests/gnu/empty_special.out deleted file mode 100644 index fa35478..0000000 --- a/tests/gnu/empty_special.out +++ /dev/null @@ -1,20 +0,0 @@ -rainbow// -rainbow/exec.sh -rainbow/file.dat -rainbow/file.txt -rainbow/lower.gz -rainbow/lower.tar -rainbow/lower.tar.gz -rainbow/lu.tar.GZ -rainbow/mh1 -rainbow/mh2 -rainbow/ow -rainbow/sgid -rainbow/sticky -rainbow/sticky_ow -rainbow/sugid -rainbow/suid -rainbow/ul.TAR.gz -rainbow/upper.GZ -rainbow/upper.TAR -rainbow/upper.TAR.GZ diff --git a/tests/gnu/empty_special.sh b/tests/gnu/empty_special.sh deleted file mode 100644 index 31e9d2e..0000000 --- a/tests/gnu/empty_special.sh +++ /dev/null @@ -1 +0,0 @@ -bfs_diff rainbow -empty -- cgit v1.2.3 From 37caa3d71fd8bb4d0d9204e4a2f5cac234fa25fd Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 29 Apr 2024 15:30:39 -0400 Subject: build: Replace `make config` with a `./configure` script This lets us do more traditional out-of-tree builds like $ ../path/to/bfs/configure $ make The .mk files are moved from ./config to ./build, mostly so that ./configure will auto-complete easily. --- .github/workflows/ci.yml | 8 +- .github/workflows/codecov.yml | 2 +- .github/workflows/codeql.yml | 2 +- Makefile | 143 +++++++++++++++--------------- README.md | 4 +- bench/bench.sh | 6 +- build/cc.sh | 16 ++++ build/config.mk | 62 +++++++++++++ build/define-if.sh | 20 +++++ build/deps.mk | 18 ++++ build/empty.c | 6 ++ build/exports.mk | 20 +++++ build/flags.mk | 136 ++++++++++++++++++++++++++++ build/has/acl-get-entry.c | 8 ++ build/has/acl-get-file.c | 8 ++ build/has/acl-get-tag-type.c | 10 +++ build/has/acl-is-trivial-np.c | 12 +++ build/has/acl-trivial.c | 8 ++ build/has/aligned-alloc.c | 8 ++ build/has/confstr.c | 9 ++ build/has/extattr-get-file.c | 10 +++ build/has/extattr-get-link.c | 10 +++ build/has/extattr-list-file.c | 10 +++ build/has/extattr-list-link.c | 10 +++ build/has/fdclosedir.c | 8 ++ build/has/getdents.c | 10 +++ build/has/getdents64-syscall.c | 12 +++ build/has/getdents64.c | 10 +++ build/has/getprogname-gnu.c | 9 ++ build/has/getprogname.c | 9 ++ build/has/max-align-t.c | 8 ++ build/has/pipe2.c | 10 +++ build/has/posix-spawn-addfchdir-np.c | 11 +++ build/has/posix-spawn-addfchdir.c | 11 +++ build/has/st-acmtim.c | 12 +++ build/has/st-acmtimespec.c | 12 +++ build/has/st-birthtim.c | 9 ++ build/has/st-birthtimespec.c | 9 ++ build/has/st-flags.c | 9 ++ build/has/statx-syscall.c | 13 +++ build/has/statx.c | 11 +++ build/has/strerror-l.c | 11 +++ build/has/strerror-r-gnu.c | 11 +++ build/has/strerror-r-posix.c | 11 +++ build/has/tm-gmtoff.c | 9 ++ build/has/uselocale.c | 9 ++ build/header.mk | 69 +++++++++++++++ build/pkgconf.sh | 100 +++++++++++++++++++++ build/pkgs.mk | 36 ++++++++ build/prelude.mk | 133 ++++++++++++++++++++++++++++ build/use/libacl.c | 9 ++ build/use/libcap.c | 9 ++ build/use/libselinux.c | 9 ++ build/use/liburing.c | 9 ++ build/use/oniguruma.c | 9 ++ config/cc.sh | 16 ---- config/config.mk | 63 ------------- config/define-if.sh | 20 ----- config/deps.mk | 18 ---- config/empty.c | 6 -- config/exports.mk | 20 ----- config/flags.mk | 136 ---------------------------- config/has/acl-get-entry.c | 8 -- config/has/acl-get-file.c | 8 -- config/has/acl-get-tag-type.c | 10 --- config/has/acl-is-trivial-np.c | 12 --- config/has/acl-trivial.c | 8 -- config/has/aligned-alloc.c | 8 -- config/has/confstr.c | 9 -- config/has/extattr-get-file.c | 10 --- config/has/extattr-get-link.c | 10 --- config/has/extattr-list-file.c | 10 --- config/has/extattr-list-link.c | 10 --- config/has/fdclosedir.c | 8 -- config/has/getdents.c | 10 --- config/has/getdents64-syscall.c | 12 --- config/has/getdents64.c | 10 --- config/has/getprogname-gnu.c | 9 -- config/has/getprogname.c | 9 -- config/has/max-align-t.c | 8 -- config/has/pipe2.c | 10 --- config/has/posix-spawn-addfchdir-np.c | 11 --- config/has/posix-spawn-addfchdir.c | 11 --- config/has/st-acmtim.c | 12 --- config/has/st-acmtimespec.c | 12 --- config/has/st-birthtim.c | 9 -- config/has/st-birthtimespec.c | 9 -- config/has/st-flags.c | 9 -- config/has/statx-syscall.c | 13 --- config/has/statx.c | 11 --- config/has/strerror-l.c | 11 --- config/has/strerror-r-gnu.c | 11 --- config/has/strerror-r-posix.c | 11 --- config/has/tm-gmtoff.c | 9 -- config/has/uselocale.c | 9 -- config/header.mk | 69 --------------- config/pkgconf.sh | 100 --------------------- config/pkgs.mk | 36 -------- config/prelude.mk | 162 ---------------------------------- config/use/libacl.c | 9 -- config/use/libcap.c | 9 -- config/use/libselinux.c | 9 -- config/use/liburing.c | 9 -- config/use/oniguruma.c | 9 -- configure | 98 ++++++++++++++++++++ docs/BUILDING.md | 47 +++++----- tests/util.sh | 9 +- tests/xspawn.c | 15 +--- 108 files changed, 1199 insertions(+), 1141 deletions(-) create mode 100755 build/cc.sh create mode 100644 build/config.mk create mode 100755 build/define-if.sh create mode 100644 build/deps.mk create mode 100644 build/empty.c create mode 100644 build/exports.mk create mode 100644 build/flags.mk create mode 100644 build/has/acl-get-entry.c create mode 100644 build/has/acl-get-file.c create mode 100644 build/has/acl-get-tag-type.c create mode 100644 build/has/acl-is-trivial-np.c create mode 100644 build/has/acl-trivial.c create mode 100644 build/has/aligned-alloc.c create mode 100644 build/has/confstr.c create mode 100644 build/has/extattr-get-file.c create mode 100644 build/has/extattr-get-link.c create mode 100644 build/has/extattr-list-file.c create mode 100644 build/has/extattr-list-link.c create mode 100644 build/has/fdclosedir.c create mode 100644 build/has/getdents.c create mode 100644 build/has/getdents64-syscall.c create mode 100644 build/has/getdents64.c create mode 100644 build/has/getprogname-gnu.c create mode 100644 build/has/getprogname.c create mode 100644 build/has/max-align-t.c create mode 100644 build/has/pipe2.c create mode 100644 build/has/posix-spawn-addfchdir-np.c create mode 100644 build/has/posix-spawn-addfchdir.c create mode 100644 build/has/st-acmtim.c create mode 100644 build/has/st-acmtimespec.c create mode 100644 build/has/st-birthtim.c create mode 100644 build/has/st-birthtimespec.c create mode 100644 build/has/st-flags.c create mode 100644 build/has/statx-syscall.c create mode 100644 build/has/statx.c create mode 100644 build/has/strerror-l.c create mode 100644 build/has/strerror-r-gnu.c create mode 100644 build/has/strerror-r-posix.c create mode 100644 build/has/tm-gmtoff.c create mode 100644 build/has/uselocale.c create mode 100644 build/header.mk create mode 100755 build/pkgconf.sh create mode 100644 build/pkgs.mk create mode 100644 build/prelude.mk create mode 100644 build/use/libacl.c create mode 100644 build/use/libcap.c create mode 100644 build/use/libselinux.c create mode 100644 build/use/liburing.c create mode 100644 build/use/oniguruma.c delete mode 100755 config/cc.sh delete mode 100644 config/config.mk delete mode 100755 config/define-if.sh delete mode 100644 config/deps.mk delete mode 100644 config/empty.c delete mode 100644 config/exports.mk delete mode 100644 config/flags.mk delete mode 100644 config/has/acl-get-entry.c delete mode 100644 config/has/acl-get-file.c delete mode 100644 config/has/acl-get-tag-type.c delete mode 100644 config/has/acl-is-trivial-np.c delete mode 100644 config/has/acl-trivial.c delete mode 100644 config/has/aligned-alloc.c delete mode 100644 config/has/confstr.c delete mode 100644 config/has/extattr-get-file.c delete mode 100644 config/has/extattr-get-link.c delete mode 100644 config/has/extattr-list-file.c delete mode 100644 config/has/extattr-list-link.c delete mode 100644 config/has/fdclosedir.c delete mode 100644 config/has/getdents.c delete mode 100644 config/has/getdents64-syscall.c delete mode 100644 config/has/getdents64.c delete mode 100644 config/has/getprogname-gnu.c delete mode 100644 config/has/getprogname.c delete mode 100644 config/has/max-align-t.c delete mode 100644 config/has/pipe2.c delete mode 100644 config/has/posix-spawn-addfchdir-np.c delete mode 100644 config/has/posix-spawn-addfchdir.c delete mode 100644 config/has/st-acmtim.c delete mode 100644 config/has/st-acmtimespec.c delete mode 100644 config/has/st-birthtim.c delete mode 100644 config/has/st-birthtimespec.c delete mode 100644 config/has/st-flags.c delete mode 100644 config/has/statx-syscall.c delete mode 100644 config/has/statx.c delete mode 100644 config/has/strerror-l.c delete mode 100644 config/has/strerror-r-gnu.c delete mode 100644 config/has/strerror-r-posix.c delete mode 100644 config/has/tm-gmtoff.c delete mode 100644 config/has/uselocale.c delete mode 100644 config/header.mk delete mode 100755 config/pkgconf.sh delete mode 100644 config/pkgs.mk delete mode 100644 config/prelude.mk delete mode 100644 config/use/libacl.c delete mode 100644 config/use/libcap.c delete mode 100644 config/use/libselinux.c delete mode 100644 config/use/liburing.c delete mode 100644 config/use/oniguruma.c create mode 100755 configure (limited to 'tests') diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 3890bb0..57f59f4 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -107,7 +107,7 @@ jobs: gmake \ oniguruma jobs=$(sysctl -n hw.ncpu) - gmake -j$jobs config + MAKE=gmake ./configure gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" netbsd: @@ -133,7 +133,7 @@ jobs: pkgconf \ tcl-expect jobs=$(sysctl -n hw.ncpu) - make -j$jobs config + ./configure make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" dragonflybsd: @@ -165,7 +165,7 @@ jobs: run: | chown -R action:action . jobs=$(sysctl -n hw.ncpu) - sudo -u action make config + sudo -u action ./configure sudo -u action make -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" omnios: @@ -198,5 +198,5 @@ jobs: PATH="/usr/xpg4/bin:$PATH" chown -R action:staff . jobs=$(getconf NPROCESSORS_ONLN) - sudo -u action gmake config + sudo -u action MAKE=gmake ./configure sudo -u action gmake -j$jobs check TEST_FLAGS="--sudo --verbose=skipped" diff --git a/.github/workflows/codecov.yml b/.github/workflows/codecov.yml index 2245f40..bb68927 100644 --- a/.github/workflows/codecov.yml +++ b/.github/workflows/codecov.yml @@ -25,7 +25,7 @@ jobs: - name: Generate coverage run: | - make config GCOV=y + ./configure GCOV=y make -j$(nproc) check TEST_FLAGS="--sudo" gcov -abcfpu obj/*/*.o diff --git a/.github/workflows/codeql.yml b/.github/workflows/codeql.yml index c21fda5..a0b8fe3 100644 --- a/.github/workflows/codeql.yml +++ b/.github/workflows/codeql.yml @@ -41,7 +41,7 @@ jobs: - name: Configure run: | - make -j$(nproc) config + ./configure - name: Initialize CodeQL uses: github/codeql-action/init@v3 diff --git a/Makefile b/Makefile index 981c322..2fb35fd 100644 --- a/Makefile +++ b/Makefile @@ -1,56 +1,50 @@ # Copyright © Tavian Barnes # SPDX-License-Identifier: 0BSD -# This Makefile implements the configuration and build steps for bfs. It is -# portable to both GNU make and most BSD make implementations. To build bfs, -# run +# To build bfs, run # -# $ make config +# $ ./configure # $ make # Utilities and GNU/BSD portability -include config/prelude.mk +include build/prelude.mk # The default build target default: bfs .PHONY: default # Include the generated build config, if it exists --include ${CONFIG} +-include gen/config.mk -## Configuration phase (`make config`) - -# The configuration goal itself -config:: - @+${MAKE} -sf config/config.mk +## Configuration phase (`./configure`) # bfs used to have flag-like targets (`make release`, `make asan ubsan`, etc.). # Direct users to the new configuration system. asan lsan msan tsan ubsan gcov lint release:: - @printf 'error: `%s %s` is no longer supported. ' "${MAKE}" $@ >&2 - @printf 'Use `%s config %s=y` instead.\n' "${MAKE}" $$(echo $@ | tr 'a-z' 'A-Z') >&2 + @printf 'error: `%s %s` is no longer supported. Use `./configure %s=y` instead.\n' \ + "${MAKE}" $@ $$(echo $@ | tr 'a-z' 'A-Z') >&2 @false -# Print an error if `make` is run before `make config` -${CONFIG}:: +# Print an error if `make` is run before `./configure` +gen/config.mk:: @if ! [ -e $@ ]; then \ - printf 'error: You must run `%s config` before `%s`.\n' "${MAKE}" "${MAKE}" >&2; \ + printf 'error: You must run `./configure` before `%s`.\n' "${MAKE}" >&2; \ false; \ fi ## Build phase (`make`) # The main binary -bfs: ${BIN}/bfs +bfs: bin/bfs .PHONY: bfs # All binaries BINS := \ - ${BIN}/bfs \ - ${BIN}/tests/mksock \ - ${BIN}/tests/units \ - ${BIN}/tests/xspawnee \ - ${BIN}/tests/xtouch + bin/bfs \ + bin/tests/mksock \ + bin/tests/units \ + bin/tests/xspawnee \ + bin/tests/xtouch all: ${BINS} .PHONY: all @@ -60,52 +54,50 @@ ALL_CFLAGS = ${CPPFLAGS} ${CFLAGS} ${DEPFLAGS} ALL_LDFLAGS = ${CFLAGS} ${LDFLAGS} # The main binary -${BIN}/bfs: ${LIBBFS} ${OBJ}/src/main.o +bin/bfs: ${LIBBFS} obj/src/main.o ${BINS}: @${MKDIR} ${@D} - +${MSG} "[ LD ] ${TGT}" ${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@ + +${MSG} "[ LD ] $@" ${CC} ${ALL_LDFLAGS} ${.ALLSRC} ${LDLIBS} -o $@ ${POSTLINK} # Get the .c file for a .o file -_CSRC = ${@:${OBJ}/%.o=%.c} -CSRC = ${_CSRC:gen/%=${GEN}/%} +CSRC = ${@:obj/%.o=%.c} -# Depend on ${CONFIG} to make sure `make config` runs first, and to rebuild when -# the configuration changes -${OBJS}: ${CONFIG} +# Rebuild when the configuration changes +${OBJS}: gen/config.mk @${MKDIR} ${@D} - ${MSG} "[ CC ] ${_CSRC}" ${CC} ${ALL_CFLAGS} -c ${CSRC} -o $@ + ${MSG} "[ CC ] ${CSRC}" ${CC} ${ALL_CFLAGS} -c ${CSRC} -o $@ # Save the version number to this file, but only update version.c if it changes -${GEN}/version.c.new:: +gen/version.c.new:: @${MKDIR} ${@D} @printf 'const char bfs_version[] = "' >$@ @if [ "$$VERSION" ]; then \ printf '%s' "$$VERSION"; \ - elif test -d .git && command -v git >/dev/null 2>&1; then \ - git describe --always --dirty; \ + elif test -e src/../.git && command -v git >/dev/null 2>&1; then \ + git -C src/.. describe --always --dirty; \ else \ echo "3.1.3"; \ fi | tr -d '\n' >>$@ @printf '";\n' >>$@ -${GEN}/version.c: ${GEN}/version.c.new +gen/version.c: gen/version.c.new @test -e $@ && cmp -s $@ ${.ALLSRC} && rm ${.ALLSRC} || mv ${.ALLSRC} $@ -${OBJ}/gen/version.o: ${GEN}/version.c +obj/gen/version.o: gen/version.c ## Test phase (`make check`) # Unit test binaries UTEST_BINS := \ - ${BIN}/tests/units \ - ${BIN}/tests/xspawnee + bin/tests/units \ + bin/tests/xspawnee # Integration test binaries ITEST_BINS := \ - ${BIN}/tests/mksock \ - ${BIN}/tests/xtouch + bin/tests/mksock \ + bin/tests/xtouch # Build (but don't run) test binaries tests: ${UTEST_BINS} ${ITEST_BINS} @@ -117,45 +109,45 @@ check: unit-tests integration-tests # Run the unit tests unit-tests: ${UTEST_BINS} - ${MSG} "[TEST] tests/units" ${BIN}/tests/units + ${MSG} "[TEST] tests/units" bin/tests/units .PHONY: unit-tests -${BIN}/tests/units: \ +bin/tests/units: \ ${UNIT_OBJS} \ ${LIBBFS} -${BIN}/tests/xspawnee: \ - ${OBJ}/tests/xspawnee.o +bin/tests/xspawnee: \ + obj/tests/xspawnee.o # The different flag combinations we check INTEGRATIONS := default dfs ids eds j1 j2 j3 s INTEGRATION_TESTS := ${INTEGRATIONS:%=check-%} # Check just `bfs` -check-default: ${BIN}/bfs ${ITEST_BINS} +check-default: bin/bfs ${ITEST_BINS} +${MSG} "[TEST] bfs" \ - ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs" ${TEST_FLAGS} + ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs" ${TEST_FLAGS} # Check the different search strategies -check-dfs check-ids check-eds: ${BIN}/bfs ${ITEST_BINS} +check-dfs check-ids check-eds: bin/bfs ${ITEST_BINS} +${MSG} "[TEST] bfs -S ${@:check-%=%}" \ - ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -S ${@:check-%=%}" ${TEST_FLAGS} + ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs -S ${@:check-%=%}" ${TEST_FLAGS} # Check various flags -check-j1 check-j2 check-j3 check-s: ${BIN}/bfs ${ITEST_BINS} +check-j1 check-j2 check-j3 check-s: bin/bfs ${ITEST_BINS} +${MSG} "[TEST] bfs -${@:check-%=%}" \ - ./tests/tests.sh --make="${MAKE}" --bfs="${BIN}/bfs -${@:check-%=%}" ${TEST_FLAGS} + ./tests/tests.sh --make="${MAKE}" --bfs="bin/bfs -${@:check-%=%}" ${TEST_FLAGS} # Run the integration tests integration-tests: ${INTEGRATION_TESTS} .PHONY: integration-tests -${BIN}/tests/mksock: \ - ${OBJ}/tests/mksock.o \ +bin/tests/mksock: \ + obj/tests/mksock.o \ ${LIBBFS} -${BIN}/tests/xtouch: \ - ${OBJ}/tests/xtouch.o \ +bin/tests/xtouch: \ + obj/tests/xtouch.o \ ${LIBBFS} # `make distcheck` configurations @@ -183,8 +175,10 @@ DISTCHECK_CONFIG_m32 := EXTRA_CFLAGS="-m32" PKG_CONFIG_LIBDIR=/usr/lib32/pkgconf DISTCHECK_CONFIG_release := RELEASE=y ${DISTCHECKS}:: - +${MAKE} -rs BUILDDIR=${BUILDDIR}/$@ config ${DISTCHECK_CONFIG_${@:distcheck-%=%}} - +${MAKE} -s BUILDDIR=${BUILDDIR}/$@ check TEST_FLAGS="--sudo --verbose=skipped" + @${MKDIR} $@ + @+cd $@ \ + && ../configure ${DISTCHECK_CONFIG_${@:distcheck-%=%}} \ + && ${MAKE} -s check TEST_FLAGS="--sudo --verbose=skipped" ## Packaging (`make install`) @@ -193,44 +187,49 @@ DEST_MANDIR := ${DESTDIR}${MANDIR} install:: ${Q}${MKDIR} ${DEST_PREFIX}/bin - ${MSG} "[INSTALL] bin/bfs" \ - ${INSTALL} -m755 ${BIN}/bfs ${DEST_PREFIX}/bin/bfs + ${MSG} "[INST] bin/bfs" \ + ${INSTALL} -m755 bin/bfs ${DEST_PREFIX}/bin/bfs ${Q}${MKDIR} ${DEST_MANDIR}/man1 - ${MSG} "[INSTALL] man/man1/bfs.1" \ + ${MSG} "[INST] man/man1/bfs.1" \ ${INSTALL} -m644 docs/bfs.1 ${DEST_MANDIR}/man1/bfs.1 ${Q}${MKDIR} ${DEST_PREFIX}/share/bash-completion/completions - ${MSG} "[INSTALL] completions/bfs.bash" \ + ${MSG} "[INST] completions/bfs.bash" \ ${INSTALL} -m644 completions/bfs.bash ${DEST_PREFIX}/share/bash-completion/completions/bfs ${Q}${MKDIR} ${DEST_PREFIX}/share/zsh/site-functions - ${MSG} "[INSTALL] completions/bfs.zsh" \ + ${MSG} "[INST] completions/bfs.zsh" \ ${INSTALL} -m644 completions/bfs.zsh ${DEST_PREFIX}/share/zsh/site-functions/_bfs ${Q}${MKDIR} ${DEST_PREFIX}/share/fish/vendor_completions.d - ${MSG} "[INSTALL] completions/bfs.fish" \ + ${MSG} "[INST] completions/bfs.fish" \ ${INSTALL} -m644 completions/bfs.fish ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish uninstall:: - ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs - ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs - ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish - ${RM} ${DEST_MANDIR}/man1/bfs.1 - ${RM} ${DEST_PREFIX}/bin/bfs + ${MSG} "[ RM ] completions/bfs.bash" \ + ${RM} ${DEST_PREFIX}/share/bash-completion/completions/bfs + ${MSG} "[ RM ] completions/bfs.zsh" \ + ${RM} ${DEST_PREFIX}/share/zsh/site-functions/_bfs + ${MSG} "[ RM ] completions/bfs.fish" \ + ${RM} ${DEST_PREFIX}/share/fish/vendor_completions.d/bfs.fish + ${MSG} "[ RM ] man/man1/bfs.1" \ + ${RM} ${DEST_MANDIR}/man1/bfs.1 + ${MSG} "[ RM ] bin/bfs" \ + ${RM} ${DEST_PREFIX}/bin/bfs # Check that `make install` works and `make uninstall` removes everything check-install:: - +${MAKE} install DESTDIR=${BUILDDIR}/pkg - +${MAKE} uninstall DESTDIR=${BUILDDIR}/pkg - ${BIN}/bfs ${BUILDDIR}/pkg -not -type d -print -exit 1 - ${RM} -r ${BUILDDIR}/pkg + +${MAKE} install DESTDIR=pkg + +${MAKE} uninstall DESTDIR=pkg + bin/bfs pkg -not -type d -print -exit 1 + ${RM} -r pkg ## Cleanup (`make clean`) # Clean all build products clean:: ${MSG} "[ RM ] bin obj" \ - ${RM} -r ${BIN} ${OBJ} + ${RM} -r bin obj # Clean everything, including generated files distclean: clean ${MSG} "[ RM ] gen" \ - ${RM} -r ${GEN} ${DISTCHECKS} + ${RM} -r gen ${DISTCHECKS} .PHONY: distclean diff --git a/README.md b/README.md index b95c16b..922ebdc 100644 --- a/README.md +++ b/README.md @@ -333,7 +333,7 @@ Once you have the dependencies, you can build bfs. Download one of the [releases](https://github.com/tavianator/bfs/releases) or clone the [git repo](https://github.com/tavianator/bfs). Then run - $ make config + $ ./configure $ make This will build the `./bin/bfs` binary. @@ -343,7 +343,7 @@ Run the test suite to make sure it works correctly: If you're interested in speed, you may want to build the release version instead: - $ make config RELEASE=y + $ ./configure RELEASE=y $ make Finally, if you want to install it globally, run diff --git a/bench/bench.sh b/bench/bench.sh index 1526fe5..ba46599 100644 --- a/bench/bench.sh +++ b/bench/bench.sh @@ -221,7 +221,7 @@ setup() { fi echo "Building bfs ..." - as-user make -s -j"$nproc" RELEASE=y + as-user ./configure RELEASE=y as-user make -s -j"$nproc" all as-user mkdir -p bench/corpus @@ -254,8 +254,8 @@ setup() { echo "Building bfs $commit ..." cd "$worktree" as-user git checkout -qd "$commit" -- - if [ -e config ]; then - as-user make -s -j"$nproc" config RELEASE=1 + if [ -e configure ]; then + as-user ./configure RELEASE=1 as-user make -s -j"$nproc" else as-user make -s -j"$nproc" release diff --git a/build/cc.sh b/build/cc.sh new file mode 100755 index 0000000..45d51ca --- /dev/null +++ b/build/cc.sh @@ -0,0 +1,16 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Run the compiler and check if it succeeded + +set -eu + +TMP=$(mktemp) +trap 'rm -f "$TMP"' EXIT + +( + set -x + $XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS "$@" $XLDLIBS -o "$TMP" +) diff --git a/build/config.mk b/build/config.mk new file mode 100644 index 0000000..153838d --- /dev/null +++ b/build/config.mk @@ -0,0 +1,62 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that implements `./configure` + +include build/prelude.mk +include build/exports.mk + +# All configuration steps +config: gen/config.mk gen/config.h +.PHONY: config + +# Makefile fragments generated by `./configure` +MKS := \ + gen/vars.mk \ + gen/flags.mk \ + gen/deps.mk \ + gen/pkgs.mk + +# The main configuration file, which includes the others +gen/config.mk: ${MKS} + ${MSG} "[ GEN] $@" + @printf '# %s\n' "$@" >$@ + @printf 'include %s\n' ${.ALLSRC} >>$@ + ${VCAT} gen/config.mk +.PHONY: gen/config.mk + +# Saves the configurable variables +gen/vars.mk:: + @${MKDIR} ${@D} + ${MSG} "[ GEN] $@" + @printf '# %s\n' "$@" >$@ + @printf 'PREFIX := %s\n' "$$XPREFIX" >>$@ + @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@ + @printf 'OS := %s\n' "$${OS:-$$(uname)}" >>$@ + @printf 'CC := %s\n' "$$XCC" >>$@ + @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@ + @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@ + @printf 'PKG_CONFIG := %s\n' "$$XPKG_CONFIG" >>$@ + @printf 'RM := %s\n' "$$XRM" >>$@ + ${VCAT} $@ + +# Sets the build flags. This depends on vars.mk and uses a recursive make so +# that the default flags can depend on variables like ${OS}. +gen/flags.mk: gen/vars.mk + @+${MAKE} -sf build/flags.mk $@ +.PHONY: gen/flags.mk + +# Check for dependency generation support +gen/deps.mk: gen/flags.mk + @+${MAKE} -sf build/deps.mk $@ +.PHONY: gen/deps.mk + +# Auto-detect dependencies and their build flags +gen/pkgs.mk: gen/flags.mk + @+${MAKE} -sf build/pkgs.mk $@ +.PHONY: gen/pkgs.mk + +# Compile-time feature detection +gen/config.h: gen/config.mk + @+${MAKE} -sf build/header.mk $@ +.PHONY: gen/config.h diff --git a/build/define-if.sh b/build/define-if.sh new file mode 100755 index 0000000..295ead8 --- /dev/null +++ b/build/define-if.sh @@ -0,0 +1,20 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Output a C preprocessor definition based on whether a command succeeds + +set -eu + +SLUG="${1#build/}" +SLUG="${SLUG%.c}" +MACRO="BFS_$(printf '%s' "$SLUG" | tr '/a-z-' '_A-Z_')" +shift + +if "$@"; then + printf '#define %s true\n' "$MACRO" +else + printf '#define %s false\n' "$MACRO" + exit 1 +fi diff --git a/build/deps.mk b/build/deps.mk new file mode 100644 index 0000000..e8dd9fb --- /dev/null +++ b/build/deps.mk @@ -0,0 +1,18 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/deps.mk + +include build/prelude.mk +include gen/vars.mk +include gen/flags.mk +include build/exports.mk + +gen/deps.mk:: + ${MSG} "[ GEN] $@" + printf '# %s\n' "$@" >$@ + if build/cc.sh -MD -MP -MF /dev/null build/empty.c; then \ + printf 'DEPFLAGS := -MD -MP\n'; \ + fi >>$@ 2>$@.log + ${VCAT} $@ + printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ diff --git a/build/empty.c b/build/empty.c new file mode 100644 index 0000000..4fa9a5b --- /dev/null +++ b/build/empty.c @@ -0,0 +1,6 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +int main(void) { + return 0; +} diff --git a/build/exports.mk b/build/exports.mk new file mode 100644 index 0000000..ed19134 --- /dev/null +++ b/build/exports.mk @@ -0,0 +1,20 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile fragment that exports variables used by configuration scripts + +export XPREFIX=${PREFIX} +export XMANDIR=${MANDIR} + +export XCC=${CC} +export XINSTALL=${INSTALL} +export XMKDIR=${MKDIR} +export XPKG_CONFIG=${PKG_CONFIG} +export XRM=${RM} + +export XCPPFLAGS=${CPPFLAGS} +export XCFLAGS=${CFLAGS} +export XLDFLAGS=${LDFLAGS} +export XLDLIBS=${LDLIBS} + +export XNOLIBS=${NOLIBS} diff --git a/build/flags.mk b/build/flags.mk new file mode 100644 index 0000000..0a44e94 --- /dev/null +++ b/build/flags.mk @@ -0,0 +1,136 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/flags.mk + +include build/prelude.mk +include gen/vars.mk + +# Configurable flags +CPPFLAGS ?= +CFLAGS ?= \ + -g \ + -Wall \ + -Wformat=2 \ + -Werror=implicit \ + -Wimplicit-fallthrough \ + -Wmissing-declarations \ + -Wshadow \ + -Wsign-compare \ + -Wstrict-prototypes +LDFLAGS ?= +LDLIBS ?= + +export XCPPFLAGS=${CPPFLAGS} +export XCFLAGS=${CFLAGS} +export XLDFLAGS=${LDFLAGS} +export XLDLIBS=${LDLIBS} + +# Immutable flags +export BFS_CPPFLAGS= \ + -Isrc \ + -Igen \ + -D__EXTENSIONS__ \ + -D_ATFILE_SOURCE \ + -D_BSD_SOURCE \ + -D_DARWIN_C_SOURCE \ + -D_DEFAULT_SOURCE \ + -D_GNU_SOURCE \ + -D_POSIX_PTHREAD_SEMANTICS \ + -D_FILE_OFFSET_BITS=64 \ + -D_TIME_BITS=64 +export BFS_CFLAGS= -std=c17 -pthread + +# Platform-specific system libraries +LDLIBS,DragonFly := -lposix1e +LDLIBS,Linux := -lrt +LDLIBS,NetBSD := -lutil +LDLIBS,SunOS := -lsec -lsocket -lnsl +export BFS_LDLIBS=${LDLIBS,${OS}} + +# Build profiles +_ASAN := ${TRUTHY,${ASAN}} +_LSAN := ${TRUTHY,${LSAN}} +_MSAN := ${TRUTHY,${MSAN}} +_TSAN := ${TRUTHY,${TSAN}} +_UBSAN := ${TRUTHY,${UBSAN}} +_GCOV := ${TRUTHY,${GCOV}} +_LINT := ${TRUTHY,${LINT}} +_RELEASE := ${TRUTHY,${RELEASE}} + +# https://github.com/google/sanitizers/issues/342 +TSAN_CPPFLAGS,y := -DBFS_USE_TARGET_CLONES=0 +export TSAN_CPPFLAGS=${TSAN_CPPFLAGS,${_TSAN}} + +ASAN_CFLAGS,y := -fsanitize=address +LSAN_CFLAGS,y := -fsanitize=leak +MSAN_CFLAGS,y := -fsanitize=memory -fsanitize-memory-track-origins +TSAN_CFLAGS,y := -fsanitize=thread +UBSAN_CFLAGS.y := -fsanitize=undefined + +export ASAN_CFLAGS=${ASAN_CFLAGS,${_ASAN}} +export LSAN_CFLAGS=${LSAN_CFLAGS,${_LSAN}} +export MSAN_CFLAGS=${MSAN_CFLAGS,${_MSAN}} +export TSAN_CFLAGS=${TSAN_CFLAGS,${_TSAN}} +export UBSAN_CFLAGS=${UBSAN_CFLAGS,${_UBSAN}} + +SAN_CFLAGS,y := -fno-sanitize-recover=all +INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}} +SAN := ${NOT,${INSANE}} +export SAN_CFLAGS=${SAN_CFLAGS,${SAN}} + +# MSAN and TSAN both need all code to be instrumented +YESLIBS := ${NOT,${_MSAN}${_TSAN}} +NOLIBS ?= ${NOT,${YESLIBS}} +export XNOLIBS=${NOLIBS} + +# gcov only intercepts fork()/exec() with -std=gnu* +GCOV_CFLAGS,y := -std=gnu17 --coverage +export GCOV_CFLAGS=${GCOV_CFLAGS,${_GCOV}} + +LINT_CPPFLAGS,y := -D_FORTIFY_SOURCE=3 -DBFS_LINT +LINT_CFLAGS,y := -Werror -O2 + +export LINT_CPPFLAGS=${LINT_CPPFLAGS,${_LINT}} +export LINT_CFLAGS=${LINT_CFLAGS,${_LINT}} + +RELEASE_CPPFLAGS,y := -DNDEBUG +RELEASE_CFLAGS,y := -O3 -flto=auto + +export RELEASE_CPPFLAGS=${RELEASE_CPPFLAGS,${_RELEASE}} +export RELEASE_CFLAGS=${RELEASE_CFLAGS,${_RELEASE}} + +# Set a variable +SETVAR = printf '%s := %s\n' >>$@ + +# Append to a variable, if non-empty +APPEND = append() { test -z "$$2" || printf '%s += %s\n' "$$1" "$$2" >>$@; }; append + +gen/flags.mk:: + ${MSG} "[ GEN] $@" + printf '# %s\n' "$@" >$@ + ${SETVAR} CPPFLAGS "$$BFS_CPPFLAGS" + ${APPEND} CPPFLAGS "$$TSAN_CPPFLAGS" + ${APPEND} CPPFLAGS "$$LINT_CPPFLAGS" + ${APPEND} CPPFLAGS "$$RELEASE_CPPFLAGS" + ${APPEND} CPPFLAGS "$$XCPPFLAGS" + ${APPEND} CPPFLAGS "$$EXTRA_CPPFLAGS" + ${SETVAR} CFLAGS "$$BFS_CFLAGS" + ${APPEND} CFLAGS "$$ASAN_CFLAGS" + ${APPEND} CFLAGS "$$LSAN_CFLAGS" + ${APPEND} CFLAGS "$$MSAN_CFLAGS" + ${APPEND} CFLAGS "$$TSAN_CFLAGS" + ${APPEND} CFLAGS "$$UBSAN_CFLAGS" + ${APPEND} CFLAGS "$$SAN_CFLAGS" + ${APPEND} CFLAGS "$$GCOV_CFLAGS" + ${APPEND} CFLAGS "$$LINT_CFLAGS" + ${APPEND} CFLAGS "$$RELEASE_CFLAGS" + ${APPEND} CFLAGS "$$XCFLAGS" + ${APPEND} CFLAGS "$$EXTRA_CFLAGS" + ${SETVAR} LDFLAGS "$$XLDFLAGS" + ${SETVAR} LDLIBS "$$XLDLIBS" + ${APPEND} LDLIBS "$$EXTRA_LDLIBS" + ${APPEND} LDLIBS "$$BFS_LDLIBS" + ${SETVAR} NOLIBS "$$XNOLIBS" + test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@ + ${VCAT} $@ diff --git a/build/has/acl-get-entry.c b/build/has/acl-get-entry.c new file mode 100644 index 0000000..3cce771 --- /dev/null +++ b/build/has/acl-get-entry.c @@ -0,0 +1,8 @@ +#include +#include + +int main(void) { + acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); + acl_entry_t entry; + return acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); +} diff --git a/build/has/acl-get-file.c b/build/has/acl-get-file.c new file mode 100644 index 0000000..89fbf23 --- /dev/null +++ b/build/has/acl-get-file.c @@ -0,0 +1,8 @@ +#include +#include +#include + +int main(void) { + acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); + return acl == (acl_t)NULL; +} diff --git a/build/has/acl-get-tag-type.c b/build/has/acl-get-tag-type.c new file mode 100644 index 0000000..2901956 --- /dev/null +++ b/build/has/acl-get-tag-type.c @@ -0,0 +1,10 @@ +#include +#include +#include + +int main(void) { + acl_entry_t entry; + memset(&entry, 0, sizeof(entry)); + acl_tag_t tag; + return acl_get_tag_type(entry, &tag); +} diff --git a/build/has/acl-is-trivial-np.c b/build/has/acl-is-trivial-np.c new file mode 100644 index 0000000..9ca9fc7 --- /dev/null +++ b/build/has/acl-is-trivial-np.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + acl_t acl = acl_get_fd(3); + int trivial; + acl_is_trivial_np(acl, &trivial); + return 0; +} diff --git a/build/has/acl-trivial.c b/build/has/acl-trivial.c new file mode 100644 index 0000000..7efc838 --- /dev/null +++ b/build/has/acl-trivial.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return acl_trivial("."); +} diff --git a/build/has/aligned-alloc.c b/build/has/aligned-alloc.c new file mode 100644 index 0000000..4460038 --- /dev/null +++ b/build/has/aligned-alloc.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return !aligned_alloc(_Alignof(void *), sizeof(void *)); +} diff --git a/build/has/confstr.c b/build/has/confstr.c new file mode 100644 index 0000000..58280b4 --- /dev/null +++ b/build/has/confstr.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + confstr(_CS_PATH, NULL, 0); + return 0; +} diff --git a/build/has/extattr-get-file.c b/build/has/extattr-get-file.c new file mode 100644 index 0000000..ac9cf96 --- /dev/null +++ b/build/has/extattr-get-file.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_get_file("file", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); +} diff --git a/build/has/extattr-get-link.c b/build/has/extattr-get-link.c new file mode 100644 index 0000000..c35be5b --- /dev/null +++ b/build/has/extattr-get-link.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_get_link("link", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); +} diff --git a/build/has/extattr-list-file.c b/build/has/extattr-list-file.c new file mode 100644 index 0000000..e68a8bb --- /dev/null +++ b/build/has/extattr-list-file.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_list_file("file", EXTATTR_NAMESPACE_USER, NULL, 0); +} diff --git a/build/has/extattr-list-link.c b/build/has/extattr-list-link.c new file mode 100644 index 0000000..49f0ec2 --- /dev/null +++ b/build/has/extattr-list-link.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + return extattr_list_link("link", EXTATTR_NAMESPACE_USER, NULL, 0); +} diff --git a/build/has/fdclosedir.c b/build/has/fdclosedir.c new file mode 100644 index 0000000..f4ad1f5 --- /dev/null +++ b/build/has/fdclosedir.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return fdclosedir(opendir(".")); +} diff --git a/build/has/getdents.c b/build/has/getdents.c new file mode 100644 index 0000000..d0d4228 --- /dev/null +++ b/build/has/getdents.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct dirent de; + getdents(3, &de, 1024); + return 0; +} diff --git a/build/has/getdents64-syscall.c b/build/has/getdents64-syscall.c new file mode 100644 index 0000000..4838c14 --- /dev/null +++ b/build/has/getdents64-syscall.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + struct dirent64 de; + syscall(SYS_getdents64, 3, &de, 1024); + return 0; +} diff --git a/build/has/getdents64.c b/build/has/getdents64.c new file mode 100644 index 0000000..1abf36d --- /dev/null +++ b/build/has/getdents64.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct dirent64 de; + getdents64(3, &de, 1024); + return 0; +} diff --git a/build/has/getprogname-gnu.c b/build/has/getprogname-gnu.c new file mode 100644 index 0000000..6b97c5e --- /dev/null +++ b/build/has/getprogname-gnu.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + const char *str = program_invocation_short_name; + return str[0]; +} diff --git a/build/has/getprogname.c b/build/has/getprogname.c new file mode 100644 index 0000000..83dc8e8 --- /dev/null +++ b/build/has/getprogname.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + const char *str = getprogname(); + return str[0]; +} diff --git a/build/has/max-align-t.c b/build/has/max-align-t.c new file mode 100644 index 0000000..96165ce --- /dev/null +++ b/build/has/max-align-t.c @@ -0,0 +1,8 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + return _Alignof(max_align_t); +} diff --git a/build/has/pipe2.c b/build/has/pipe2.c new file mode 100644 index 0000000..4cb43b5 --- /dev/null +++ b/build/has/pipe2.c @@ -0,0 +1,10 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + int fds[2]; + return pipe2(fds, O_CLOEXEC); +} diff --git a/build/has/posix-spawn-addfchdir-np.c b/build/has/posix-spawn-addfchdir-np.c new file mode 100644 index 0000000..b870a53 --- /dev/null +++ b/build/has/posix-spawn-addfchdir-np.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addfchdir_np(&actions, 3); + return 0; +} diff --git a/build/has/posix-spawn-addfchdir.c b/build/has/posix-spawn-addfchdir.c new file mode 100644 index 0000000..c52ff81 --- /dev/null +++ b/build/has/posix-spawn-addfchdir.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + posix_spawn_file_actions_t actions; + posix_spawn_file_actions_init(&actions); + posix_spawn_file_actions_addfchdir(&actions, 3); + return 0; +} diff --git a/build/has/st-acmtim.c b/build/has/st-acmtim.c new file mode 100644 index 0000000..d687ab0 --- /dev/null +++ b/build/has/st-acmtim.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + unsigned int a = sb.st_atim.tv_sec; + unsigned int c = sb.st_ctim.tv_sec; + unsigned int m = sb.st_mtim.tv_sec; + return a + c + m; +} diff --git a/build/has/st-acmtimespec.c b/build/has/st-acmtimespec.c new file mode 100644 index 0000000..f747bc0 --- /dev/null +++ b/build/has/st-acmtimespec.c @@ -0,0 +1,12 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + unsigned int a = sb.st_atimespec.tv_sec; + unsigned int c = sb.st_ctimespec.tv_sec; + unsigned int m = sb.st_mtimespec.tv_sec; + return a + c + m; +} diff --git a/build/has/st-birthtim.c b/build/has/st-birthtim.c new file mode 100644 index 0000000..4964571 --- /dev/null +++ b/build/has/st-birthtim.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_birthtim.tv_sec; +} diff --git a/build/has/st-birthtimespec.c b/build/has/st-birthtimespec.c new file mode 100644 index 0000000..91a613f --- /dev/null +++ b/build/has/st-birthtimespec.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_birthtimespec.tv_sec; +} diff --git a/build/has/st-flags.c b/build/has/st-flags.c new file mode 100644 index 0000000..b1d0c32 --- /dev/null +++ b/build/has/st-flags.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct stat sb = {0}; + return sb.st_flags; +} diff --git a/build/has/statx-syscall.c b/build/has/statx-syscall.c new file mode 100644 index 0000000..87ec869 --- /dev/null +++ b/build/has/statx-syscall.c @@ -0,0 +1,13 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include +#include + +int main(void) { + struct statx sb; + syscall(SYS_statx, AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); + return 0; +} diff --git a/build/has/statx.c b/build/has/statx.c new file mode 100644 index 0000000..65f1674 --- /dev/null +++ b/build/has/statx.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + struct statx sb; + statx(AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); + return 0; +} diff --git a/build/has/strerror-l.c b/build/has/strerror-l.c new file mode 100644 index 0000000..3dcc4d7 --- /dev/null +++ b/build/has/strerror-l.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include +#include + +int main(void) { + locale_t locale = duplocale(LC_GLOBAL_LOCALE); + return !strerror_l(ENOMEM, locale); +} diff --git a/build/has/strerror-r-gnu.c b/build/has/strerror-r-gnu.c new file mode 100644 index 0000000..26ca0ee --- /dev/null +++ b/build/has/strerror-r-gnu.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + char buf[256]; + // Check that strerror_r() returns a pointer + return *strerror_r(ENOMEM, buf, sizeof(buf)); +} diff --git a/build/has/strerror-r-posix.c b/build/has/strerror-r-posix.c new file mode 100644 index 0000000..41b2d30 --- /dev/null +++ b/build/has/strerror-r-posix.c @@ -0,0 +1,11 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include +#include + +int main(void) { + char buf[256]; + // Check that strerror_r() returns an integer + return 2 * strerror_r(ENOMEM, buf, sizeof(buf)); +} diff --git a/build/has/tm-gmtoff.c b/build/has/tm-gmtoff.c new file mode 100644 index 0000000..543df48 --- /dev/null +++ b/build/has/tm-gmtoff.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct tm tm = {0}; + return tm.tm_gmtoff; +} diff --git a/build/has/uselocale.c b/build/has/uselocale.c new file mode 100644 index 0000000..a712ff8 --- /dev/null +++ b/build/has/uselocale.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + locale_t locale = uselocale((locale_t)0); + return locale == LC_GLOBAL_LOCALE; +} diff --git a/build/header.mk b/build/header.mk new file mode 100644 index 0000000..3f77ca5 --- /dev/null +++ b/build/header.mk @@ -0,0 +1,69 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/config.h + +include build/prelude.mk +include gen/config.mk +include build/exports.mk + +# All header fragments we generate +HEADERS := \ + gen/has/acl-get-entry.h \ + gen/has/acl-get-file.h \ + gen/has/acl-get-tag-type.h \ + gen/has/acl-is-trivial-np.h \ + gen/has/acl-trivial.h \ + gen/has/aligned-alloc.h \ + gen/has/confstr.h \ + gen/has/extattr-get-file.h \ + gen/has/extattr-get-link.h \ + gen/has/extattr-list-file.h \ + gen/has/extattr-list-link.h \ + gen/has/fdclosedir.h \ + gen/has/getdents.h \ + gen/has/getdents64.h \ + gen/has/getdents64-syscall.h \ + gen/has/getprogname.h \ + gen/has/getprogname-gnu.h \ + gen/has/max-align-t.h \ + gen/has/pipe2.h \ + gen/has/posix-spawn-addfchdir.h \ + gen/has/posix-spawn-addfchdir-np.h \ + gen/has/st-acmtim.h \ + gen/has/st-acmtimespec.h \ + gen/has/st-birthtim.h \ + gen/has/st-birthtimespec.h \ + gen/has/st-flags.h \ + gen/has/statx.h \ + gen/has/statx-syscall.h \ + gen/has/strerror-l.h \ + gen/has/strerror-r-gnu.h \ + gen/has/strerror-r-posix.h \ + gen/has/tm-gmtoff.h \ + gen/has/uselocale.h + +# Previously generated by pkgs.mk +PKG_HEADERS := ${ALL_PKGS:%=gen/use/%.h} + +gen/config.h: ${PKG_HEADERS} ${HEADERS} + ${MSG} "[ GEN] $@" + printf '// %s\n' "$@" >$@ + printf '#ifndef BFS_CONFIG_H\n' >>$@ + printf '#define BFS_CONFIG_H\n' >>$@ + cat ${.ALLSRC} >>$@ + printf '#endif // BFS_CONFIG_H\n' >>$@ + cat ${.ALLSRC:%=%.log} >$@.log + ${VCAT} $@ +.PHONY: gen/config.h + +# The short name of the config test +SLUG = ${@:gen/%.h=%} + +${HEADERS}:: + ${MKDIR} ${@D} + if build/define-if.sh ${SLUG} build/cc.sh build/${SLUG}.c >$@ 2>$@.log; then \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' ${SLUG}.c; \ + else \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' ${SLUG}.c; \ + fi diff --git a/build/pkgconf.sh b/build/pkgconf.sh new file mode 100755 index 0000000..96e4bf1 --- /dev/null +++ b/build/pkgconf.sh @@ -0,0 +1,100 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# pkg-config wrapper with hardcoded fallbacks + +set -eu + +MODE= +case "${1:-}" in + --*) + MODE="$1" + shift +esac + +if [ $# -lt 1 ]; then + exit +fi + +case "$XNOLIBS" in + y|1) + exit 1 +esac + +if [ -z "$MODE" ]; then + # Check whether the libraries exist at all + for LIB; do + # Check ${USE_$LIB} + USE_LIB="USE_$(printf '%s' "$LIB" | tr 'a-z-' 'A-Z_')" + eval "USE=\"\${$USE_LIB:-}\"" + case "$USE" in + y|1) + continue + ;; + n|0) + exit 1 + ;; + esac + + CFLAGS=$("$0" --cflags "$LIB") || exit 1 + LDFLAGS=$("$0" --ldflags "$LIB") || exit 1 + LDLIBS=$("$0" --ldlibs "$LIB") || exit 1 + build/cc.sh $CFLAGS $LDFLAGS build/use/$LIB.c $LDLIBS || exit 1 + done +fi + +# Defer to pkg-config if possible +if command -v "${XPKG_CONFIG:-}" >/dev/null 2>&1; then + case "$MODE" in + --cflags) + "$XPKG_CONFIG" --cflags "$@" + ;; + --ldflags) + "$XPKG_CONFIG" --libs-only-L --libs-only-other "$@" + ;; + --ldlibs) + "$XPKG_CONFIG" --libs-only-l "$@" + ;; + esac + + exit +fi + +# pkg-config unavailable, emulate it ourselves +CFLAGS="" +LDFLAGS="" +LDLIBS="" + +for LIB; do + case "$LIB" in + libacl) + LDLIB=-lacl + ;; + libcap) + LDLIB=-lcap + ;; + libselinux) + LDLIB=-lselinux + ;; + liburing) + LDLIB=-luring + ;; + oniguruma) + LDLIB=-lonig + ;; + *) + printf 'error: Unknown package %s\n' "$LIB" >&2 + exit 1 + ;; + esac + + LDLIBS="$LDLIBS$LDLIB " +done + +case "$MODE" in + --ldlibs) + printf '%s\n' "$LDLIBS" + ;; +esac diff --git a/build/pkgs.mk b/build/pkgs.mk new file mode 100644 index 0000000..51ca664 --- /dev/null +++ b/build/pkgs.mk @@ -0,0 +1,36 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Makefile that generates gen/pkgs.mk + +include build/prelude.mk +include gen/vars.mk +include gen/flags.mk +include build/exports.mk + +HEADERS := ${ALL_PKGS:%=gen/use/%.h} + +gen/pkgs.mk: ${HEADERS} + ${MSG} "[ GEN] $@" + printf '# %s\n' "$@" >$@ + gen() { \ + printf 'PKGS := %s\n' "$$*"; \ + printf 'CFLAGS += %s\n' "$$(build/pkgconf.sh --cflags "$$@")"; \ + printf 'LDFLAGS += %s\n' "$$(build/pkgconf.sh --ldflags "$$@")"; \ + printf 'LDLIBS := %s $${LDLIBS}\n' "$$(build/pkgconf.sh --ldlibs "$$@")"; \ + }; \ + gen $$(grep -l ' true$$' ${.ALLSRC} | sed 's|.*/\(.*\)\.h|\1|') >>$@ + ${VCAT} $@ + +.PHONY: gen/pkgs.mk + +# Convert gen/use/foo.h to foo +PKG = ${@:gen/use/%.h=%} + +${HEADERS}:: + ${MKDIR} ${@D} + if build/define-if.sh use/${PKG} build/pkgconf.sh ${PKG} >$@ 2>$@.log; then \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' use/${PKG}.c; \ + else \ + test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' use/${PKG}.c; \ + fi diff --git a/build/prelude.mk b/build/prelude.mk new file mode 100644 index 0000000..b235fa9 --- /dev/null +++ b/build/prelude.mk @@ -0,0 +1,133 @@ +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# Common makefile utilities. Compatible with both GNU make and most BSD makes. + +# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to +.OBJDIR: . + +# We don't use any suffix rules +.SUFFIXES: + +# GNU make has $^ for the full list of targets, while BSD make has $> and the +# long-form ${.ALLSRC}. We could write $^ $> to get them both, but that would +# break if one of them implemented support for the other. So instead, bring +# BSD's ${.ALLSRC} to GNU. +.ALLSRC ?= $^ + +# Installation paths +DESTDIR ?= +PREFIX ?= /usr +MANDIR ?= ${PREFIX}/share/man + +# Configurable executables +CC ?= cc +INSTALL ?= install +MKDIR ?= mkdir -p +PKG_CONFIG ?= pkg-config +RM ?= rm -f + +# GNU and BSD make have incompatible syntax for conditionals, but we can do a +# lot with just nested variable expansion. We use "y" as the canonical +# truthy value, and "" (the empty string) as the canonical falsey value. +# +# To normalize a boolean, use ${TRUTHY,${VAR}}, which expands like this: +# +# VAR=y ${TRUTHY,${VAR}} => ${TRUTHY,y} => y +# VAR=1 ${TRUTHY,${VAR}} => ${TRUTHY,1} => y +# VAR=n ${TRUTHY,${VAR}} => ${TRUTHY,n} => [empty] +# VAR=other ${TRUTHY,${VAR}} => ${TRUTHY,other} => [empty] +# VAR= ${TRUTHY,${VAR}} => ${TRUTHY,} => [emtpy] +# +# Inspired by https://github.com/wahern/autoguess +TRUTHY,y := y +TRUTHY,1 := y + +# Boolean operators are also implemented with nested expansion +NOT, := y + +# Normalize ${V} to either "y" or "" +IS_V := ${TRUTHY,${V}} + +# Suppress output unless V=1 +Q, := @ +Q := ${Q,${IS_V}} + +# Show full commands with `make V=1`, otherwise short summaries +MSG = @msg() { \ + MSG="$$1"; \ + shift; \ + test "${IS_V}" || printf '%s\n' "$$MSG"; \ + test "$${1:-}" || return 0; \ + test "${IS_V}" && printf '%s\n' "$$*"; \ + "$$@"; \ + }; \ + msg + +# Maximum width of a short message, to align the ✔/✘ +MSG_WIDTH := 30 + +# cat a file if V=1 +VCAT,y := @cat +VCAT, := @: +VCAT := ${VCAT,${IS_V}} + +# All external dependencies +ALL_PKGS := \ + libacl \ + libcap \ + libselinux \ + liburing \ + oniguruma + +# List all object files here, as they're needed by both `./configure` and `make` + +# All object files except the entry point +LIBBFS := \ + obj/src/alloc.o \ + obj/src/bar.o \ + obj/src/bfstd.o \ + obj/src/bftw.o \ + obj/src/color.o \ + obj/src/ctx.o \ + obj/src/diag.o \ + obj/src/dir.o \ + obj/src/dstring.o \ + obj/src/eval.o \ + obj/src/exec.o \ + obj/src/expr.o \ + obj/src/fsade.o \ + obj/src/ioq.o \ + obj/src/mtab.o \ + obj/src/opt.o \ + obj/src/parse.o \ + obj/src/printf.o \ + obj/src/pwcache.o \ + obj/src/stat.o \ + obj/src/thread.o \ + obj/src/trie.o \ + obj/src/typo.o \ + obj/src/xregex.o \ + obj/src/xspawn.o \ + obj/src/xtime.o \ + obj/gen/version.o + +# Unit test objects +UNIT_OBJS := \ + obj/tests/alloc.o \ + obj/tests/bfstd.o \ + obj/tests/bit.o \ + obj/tests/ioq.o \ + obj/tests/main.o \ + obj/tests/trie.o \ + obj/tests/xspawn.o \ + obj/tests/xtime.o + +# All object files +OBJS := \ + obj/src/main.o \ + obj/tests/mksock.o \ + obj/tests/xspawnee.o \ + obj/tests/xtouch.o \ + ${LIBBFS} \ + ${UNIT_OBJS} diff --git a/build/use/libacl.c b/build/use/libacl.c new file mode 100644 index 0000000..de1fe50 --- /dev/null +++ b/build/use/libacl.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + acl_free(0); + return 0; +} diff --git a/build/use/libcap.c b/build/use/libcap.c new file mode 100644 index 0000000..58e832c --- /dev/null +++ b/build/use/libcap.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + cap_free(0); + return 0; +} diff --git a/build/use/libselinux.c b/build/use/libselinux.c new file mode 100644 index 0000000..bca409d --- /dev/null +++ b/build/use/libselinux.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + freecon(0); + return 0; +} diff --git a/build/use/liburing.c b/build/use/liburing.c new file mode 100644 index 0000000..bea499a --- /dev/null +++ b/build/use/liburing.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + io_uring_free_probe(0); + return 0; +} diff --git a/build/use/oniguruma.c b/build/use/oniguruma.c new file mode 100644 index 0000000..cb17596 --- /dev/null +++ b/build/use/oniguruma.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + onig_free(0); + return 0; +} diff --git a/config/cc.sh b/config/cc.sh deleted file mode 100755 index 45d51ca..0000000 --- a/config/cc.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Run the compiler and check if it succeeded - -set -eu - -TMP=$(mktemp) -trap 'rm -f "$TMP"' EXIT - -( - set -x - $XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS "$@" $XLDLIBS -o "$TMP" -) diff --git a/config/config.mk b/config/config.mk deleted file mode 100644 index 280c6ac..0000000 --- a/config/config.mk +++ /dev/null @@ -1,63 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile fragment that implements `make config` - -include config/prelude.mk -include config/exports.mk - -# All configuration steps -config: ${CONFIG} ${GEN}/config.h -.PHONY: config - -# Makefile fragments generated by `make config` -MKS := \ - ${GEN}/vars.mk \ - ${GEN}/flags.mk \ - ${GEN}/deps.mk \ - ${GEN}/pkgs.mk - -# The main configuration file, which includes the others -${CONFIG}: ${MKS} - ${MSG} "[ GEN] ${TGT}" - @printf '# %s\n' "${TGT}" >$@ - @printf 'include $${GEN}/%s\n' ${MKS:${GEN}/%=%} >>$@ - ${VCAT} ${CONFIG} -.PHONY: ${CONFIG} - -# Saves the configurable variables -${GEN}/vars.mk:: - @${MKDIR} ${@D} - ${MSG} "[ GEN] ${TGT}" - @printf '# %s\n' "${TGT}" >$@ - @printf 'PREFIX := %s\n' "$$XPREFIX" >>$@ - @printf 'MANDIR := %s\n' "$$XMANDIR" >>$@ - @printf 'OS := %s\n' "$${OS:-$$(uname)}" >>$@ - @printf 'CC := %s\n' "$$XCC" >>$@ - @printf 'INSTALL := %s\n' "$$XINSTALL" >>$@ - @printf 'MKDIR := %s\n' "$$XMKDIR" >>$@ - @printf 'PKG_CONFIG := %s\n' "$$XPKG_CONFIG" >>$@ - @printf 'RM := %s\n' "$$XRM" >>$@ - @printf 'PKGS :=\n' >>$@ - ${VCAT} $@ - -# Sets the build flags. This depends on vars.mk and uses a recursive make so -# that the default flags can depend on variables like ${OS}. -${GEN}/flags.mk: ${GEN}/vars.mk - @+${MAKE} -sf config/flags.mk $@ -.PHONY: ${GEN}/flags.mk - -# Check for dependency generation support -${GEN}/deps.mk: ${GEN}/flags.mk - @+${MAKE} -sf config/deps.mk $@ -.PHONY: ${GEN}/deps.mk - -# Auto-detect dependencies and their build flags -${GEN}/pkgs.mk: ${GEN}/flags.mk - @+${MAKE} -sf config/pkgs.mk $@ -.PHONY: ${GEN}/pkgs.mk - -# Compile-time feature detection -${GEN}/config.h: ${CONFIG} - @+${MAKE} -sf config/header.mk $@ -.PHONY: ${GEN}/config.h diff --git a/config/define-if.sh b/config/define-if.sh deleted file mode 100755 index 059c1ac..0000000 --- a/config/define-if.sh +++ /dev/null @@ -1,20 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Output a C preprocessor definition based on whether a command succeeds - -set -eu - -SLUG="${1#config/}" -SLUG="${SLUG%.c}" -MACRO="BFS_$(printf '%s' "$SLUG" | tr '/a-z-' '_A-Z_')" -shift - -if "$@"; then - printf '#define %s true\n' "$MACRO" -else - printf '#define %s false\n' "$MACRO" - exit 1 -fi diff --git a/config/deps.mk b/config/deps.mk deleted file mode 100644 index ac394a5..0000000 --- a/config/deps.mk +++ /dev/null @@ -1,18 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/deps.mk - -include config/prelude.mk -include ${GEN}/vars.mk -include ${GEN}/flags.mk -include config/exports.mk - -${GEN}/deps.mk:: - ${MSG} "[ GEN] ${TGT}" - printf '# %s\n' "${TGT}" >$@ - if config/cc.sh -MD -MP -MF /dev/null config/empty.c; then \ - printf 'DEPFLAGS = -MD -MP\n'; \ - fi >>$@ 2>$@.log - ${VCAT} $@ - printf -- '-include %s\n' ${OBJS:.o=.d} >>$@ diff --git a/config/empty.c b/config/empty.c deleted file mode 100644 index 4fa9a5b..0000000 --- a/config/empty.c +++ /dev/null @@ -1,6 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -int main(void) { - return 0; -} diff --git a/config/exports.mk b/config/exports.mk deleted file mode 100644 index ed19134..0000000 --- a/config/exports.mk +++ /dev/null @@ -1,20 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile fragment that exports variables used by configuration scripts - -export XPREFIX=${PREFIX} -export XMANDIR=${MANDIR} - -export XCC=${CC} -export XINSTALL=${INSTALL} -export XMKDIR=${MKDIR} -export XPKG_CONFIG=${PKG_CONFIG} -export XRM=${RM} - -export XCPPFLAGS=${CPPFLAGS} -export XCFLAGS=${CFLAGS} -export XLDFLAGS=${LDFLAGS} -export XLDLIBS=${LDLIBS} - -export XNOLIBS=${NOLIBS} diff --git a/config/flags.mk b/config/flags.mk deleted file mode 100644 index 837c30f..0000000 --- a/config/flags.mk +++ /dev/null @@ -1,136 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/flags.mk - -include config/prelude.mk -include ${GEN}/vars.mk - -# Configurable flags -CPPFLAGS ?= -CFLAGS ?= \ - -g \ - -Wall \ - -Wformat=2 \ - -Werror=implicit \ - -Wimplicit-fallthrough \ - -Wmissing-declarations \ - -Wshadow \ - -Wsign-compare \ - -Wstrict-prototypes -LDFLAGS ?= -LDLIBS ?= - -export XCPPFLAGS=${CPPFLAGS} -export XCFLAGS=${CFLAGS} -export XLDFLAGS=${LDFLAGS} -export XLDLIBS=${LDLIBS} - -# Immutable flags -export BFS_CPPFLAGS= \ - -Isrc \ - -I${GEN} \ - -D__EXTENSIONS__ \ - -D_ATFILE_SOURCE \ - -D_BSD_SOURCE \ - -D_DARWIN_C_SOURCE \ - -D_DEFAULT_SOURCE \ - -D_GNU_SOURCE \ - -D_POSIX_PTHREAD_SEMANTICS \ - -D_FILE_OFFSET_BITS=64 \ - -D_TIME_BITS=64 -export BFS_CFLAGS= -std=c17 -pthread - -# Platform-specific system libraries -LDLIBS,DragonFly := -lposix1e -LDLIBS,Linux := -lrt -LDLIBS,NetBSD := -lutil -LDLIBS,SunOS := -lsec -lsocket -lnsl -export BFS_LDLIBS=${LDLIBS,${OS}} - -# Build profiles -_ASAN := ${TRUTHY,${ASAN}} -_LSAN := ${TRUTHY,${LSAN}} -_MSAN := ${TRUTHY,${MSAN}} -_TSAN := ${TRUTHY,${TSAN}} -_UBSAN := ${TRUTHY,${UBSAN}} -_GCOV := ${TRUTHY,${GCOV}} -_LINT := ${TRUTHY,${LINT}} -_RELEASE := ${TRUTHY,${RELEASE}} - -# https://github.com/google/sanitizers/issues/342 -TSAN_CPPFLAGS,y := -DBFS_USE_TARGET_CLONES=0 -export TSAN_CPPFLAGS=${TSAN_CPPFLAGS,${_TSAN}} - -ASAN_CFLAGS,y := -fsanitize=address -LSAN_CFLAGS,y := -fsanitize=leak -MSAN_CFLAGS,y := -fsanitize=memory -fsanitize-memory-track-origins -TSAN_CFLAGS,y := -fsanitize=thread -UBSAN_CFLAGS.y := -fsanitize=undefined - -export ASAN_CFLAGS=${ASAN_CFLAGS,${_ASAN}} -export LSAN_CFLAGS=${LSAN_CFLAGS,${_LSAN}} -export MSAN_CFLAGS=${MSAN_CFLAGS,${_MSAN}} -export TSAN_CFLAGS=${TSAN_CFLAGS,${_TSAN}} -export UBSAN_CFLAGS=${UBSAN_CFLAGS,${_UBSAN}} - -SAN_CFLAGS,y := -fno-sanitize-recover=all -INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}} -SAN := ${NOT,${INSANE}} -export SAN_CFLAGS=${SAN_CFLAGS,${SAN}} - -# MSAN and TSAN both need all code to be instrumented -YESLIBS := ${NOT,${_MSAN}${_TSAN}} -NOLIBS ?= ${NOT,${YESLIBS}} -export XNOLIBS=${NOLIBS} - -# gcov only intercepts fork()/exec() with -std=gnu* -GCOV_CFLAGS,y := -std=gnu17 --coverage -export GCOV_CFLAGS=${GCOV_CFLAGS,${_GCOV}} - -LINT_CPPFLAGS,y := -D_FORTIFY_SOURCE=3 -DBFS_LINT -LINT_CFLAGS,y := -Werror -O2 - -export LINT_CPPFLAGS=${LINT_CPPFLAGS,${_LINT}} -export LINT_CFLAGS=${LINT_CFLAGS,${_LINT}} - -RELEASE_CPPFLAGS,y := -DNDEBUG -RELEASE_CFLAGS,y := -O3 -flto=auto - -export RELEASE_CPPFLAGS=${RELEASE_CPPFLAGS,${_RELEASE}} -export RELEASE_CFLAGS=${RELEASE_CFLAGS,${_RELEASE}} - -# Set a variable -SETVAR = printf '%s := %s\n' >>$@ - -# Append to a variable, if non-empty -APPEND = append() { test -z "$$2" || printf '%s += %s\n' "$$1" "$$2" >>$@; }; append - -${GEN}/flags.mk:: - ${MSG} "[ GEN] ${TGT}" - printf '# %s\n' "${TGT}" >$@ - ${SETVAR} CPPFLAGS "$$BFS_CPPFLAGS" - ${APPEND} CPPFLAGS "$$TSAN_CPPFLAGS" - ${APPEND} CPPFLAGS "$$LINT_CPPFLAGS" - ${APPEND} CPPFLAGS "$$RELEASE_CPPFLAGS" - ${APPEND} CPPFLAGS "$$XCPPFLAGS" - ${APPEND} CPPFLAGS "$$EXTRA_CPPFLAGS" - ${SETVAR} CFLAGS "$$BFS_CFLAGS" - ${APPEND} CFLAGS "$$ASAN_CFLAGS" - ${APPEND} CFLAGS "$$LSAN_CFLAGS" - ${APPEND} CFLAGS "$$MSAN_CFLAGS" - ${APPEND} CFLAGS "$$TSAN_CFLAGS" - ${APPEND} CFLAGS "$$UBSAN_CFLAGS" - ${APPEND} CFLAGS "$$SAN_CFLAGS" - ${APPEND} CFLAGS "$$GCOV_CFLAGS" - ${APPEND} CFLAGS "$$LINT_CFLAGS" - ${APPEND} CFLAGS "$$RELEASE_CFLAGS" - ${APPEND} CFLAGS "$$XCFLAGS" - ${APPEND} CFLAGS "$$EXTRA_CFLAGS" - ${SETVAR} LDFLAGS "$$XLDFLAGS" - ${SETVAR} LDLIBS "$$XLDLIBS" - ${APPEND} LDLIBS "$$EXTRA_LDLIBS" - ${APPEND} LDLIBS "$$BFS_LDLIBS" - ${SETVAR} NOLIBS "$$XNOLIBS" - test "${OS}-${SAN}" != FreeBSD-y || printf 'POSTLINK = elfctl -e +noaslr $$@\n' >>$@ - ${VCAT} $@ diff --git a/config/has/acl-get-entry.c b/config/has/acl-get-entry.c deleted file mode 100644 index 3cce771..0000000 --- a/config/has/acl-get-entry.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include - -int main(void) { - acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); - acl_entry_t entry; - return acl_get_entry(acl, ACL_FIRST_ENTRY, &entry); -} diff --git a/config/has/acl-get-file.c b/config/has/acl-get-file.c deleted file mode 100644 index 89fbf23..0000000 --- a/config/has/acl-get-file.c +++ /dev/null @@ -1,8 +0,0 @@ -#include -#include -#include - -int main(void) { - acl_t acl = acl_get_file(".", ACL_TYPE_DEFAULT); - return acl == (acl_t)NULL; -} diff --git a/config/has/acl-get-tag-type.c b/config/has/acl-get-tag-type.c deleted file mode 100644 index 2901956..0000000 --- a/config/has/acl-get-tag-type.c +++ /dev/null @@ -1,10 +0,0 @@ -#include -#include -#include - -int main(void) { - acl_entry_t entry; - memset(&entry, 0, sizeof(entry)); - acl_tag_t tag; - return acl_get_tag_type(entry, &tag); -} diff --git a/config/has/acl-is-trivial-np.c b/config/has/acl-is-trivial-np.c deleted file mode 100644 index 9ca9fc7..0000000 --- a/config/has/acl-is-trivial-np.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - acl_t acl = acl_get_fd(3); - int trivial; - acl_is_trivial_np(acl, &trivial); - return 0; -} diff --git a/config/has/acl-trivial.c b/config/has/acl-trivial.c deleted file mode 100644 index 7efc838..0000000 --- a/config/has/acl-trivial.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return acl_trivial("."); -} diff --git a/config/has/aligned-alloc.c b/config/has/aligned-alloc.c deleted file mode 100644 index 4460038..0000000 --- a/config/has/aligned-alloc.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return !aligned_alloc(_Alignof(void *), sizeof(void *)); -} diff --git a/config/has/confstr.c b/config/has/confstr.c deleted file mode 100644 index 58280b4..0000000 --- a/config/has/confstr.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - confstr(_CS_PATH, NULL, 0); - return 0; -} diff --git a/config/has/extattr-get-file.c b/config/has/extattr-get-file.c deleted file mode 100644 index ac9cf96..0000000 --- a/config/has/extattr-get-file.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_get_file("file", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); -} diff --git a/config/has/extattr-get-link.c b/config/has/extattr-get-link.c deleted file mode 100644 index c35be5b..0000000 --- a/config/has/extattr-get-link.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_get_link("link", EXTATTR_NAMESPACE_USER, "xattr", NULL, 0); -} diff --git a/config/has/extattr-list-file.c b/config/has/extattr-list-file.c deleted file mode 100644 index e68a8bb..0000000 --- a/config/has/extattr-list-file.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_list_file("file", EXTATTR_NAMESPACE_USER, NULL, 0); -} diff --git a/config/has/extattr-list-link.c b/config/has/extattr-list-link.c deleted file mode 100644 index 49f0ec2..0000000 --- a/config/has/extattr-list-link.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - return extattr_list_link("link", EXTATTR_NAMESPACE_USER, NULL, 0); -} diff --git a/config/has/fdclosedir.c b/config/has/fdclosedir.c deleted file mode 100644 index f4ad1f5..0000000 --- a/config/has/fdclosedir.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return fdclosedir(opendir(".")); -} diff --git a/config/has/getdents.c b/config/has/getdents.c deleted file mode 100644 index d0d4228..0000000 --- a/config/has/getdents.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct dirent de; - getdents(3, &de, 1024); - return 0; -} diff --git a/config/has/getdents64-syscall.c b/config/has/getdents64-syscall.c deleted file mode 100644 index 4838c14..0000000 --- a/config/has/getdents64-syscall.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - struct dirent64 de; - syscall(SYS_getdents64, 3, &de, 1024); - return 0; -} diff --git a/config/has/getdents64.c b/config/has/getdents64.c deleted file mode 100644 index 1abf36d..0000000 --- a/config/has/getdents64.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct dirent64 de; - getdents64(3, &de, 1024); - return 0; -} diff --git a/config/has/getprogname-gnu.c b/config/has/getprogname-gnu.c deleted file mode 100644 index 6b97c5e..0000000 --- a/config/has/getprogname-gnu.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - const char *str = program_invocation_short_name; - return str[0]; -} diff --git a/config/has/getprogname.c b/config/has/getprogname.c deleted file mode 100644 index 83dc8e8..0000000 --- a/config/has/getprogname.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - const char *str = getprogname(); - return str[0]; -} diff --git a/config/has/max-align-t.c b/config/has/max-align-t.c deleted file mode 100644 index 96165ce..0000000 --- a/config/has/max-align-t.c +++ /dev/null @@ -1,8 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - return _Alignof(max_align_t); -} diff --git a/config/has/pipe2.c b/config/has/pipe2.c deleted file mode 100644 index 4cb43b5..0000000 --- a/config/has/pipe2.c +++ /dev/null @@ -1,10 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - int fds[2]; - return pipe2(fds, O_CLOEXEC); -} diff --git a/config/has/posix-spawn-addfchdir-np.c b/config/has/posix-spawn-addfchdir-np.c deleted file mode 100644 index b870a53..0000000 --- a/config/has/posix-spawn-addfchdir-np.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - posix_spawn_file_actions_t actions; - posix_spawn_file_actions_init(&actions); - posix_spawn_file_actions_addfchdir_np(&actions, 3); - return 0; -} diff --git a/config/has/posix-spawn-addfchdir.c b/config/has/posix-spawn-addfchdir.c deleted file mode 100644 index c52ff81..0000000 --- a/config/has/posix-spawn-addfchdir.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - posix_spawn_file_actions_t actions; - posix_spawn_file_actions_init(&actions); - posix_spawn_file_actions_addfchdir(&actions, 3); - return 0; -} diff --git a/config/has/st-acmtim.c b/config/has/st-acmtim.c deleted file mode 100644 index d687ab0..0000000 --- a/config/has/st-acmtim.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - unsigned int a = sb.st_atim.tv_sec; - unsigned int c = sb.st_ctim.tv_sec; - unsigned int m = sb.st_mtim.tv_sec; - return a + c + m; -} diff --git a/config/has/st-acmtimespec.c b/config/has/st-acmtimespec.c deleted file mode 100644 index f747bc0..0000000 --- a/config/has/st-acmtimespec.c +++ /dev/null @@ -1,12 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - unsigned int a = sb.st_atimespec.tv_sec; - unsigned int c = sb.st_ctimespec.tv_sec; - unsigned int m = sb.st_mtimespec.tv_sec; - return a + c + m; -} diff --git a/config/has/st-birthtim.c b/config/has/st-birthtim.c deleted file mode 100644 index 4964571..0000000 --- a/config/has/st-birthtim.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - return sb.st_birthtim.tv_sec; -} diff --git a/config/has/st-birthtimespec.c b/config/has/st-birthtimespec.c deleted file mode 100644 index 91a613f..0000000 --- a/config/has/st-birthtimespec.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - return sb.st_birthtimespec.tv_sec; -} diff --git a/config/has/st-flags.c b/config/has/st-flags.c deleted file mode 100644 index b1d0c32..0000000 --- a/config/has/st-flags.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct stat sb = {0}; - return sb.st_flags; -} diff --git a/config/has/statx-syscall.c b/config/has/statx-syscall.c deleted file mode 100644 index 87ec869..0000000 --- a/config/has/statx-syscall.c +++ /dev/null @@ -1,13 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include -#include - -int main(void) { - struct statx sb; - syscall(SYS_statx, AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); - return 0; -} diff --git a/config/has/statx.c b/config/has/statx.c deleted file mode 100644 index 65f1674..0000000 --- a/config/has/statx.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - struct statx sb; - statx(AT_FDCWD, ".", 0, STATX_BASIC_STATS, &sb); - return 0; -} diff --git a/config/has/strerror-l.c b/config/has/strerror-l.c deleted file mode 100644 index 3dcc4d7..0000000 --- a/config/has/strerror-l.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include -#include - -int main(void) { - locale_t locale = duplocale(LC_GLOBAL_LOCALE); - return !strerror_l(ENOMEM, locale); -} diff --git a/config/has/strerror-r-gnu.c b/config/has/strerror-r-gnu.c deleted file mode 100644 index 26ca0ee..0000000 --- a/config/has/strerror-r-gnu.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - char buf[256]; - // Check that strerror_r() returns a pointer - return *strerror_r(ENOMEM, buf, sizeof(buf)); -} diff --git a/config/has/strerror-r-posix.c b/config/has/strerror-r-posix.c deleted file mode 100644 index 41b2d30..0000000 --- a/config/has/strerror-r-posix.c +++ /dev/null @@ -1,11 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include -#include - -int main(void) { - char buf[256]; - // Check that strerror_r() returns an integer - return 2 * strerror_r(ENOMEM, buf, sizeof(buf)); -} diff --git a/config/has/tm-gmtoff.c b/config/has/tm-gmtoff.c deleted file mode 100644 index 543df48..0000000 --- a/config/has/tm-gmtoff.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - struct tm tm = {0}; - return tm.tm_gmtoff; -} diff --git a/config/has/uselocale.c b/config/has/uselocale.c deleted file mode 100644 index a712ff8..0000000 --- a/config/has/uselocale.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - locale_t locale = uselocale((locale_t)0); - return locale == LC_GLOBAL_LOCALE; -} diff --git a/config/header.mk b/config/header.mk deleted file mode 100644 index 50ad5e8..0000000 --- a/config/header.mk +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/config.h - -include config/prelude.mk -include ${GEN}/config.mk -include config/exports.mk - -# All header fragments we generate -HEADERS := \ - ${GEN}/has/acl-get-entry.h \ - ${GEN}/has/acl-get-file.h \ - ${GEN}/has/acl-get-tag-type.h \ - ${GEN}/has/acl-is-trivial-np.h \ - ${GEN}/has/acl-trivial.h \ - ${GEN}/has/aligned-alloc.h \ - ${GEN}/has/confstr.h \ - ${GEN}/has/extattr-get-file.h \ - ${GEN}/has/extattr-get-link.h \ - ${GEN}/has/extattr-list-file.h \ - ${GEN}/has/extattr-list-link.h \ - ${GEN}/has/fdclosedir.h \ - ${GEN}/has/getdents.h \ - ${GEN}/has/getdents64.h \ - ${GEN}/has/getdents64-syscall.h \ - ${GEN}/has/getprogname.h \ - ${GEN}/has/getprogname-gnu.h \ - ${GEN}/has/max-align-t.h \ - ${GEN}/has/pipe2.h \ - ${GEN}/has/posix-spawn-addfchdir.h \ - ${GEN}/has/posix-spawn-addfchdir-np.h \ - ${GEN}/has/st-acmtim.h \ - ${GEN}/has/st-acmtimespec.h \ - ${GEN}/has/st-birthtim.h \ - ${GEN}/has/st-birthtimespec.h \ - ${GEN}/has/st-flags.h \ - ${GEN}/has/statx.h \ - ${GEN}/has/statx-syscall.h \ - ${GEN}/has/strerror-l.h \ - ${GEN}/has/strerror-r-gnu.h \ - ${GEN}/has/strerror-r-posix.h \ - ${GEN}/has/tm-gmtoff.h \ - ${GEN}/has/uselocale.h - -# Previously generated by pkgs.mk -PKG_HEADERS := ${ALL_PKGS:%=${GEN}/use/%.h} - -${GEN}/config.h: ${PKG_HEADERS} ${HEADERS} - ${MSG} "[ GEN] ${TGT}" - printf '// %s\n' "${TGT}" >$@ - printf '#ifndef BFS_CONFIG_H\n' >>$@ - printf '#define BFS_CONFIG_H\n' >>$@ - cat ${.ALLSRC} >>$@ - printf '#endif // BFS_CONFIG_H\n' >>$@ - cat ${.ALLSRC:%=%.log} >$@.log - ${VCAT} $@ -.PHONY: ${GEN}/config.h - -# The short name of the config test -SLUG = ${@:${GEN}/%.h=%} - -${HEADERS}:: - ${MKDIR} ${@D} - if config/define-if.sh ${SLUG} config/cc.sh config/${SLUG}.c >$@ 2>$@.log; then \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' ${SLUG}.c; \ - else \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' ${SLUG}.c; \ - fi diff --git a/config/pkgconf.sh b/config/pkgconf.sh deleted file mode 100755 index 365ed61..0000000 --- a/config/pkgconf.sh +++ /dev/null @@ -1,100 +0,0 @@ -#!/bin/sh - -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# pkg-config wrapper with hardcoded fallbacks - -set -eu - -MODE= -case "${1:-}" in - --*) - MODE="$1" - shift -esac - -if [ $# -lt 1 ]; then - exit -fi - -case "$XNOLIBS" in - y|1) - exit 1 -esac - -if [ -z "$MODE" ]; then - # Check whether the libraries exist at all - for LIB; do - # Check ${USE_$LIB} - USE_LIB="USE_$(printf '%s' "$LIB" | tr 'a-z-' 'A-Z_')" - eval "USE=\"\${$USE_LIB:-}\"" - case "$USE" in - y|1) - continue - ;; - n|0) - exit 1 - ;; - esac - - CFLAGS=$("$0" --cflags "$LIB") || exit 1 - LDFLAGS=$("$0" --ldflags "$LIB") || exit 1 - LDLIBS=$("$0" --ldlibs "$LIB") || exit 1 - config/cc.sh $CFLAGS $LDFLAGS config/use/$LIB.c $LDLIBS || exit 1 - done -fi - -# Defer to pkg-config if possible -if command -v "${XPKG_CONFIG:-}" >/dev/null 2>&1; then - case "$MODE" in - --cflags) - "$XPKG_CONFIG" --cflags "$@" - ;; - --ldflags) - "$XPKG_CONFIG" --libs-only-L --libs-only-other "$@" - ;; - --ldlibs) - "$XPKG_CONFIG" --libs-only-l "$@" - ;; - esac - - exit -fi - -# pkg-config unavailable, emulate it ourselves -CFLAGS="" -LDFLAGS="" -LDLIBS="" - -for LIB; do - case "$LIB" in - libacl) - LDLIB=-lacl - ;; - libcap) - LDLIB=-lcap - ;; - libselinux) - LDLIB=-lselinux - ;; - liburing) - LDLIB=-luring - ;; - oniguruma) - LDLIB=-lonig - ;; - *) - printf 'error: Unknown package %s\n' "$LIB" >&2 - exit 1 - ;; - esac - - LDLIBS="$LDLIBS$LDLIB " -done - -case "$MODE" in - --ldlibs) - printf '%s\n' "$LDLIBS" - ;; -esac diff --git a/config/pkgs.mk b/config/pkgs.mk deleted file mode 100644 index 898f262..0000000 --- a/config/pkgs.mk +++ /dev/null @@ -1,36 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Makefile that generates gen/pkgs.mk - -include config/prelude.mk -include ${GEN}/vars.mk -include ${GEN}/flags.mk -include config/exports.mk - -HEADERS := ${ALL_PKGS:%=${GEN}/use/%.h} - -${GEN}/pkgs.mk: ${HEADERS} - ${MSG} "[ GEN] ${TGT}" - printf '# %s\n' "${TGT}" >$@ - gen() { \ - printf 'PKGS := %s\n' "$$*"; \ - printf 'CFLAGS += %s\n' "$$(config/pkgconf.sh --cflags "$$@")"; \ - printf 'LDFLAGS += %s\n' "$$(config/pkgconf.sh --ldflags "$$@")"; \ - printf 'LDLIBS := %s $${LDLIBS}\n' "$$(config/pkgconf.sh --ldlibs "$$@")"; \ - }; \ - gen $$(grep -l ' true$$' ${.ALLSRC} | sed 's|.*/\(.*\)\.h|\1|') >>$@ - ${VCAT} $@ - -.PHONY: ${GEN}/pkgs.mk - -# Convert ${GEN}/use/foo.h to foo -PKG = ${@:${GEN}/use/%.h=%} - -${HEADERS}:: - ${MKDIR} ${@D} - if config/define-if.sh use/${PKG} config/pkgconf.sh ${PKG} >$@ 2>$@.log; then \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✔\n' use/${PKG}.c; \ - else \ - test "${IS_V}" || printf '[ CC ] %-${MSG_WIDTH}s ✘\n' use/${PKG}.c; \ - fi diff --git a/config/prelude.mk b/config/prelude.mk deleted file mode 100644 index 109e60c..0000000 --- a/config/prelude.mk +++ /dev/null @@ -1,162 +0,0 @@ -# Copyright © Tavian Barnes -# SPDX-License-Identifier: 0BSD - -# Common makefile utilities. Compatible with both GNU make and most BSD makes. - -# BSD make will chdir into ${.OBJDIR} by default, unless we tell it not to -.OBJDIR: . - -# We don't use any suffix rules -.SUFFIXES: - -# GNU make has $^ for the full list of targets, while BSD make has $> and the -# long-form ${.ALLSRC}. We could write $^ $> to get them both, but that would -# break if one of them implemented support for the other. So instead, bring -# BSD's ${.ALLSRC} to GNU. -.ALLSRC ?= $^ - -# For out-of-tree builds, e.g. -# -# $ make config BUILDDIR=/path/to/build/dir -# $ make BUILDDIR=/path/to/build/dir -BUILDDIR ?= . - -# Shorthand for build subdirectories -BIN := ${BUILDDIR}/bin -GEN := ${BUILDDIR}/gen -OBJ := ${BUILDDIR}/obj - -# GNU make strips a leading ./ from target names, so do the same for BSD make -BIN := ${BIN:./%=%} -GEN := ${GEN:./%=%} -OBJ := ${OBJ:./%=%} - -# The configuration file generated by `make config` -CONFIG := ${GEN}/config.mk - -# Installation paths -DESTDIR ?= -PREFIX ?= /usr -MANDIR ?= ${PREFIX}/share/man - -# GNU make supports `export VAR`, but BSD make requires `export VAR=value`. -# Sadly, GNU make gives a recursion error on `export VAR=${VAR}`. -_BUILDDIR := ${BUILDDIR} -export BUILDDIR=${_BUILDDIR} - -# Configurable executables; can be overridden with -# -# $ make config CC=clang -CC ?= cc -INSTALL ?= install -MKDIR ?= mkdir -p -PKG_CONFIG ?= pkg-config -RM ?= rm -f - -# GNU and BSD make have incompatible syntax for conditionals, but we can do a -# lot with just nested variable expansion. We use "y" as the canonical -# truthy value, and "" (the empty string) as the canonical falsey value. -# -# To normalize a boolean, use ${TRUTHY,${VAR}}, which expands like this: -# -# VAR=y ${TRUTHY,${VAR}} => ${TRUTHY,y} => y -# VAR=1 ${TRUTHY,${VAR}} => ${TRUTHY,1} => y -# VAR=n ${TRUTHY,${VAR}} => ${TRUTHY,n} => [empty] -# VAR=other ${TRUTHY,${VAR}} => ${TRUTHY,other} => [empty] -# VAR= ${TRUTHY,${VAR}} => ${TRUTHY,} => [emtpy] -# -# Inspired by https://github.com/wahern/autoguess -TRUTHY,y := y -TRUTHY,1 := y - -# Boolean operators are also implemented with nested expansion -NOT, := y - -# Normalize ${V} to either "y" or "" -IS_V := ${TRUTHY,${V}} - -# Suppress output unless V=1 -Q, := @ -Q := ${Q,${IS_V}} - -# The current target, with ${BUILDDIR} stripped for shorter messages -TGT = ${@:${BUILDDIR}/%=%} - -# Show full commands with `make V=1`, otherwise short summaries -MSG = @msg() { \ - MSG="$$1"; \ - shift; \ - test "${IS_V}" || printf '%s\n' "$$MSG"; \ - test "$${1:-}" || return 0; \ - test "${IS_V}" && printf '%s\n' "$$*"; \ - "$$@"; \ - }; \ - msg - -# Maximum width of a short message, to align the ✔/✘ -MSG_WIDTH := 30 - -# cat a file if V=1 -VCAT,y := @cat -VCAT, := @: -VCAT := ${VCAT,${IS_V}} - -# All external dependencies -ALL_PKGS := \ - libacl \ - libcap \ - libselinux \ - liburing \ - oniguruma - -# List all object files here, as they're needed by both `make config` and `make` - -# All object files except the entry point -LIBBFS := \ - ${OBJ}/src/alloc.o \ - ${OBJ}/src/bar.o \ - ${OBJ}/src/bfstd.o \ - ${OBJ}/src/bftw.o \ - ${OBJ}/src/color.o \ - ${OBJ}/src/ctx.o \ - ${OBJ}/src/diag.o \ - ${OBJ}/src/dir.o \ - ${OBJ}/src/dstring.o \ - ${OBJ}/src/eval.o \ - ${OBJ}/src/exec.o \ - ${OBJ}/src/expr.o \ - ${OBJ}/src/fsade.o \ - ${OBJ}/src/ioq.o \ - ${OBJ}/src/mtab.o \ - ${OBJ}/src/opt.o \ - ${OBJ}/src/parse.o \ - ${OBJ}/src/printf.o \ - ${OBJ}/src/pwcache.o \ - ${OBJ}/src/stat.o \ - ${OBJ}/src/thread.o \ - ${OBJ}/src/trie.o \ - ${OBJ}/src/typo.o \ - ${OBJ}/src/xregex.o \ - ${OBJ}/src/xspawn.o \ - ${OBJ}/src/xtime.o \ - ${OBJ}/gen/version.o - -# Unit test objects -UNIT_OBJS := \ - ${OBJ}/tests/alloc.o \ - ${OBJ}/tests/bfstd.o \ - ${OBJ}/tests/bit.o \ - ${OBJ}/tests/ioq.o \ - ${OBJ}/tests/main.o \ - ${OBJ}/tests/trie.o \ - ${OBJ}/tests/xspawn.o \ - ${OBJ}/tests/xtime.o - -# All object files -OBJS := \ - ${OBJ}/src/main.o \ - ${OBJ}/tests/mksock.o \ - ${OBJ}/tests/xspawnee.o \ - ${OBJ}/tests/xtouch.o \ - ${LIBBFS} \ - ${UNIT_OBJS} diff --git a/config/use/libacl.c b/config/use/libacl.c deleted file mode 100644 index de1fe50..0000000 --- a/config/use/libacl.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - acl_free(0); - return 0; -} diff --git a/config/use/libcap.c b/config/use/libcap.c deleted file mode 100644 index 58e832c..0000000 --- a/config/use/libcap.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - cap_free(0); - return 0; -} diff --git a/config/use/libselinux.c b/config/use/libselinux.c deleted file mode 100644 index bca409d..0000000 --- a/config/use/libselinux.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - freecon(0); - return 0; -} diff --git a/config/use/liburing.c b/config/use/liburing.c deleted file mode 100644 index bea499a..0000000 --- a/config/use/liburing.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - io_uring_free_probe(0); - return 0; -} diff --git a/config/use/oniguruma.c b/config/use/oniguruma.c deleted file mode 100644 index cb17596..0000000 --- a/config/use/oniguruma.c +++ /dev/null @@ -1,9 +0,0 @@ -// Copyright © Tavian Barnes -// SPDX-License-Identifier: 0BSD - -#include - -int main(void) { - onig_free(0); - return 0; -} diff --git a/configure b/configure new file mode 100755 index 0000000..6920212 --- /dev/null +++ b/configure @@ -0,0 +1,98 @@ +#!/bin/sh + +# Copyright © Tavian Barnes +# SPDX-License-Identifier: 0BSD + +# bfs build configuration script + +set -eu + +help() { + cat <] [CC=...] [CFLAGS=...] [...] + +Compiler configuration: + + CC + The C compiler to use + CPPFLAGS + C preprocessor flags + CFLAGS + C compiler flags + LDFLAGS + Linker flags + LDLIBS + Dynamic libraries to link + + EXTRA_CPPFLAGS + EXTRA_CFLAGS + EXTRA_LDFLAGS + EXTRA_LDLIBS + Adds to the default flags, instead of replacing them + +Build profiles: + + RELEASE=y + Enable optimizations, disable assertions + ASAN=y + LSAN=y + MSAN=y + TSAN=y + UBSAN=y + Enable sanitizers + GCOV=y + Enable code coverage instrumentation + +External dependencies: + + PKG_CONFIG + The pkg-config binary to use + + USE_LIBACL=[y|n] + USE_LIBCAP=[y|n] + USE_LIBSELINUX=[y|n] + USE_LIBURIG=[y|n] + USE_ONIGURUMA=[y|n] + Enable or disable external dependencies + +Packaging configuration: + + PREFIX + Set the installation prefix (default: /usr) + MANDIR + Set the man page directory (default: \$PREFIX/share/man) +EOF +} + +for arg; do + case "$arg" in + --help) + help + exit 0 + ;; + -*|*=*) + continue # make flag (-j2) or variable (CC=clang) + ;; + *) + printf 'error: Unrecognized option "%s"\n\n' "$arg" >&2 + help >&2 + exit 1 + ;; + esac +done + +# Get the relative path to the source tree based on how the script was run +DIR=$(dirname -- "$0") + +# Set up symbolic links for out-of-tree builds +for f in Makefile build completions docs src tests; do + test -e "$f" || ln -s "$DIR/$f" "$f" +done + +# Infer -jN unless MAKEFLAGS is set +j= +if ! [ "${MAKEFLAGS+y}" ]; then + j="-j$({ nproc || sysctl -n hw.ncpu || getconf _NPROCESSORS_ONLN || echo 1; } 2>/dev/null)" +fi + +${MAKE:-make} $j -rsf build/config.mk "$@" diff --git a/docs/BUILDING.md b/docs/BUILDING.md index 4ed139c..db5d721 100644 --- a/docs/BUILDING.md +++ b/docs/BUILDING.md @@ -4,10 +4,9 @@ Building `bfs` Compiling --------- -`bfs` uses [GNU Make](https://www.gnu.org/software/make/) as its build system. A simple invocation of - $ make config + $ ./configure $ make should build `bfs` successfully. @@ -18,7 +17,6 @@ For example, to use all your cores, run `make -j$(nproc)`. | Command | Description | |------------------|---------------------------------------------------------------| -| `make config` | Configures the build system | | `make` | Builds just the `bfs` binary | | `make all` | Builds everything, including the tests (but doesn't run them) | | `make check` | Builds everything, and runs the tests | @@ -33,13 +31,13 @@ The configuration system provides a few shorthand flags for handy configurations | Command | Description | |-------------------------|-------------------------------------------------------------| -| `make config RELEASE=y` | Build `bfs` with optimizations, LTO, and without assertions | -| `make config ASAN=y` | Enable [AddressSanitizer] | -| `make config LSAN=y` | Enable [LeakSanitizer] | -| `make config MSAN=y` | Enable [MemorySanitizer] | -| `make config TSAN=y` | Enable [ThreadSanitizer] | -| `make config UBSAN=y` | Enable [UndefinedBehaviorSanitizer] | -| `make config GCOV=y` | Enable [code coverage] | +| `./configure RELEASE=y` | Build `bfs` with optimizations, LTO, and without assertions | +| `./configure ASAN=y` | Enable [AddressSanitizer] | +| `./configure LSAN=y` | Enable [LeakSanitizer] | +| `./configure MSAN=y` | Enable [MemorySanitizer] | +| `./configure TSAN=y` | Enable [ThreadSanitizer] | +| `./configure UBSAN=y` | Enable [UndefinedBehaviorSanitizer] | +| `./configure GCOV=y` | Enable [code coverage] | [AddressSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizer [LeakSanitizer]: https://github.com/google/sanitizers/wiki/AddressSanitizerLeakSanitizer#stand-alone-mode @@ -48,21 +46,20 @@ The configuration system provides a few shorthand flags for handy configurations [UndefinedBehaviorSanitizer]: https://clang.llvm.org/docs/UndefinedBehaviorSanitizer.html [code coverage]: https://gcc.gnu.org/onlinedocs/gcc/Gcov.html -You can combine multiple profiles (e.g. `make config ASAN=y UBSAN=y`), but not all of them will work together. +You can combine multiple profiles (e.g. `./configure ASAN=y UBSAN=y`), but not all of them will work together. ### Flags -Other flags can be specified on the `make config` command line or in the environment. +Other flags can be specified on the `./configure` command line or in the environment. Here are some of the common ones; check the [`Makefile`](/Makefile) for more. | Flag | Description | |-------------------------------------|----------------------------------------------------| -| `CC` | The C compiler to use, e.g. `make config CC=clang` | +| `CC` | The C compiler to use, e.g. `./configure CC=clang` | | `CFLAGS`
`EXTRA_CFLAGS` | Override/add to the default compiler flags | | `LDFLAGS`
`EXTRA_LDFLAGS` | Override/add to the linker flags | | `USE_LIBACL`
`USE_LIBCAP`
... | Enable/disable [optional dependencies] | | `TEST_FLAGS` | `tests.sh` flags for `make check` | -| `BUILDDIR` | The build output directory (default: `.`) | | `DESTDIR` | The root directory for `make install` | | `PREFIX` | The installation prefix (default: `/usr`) | | `MANDIR` | The man page installation directory | @@ -72,9 +69,9 @@ Here are some of the common ones; check the [`Makefile`](/Makefile) for more. ### Dependencies `bfs` depends on some system libraries for some of its features. -These dependencies are optional, and can be turned off in `make config` if necessary by setting the appropriate variable to `n` (e.g. `make config USE_ONIGURUMA=n`). +These dependencies are optional, and can be turned off in `./configure` if necessary by setting the appropriate variable to `n` (e.g. `./configure USE_ONIGURUMA=n`). -| Dependency | Platforms | `make config` flag | +| Dependency | Platforms | `./configure` flag | |--------------|------------|--------------------| | [libacl] | Linux only | `USE_LIBACL` | | [libcap] | Linux only | `USE_LIBCAP` | @@ -90,22 +87,28 @@ These dependencies are optional, and can be turned off in `make config` if neces ### Dependency tracking -The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in the [`Makefile`](/Makefile)). +The build system automatically tracks header dependencies with the `-M` family of compiler options (see `DEPFLAGS` in [`build/deps.mk`](/build/deps.mk)). So if you edit a header file, `make` will rebuild the necessary object files ensuring they don't go out of sync. We also add a dependency on the current configuration, so you can change configurations and rebuild without having to `make clean`. - -We go one step further than most build systems by tracking the flags that were used for the previous compilation. -That means you can change configurations without having to `make clean`. For example, - $ make config + $ ./configure $ make - $ make config RELEASE=y + $ ./configure RELEASE=y $ make will build the project in debug mode and then rebuild it in release mode. +### Out-of-tree builds + +You can set up an out-of-tree build by running the `configure` script from another directory, for example: + + $ mkdir out + $ cd out + $ ../configure + $ make + Testing ------- diff --git a/tests/util.sh b/tests/util.sh index 7dba9fb..3969db5 100644 --- a/tests/util.sh +++ b/tests/util.sh @@ -12,12 +12,9 @@ _realpath() ( ) # Globals -TESTS=$(_realpath "$TESTS") -if [ "${BUILDDIR-}" ]; then - BIN=$(_realpath "$BUILDDIR/bin") -else - BIN=$(_realpath "$TESTS/../bin") -fi +ROOT=$(_realpath "$(dirname -- "$TESTS")") +TESTS="$ROOT/tests" +BIN="$ROOT/bin" MKSOCK="$BIN/tests/mksock" XTOUCH="$BIN/tests/xtouch" UNAME=$(uname) diff --git a/tests/xspawn.c b/tests/xspawn.c index 7362aa5..785ea48 100644 --- a/tests/xspawn.c +++ b/tests/xspawn.c @@ -64,27 +64,20 @@ static bool check_use_path(bool use_posix) { spawn.flags &= ~BFS_SPAWN_USE_POSIX; } - const char *builddir = getenv("BUILDDIR"); - dchar *bin = dstrprintf("%s/bin", builddir ? builddir : "."); - ret &= bfs_pcheck(bin, "dstrprintf()"); - if (!ret) { - goto destroy; - } - - ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, bin, O_RDONLY | O_DIRECTORY, 0) == 0); + ret &= bfs_pcheck(bfs_spawn_addopen(&spawn, 10, "bin", O_RDONLY | O_DIRECTORY, 0) == 0); ret &= bfs_pcheck(bfs_spawn_adddup2(&spawn, 10, 11) == 0); ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 10) == 0); ret &= bfs_pcheck(bfs_spawn_addfchdir(&spawn, 11) == 0); ret &= bfs_pcheck(bfs_spawn_addclose(&spawn, 11) == 0); if (!ret) { - goto bin; + goto destroy; } // Check that $PATH is resolved in the parent's environment char **envp; ret &= bfs_pcheck(envp = envdup()); if (!ret) { - goto bin; + goto destroy; } // Check that $PATH is resolved after the file actions @@ -138,8 +131,6 @@ env: free(*var); } free(envp); -bin: - dstrfree(bin); destroy: ret &= bfs_pcheck(bfs_spawn_destroy(&spawn) == 0); out: -- cgit v1.2.3 From f976c98d334dce9ba30aa7da4427bb530aeea536 Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Mon, 6 May 2024 16:04:05 -0400 Subject: xtime: Use the libc's timegm() if present --- build/has/timegm.c | 9 +++++++++ build/header.mk | 1 + src/xtime.c | 36 +++++++++++++++++++++++++++++++++--- tests/xtime.c | 12 ++++++++---- 4 files changed, 51 insertions(+), 7 deletions(-) create mode 100644 build/has/timegm.c (limited to 'tests') diff --git a/build/has/timegm.c b/build/has/timegm.c new file mode 100644 index 0000000..6e2d155 --- /dev/null +++ b/build/has/timegm.c @@ -0,0 +1,9 @@ +// Copyright © Tavian Barnes +// SPDX-License-Identifier: 0BSD + +#include + +int main(void) { + struct tm tm = {0}; + return (int)timegm(&tm); +} diff --git a/build/header.mk b/build/header.mk index a9157ad..d235fd0 100644 --- a/build/header.mk +++ b/build/header.mk @@ -40,6 +40,7 @@ HEADERS := \ gen/has/strerror-l.h \ gen/has/strerror-r-gnu.h \ gen/has/strerror-r-posix.h \ + gen/has/timegm.h \ gen/has/tm-gmtoff.h \ gen/has/uselocale.h diff --git a/src/xtime.c b/src/xtime.c index 91ed915..c3537e7 100644 --- a/src/xtime.c +++ b/src/xtime.c @@ -12,13 +12,13 @@ #include int xmktime(struct tm *tm, time_t *timep) { - *timep = mktime(tm); + time_t time = mktime(tm); - if (*timep == -1) { + if (time == -1) { int error = errno; struct tm tmp; - if (!localtime_r(timep, &tmp)) { + if (!localtime_r(&time, &tmp)) { bfs_bug("localtime_r(-1): %s", xstrerror(errno)); return -1; } @@ -30,9 +30,37 @@ int xmktime(struct tm *tm, time_t *timep) { } } + *timep = time; + return 0; +} + +#if BFS_HAS_TIMEGM + +int xtimegm(struct tm *tm, time_t *timep) { + time_t time = timegm(tm); + + if (time == -1) { + int error = errno; + + struct tm tmp; + if (!gmtime_r(&time, &tmp)) { + bfs_bug("gmtime_r(-1): %s", xstrerror(errno)); + return -1; + } + + if (tm->tm_year != tmp.tm_year || tm->tm_yday != tmp.tm_yday + || tm->tm_hour != tmp.tm_hour || tm->tm_min != tmp.tm_min || tm->tm_sec != tmp.tm_sec) { + errno = error; + return -1; + } + } + + *timep = time; return 0; } +#else + static int safe_add(int *value, int delta) { if (*value >= 0) { if (delta > INT_MAX - *value) { @@ -147,6 +175,8 @@ overflow: return -1; } +#endif // !BFS_HAS_TIMEGM + /** Parse a decimal digit. */ static int xgetdigit(char c) { int ret = c - '0'; diff --git a/tests/xtime.c b/tests/xtime.c index a7c63d2..d9d6c5c 100644 --- a/tests/xtime.c +++ b/tests/xtime.c @@ -137,6 +137,7 @@ static bool check_one_xtimegm(const struct tm *tm) { return ret; } +#if !BFS_HAS_TIMEGM /** Check an overflowing xtimegm() call. */ static bool check_xtimegm_overflow(const struct tm *tm) { struct tm copy = *tm; @@ -154,6 +155,7 @@ static bool check_xtimegm_overflow(const struct tm *tm) { return ret; } +#endif /** xtimegm() tests. */ static bool check_xtimegm(void) { @@ -173,11 +175,13 @@ static bool check_xtimegm(void) { ret &= check_one_xtimegm(&tm); } +#if !BFS_HAS_TIMEGM // Check integer overflow cases - check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); - check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_sec = INT_MAX, .tm_min = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_min = INT_MAX, .tm_hour = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_hour = INT_MAX, .tm_mday = INT_MAX }); + ret &= check_xtimegm_overflow(&(struct tm) { .tm_mon = INT_MAX, .tm_year = INT_MAX }); +#endif return ret; } -- cgit v1.2.3