summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--Makefile5
-rwxr-xr-xbuild/cc.sh11
-rw-r--r--build/config.mk8
-rw-r--r--build/deps.mk2
-rw-r--r--build/header.mk24
-rwxr-xr-xbuild/pkgconf.sh2
-rwxr-xr-xconfigure153
-rw-r--r--docs/SECURITY.md126
-rw-r--r--docs/bfs.18
-rw-r--r--src/bftw.c41
-rw-r--r--src/color.c12
-rw-r--r--src/exec.c2
-rw-r--r--src/ioq.c22
-rw-r--r--src/parse.c11
-rw-r--r--src/prelude.h3
-rw-r--r--src/version.c8
16 files changed, 293 insertions, 145 deletions
diff --git a/Makefile b/Makefile
index 2679c85..d21cbb3 100644
--- a/Makefile
+++ b/Makefile
@@ -215,6 +215,11 @@ check-install::
bin/bfs pkg -not -type d -print -exit 1
${RM} -r pkg
+# Check man page markup
+check-man::
+ ${MSG} "[LINT] docs/bfs.1" \
+ groff -man -rCHECKSTYLE=3 -ww -b -z docs/bfs.1
+
## Cleanup (`make clean`)
# Clean all build products
diff --git a/build/cc.sh b/build/cc.sh
index 45d51ca..23a4c01 100755
--- a/build/cc.sh
+++ b/build/cc.sh
@@ -5,12 +5,5 @@
# 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"
-)
+set -eux
+$XCC $XCPPFLAGS $XCFLAGS $XLDFLAGS "$@" $XLDLIBS
diff --git a/build/config.mk b/build/config.mk
index 24873ec..80206c7 100644
--- a/build/config.mk
+++ b/build/config.mk
@@ -7,7 +7,7 @@ include build/prelude.mk
include build/exports.mk
# All configuration steps
-config: gen/config.mk gen/config.h
+config: gen/config.mk
.PHONY: config
# Makefile fragments generated by `./configure`
@@ -18,10 +18,10 @@ MKS := \
gen/pkgs.mk
# The main configuration file, which includes the others
-gen/config.mk: ${MKS}
+gen/config.mk: ${MKS} gen/config.h
${MSG} "[ GEN] $@"
@printf '# %s\n' "$@" >$@
- @printf 'include %s\n' ${.ALLSRC} >>$@
+ @printf 'include %s\n' ${MKS} >>$@
${VCAT} gen/config.mk
.PHONY: gen/config.mk
@@ -57,6 +57,6 @@ gen/pkgs.mk: gen/flags.mk
.PHONY: gen/pkgs.mk
# Compile-time feature detection
-gen/config.h: gen/config.mk
+gen/config.h: gen/pkgs.mk
@+XMAKEFLAGS="$$MAKEFLAGS" ${MAKE} -sf build/header.mk $@
.PHONY: gen/config.h
diff --git a/build/deps.mk b/build/deps.mk
index d382f5d..a6ea673 100644
--- a/build/deps.mk
+++ b/build/deps.mk
@@ -11,7 +11,7 @@ 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 \
+ @if build/cc.sh -MD -MP build/empty.c -o gen/.deps.out; then \
printf '_CPPFLAGS += -MD -MP\n'; \
fi >>$@ 2>$@.log
${VCAT} $@
diff --git a/build/header.mk b/build/header.mk
index 8b28346..afd04d0 100644
--- a/build/header.mk
+++ b/build/header.mk
@@ -4,7 +4,10 @@
# Makefile that generates gen/config.h
include build/prelude.mk
-include gen/config.mk
+include gen/vars.mk
+include gen/flags.mk
+include gen/deps.mk
+include gen/pkgs.mk
include build/exports.mk
# All header fragments we generate
@@ -61,9 +64,10 @@ gen/config.h: ${PKG_HEADERS} ${HEADERS}
@printf '#define BFS_CONFIG_H\n' >>$@
@cat ${.ALLSRC} >>$@
@printf '#endif // BFS_CONFIG_H\n' >>$@
- @cat ${.ALLSRC:%=%.log} >gen/config.log
+ @cat gen/cc.log ${.ALLSRC:%=%.log} >gen/config.log
${VCAT} $@
- @printf '%s' "$$CONFIG" | build/embed.sh >gen/config.i
+ @printf '%s' "$$CONFFLAGS" | build/embed.sh >gen/confflags.i
+ @printf '%s' "$$XCC" | build/embed.sh >gen/cc.i
@printf '%s' "$$XCPPFLAGS" | build/embed.sh >gen/cppflags.i
@printf '%s' "$$XCFLAGS" | build/embed.sh >gen/cflags.i
@printf '%s' "$$XLDFLAGS" | build/embed.sh >gen/ldflags.i
@@ -72,8 +76,18 @@ gen/config.h: ${PKG_HEADERS} ${HEADERS}
# The short name of the config test
SLUG = ${@:gen/%.h=%}
+# The hidden output file name
+OUT = ${SLUG:has/%=gen/has/.%.out}
-${HEADERS}::
+${HEADERS}: cc
@${MKDIR} ${@D}
- @build/define-if.sh ${SLUG} build/cc.sh build/${SLUG}.c >$@ 2>$@.log; \
+ @build/define-if.sh ${SLUG} build/cc.sh build/${SLUG}.c -o ${OUT} >$@ 2>$@.log; \
build/msg-if.sh "[ CC ] ${SLUG}.c" test $$? -eq 0
+.PHONY: ${HEADERS}
+
+# Check that the C compiler works at all
+cc::
+ @build/cc.sh build/empty.c -o gen/.cc.out 2>gen/cc.log; \
+ ret=$$?; \
+ build/msg-if.sh "[ CC ] build/empty.c" test $$ret -eq 0; \
+ exit $$ret
diff --git a/build/pkgconf.sh b/build/pkgconf.sh
index 244c95d..a8a3341 100755
--- a/build/pkgconf.sh
+++ b/build/pkgconf.sh
@@ -37,7 +37,7 @@ if [ -z "$MODE" ]; then
CFLAGS=$("$0" --cflags "$LIB") || exit 1
LDFLAGS=$("$0" --ldflags "$LIB") || exit 1
LDLIBS=$("$0" --ldlibs "$LIB") || exit 1
- build/cc.sh $CFLAGS $LDFLAGS build/with/$LIB.c $LDLIBS || exit 1
+ build/cc.sh $CFLAGS $LDFLAGS "build/with/$LIB.c" $LDLIBS -o "gen/with/.$LIB.out" || exit 1
done
fi
diff --git a/configure b/configure
index 40eb313..8fb0cc2 100755
--- a/configure
+++ b/configure
@@ -7,22 +7,62 @@
set -eu
-# Save the ./configure command line for bfs --version
-export CONFIG="$0 $*"
+# Print the help message
+help() {
+ cat <<EOF
+Usage:
-# Default to `make`
-MAKE="${MAKE:-make}"
-
-# Pass -j$(nproc) unless MAKEFLAGS is set
-if [ "${MAKEFLAGS+y}" ]; then
- j=""
-else
- j="-j$({ nproc || sysctl -n hw.ncpu || getconf _NPROCESSORS_ONLN || echo 1; } 2>/dev/null)"
-fi
-
-# Convert kebab-case to UPPER_CASE
-toupper() {
- printf '%s' "$1" | tr 'a-z-' 'A-Z_'
+ \$ $0 [--enable-*|--disable-*] [--with-*|--without-*] [CC=...] [...]
+ \$ $MAKE $j
+
+Variables set in the environment or on the command line will be picked up:
+
+ MAKE
+ The make implementation to use
+ CC
+ The C compiler to use
+
+ CPPFLAGS="-I... -D..."
+ CFLAGS="-W... -f..."
+ LDFLAGS="-L... -Wl,..."
+ Preprocessor/compiler/linker flags
+
+ LDLIBS="-l... -l..."
+ Dynamic libraries to link
+
+ EXTRA_{CPPFLAGS,CFLAGS,LDFLAGS,LDLIBS}
+ Adds to the default flags, instead of replacing them
+
+The default flags result in a plain debug build. Other build profiles include:
+
+ --enable-release
+ Enable optimizations, disable assertions
+ --enable-{asan,lsan,msan,tsan,ubsan}
+ Enable sanitizers
+ --enable-gcov
+ Enable code coverage instrumentation
+
+External dependencies are auto-detected by default, but you can build --with or
+--without them explicitly:
+
+ --with-libacl --without-libacl
+ --with-libcap --without-libcap
+ --with-libselinux --without-libselinux
+ --with-liburing --without-liburing
+ --with-oniguruma --without-oniguruma
+
+Packaging:
+
+ --prefix=/path
+ Set the installation prefix (default: /usr)
+ --mandir=/path
+ Set the man page directory (default: \$PREFIX/share/man)
+
+This script is a thin wrapper around a makefile-based configuration system.
+Any other arguments will be passed directly to the $MAKE invocation, e.g.
+
+ \$ $0 $j V=1
+EOF
}
# Report an argument parsing error
@@ -32,7 +72,26 @@ invalid() {
exit 1
}
+# Get the number of cores to use
+nproc() {
+ {
+ command nproc \
+ || sysctl -n hw.ncpu \
+ || getconf _NPROCESSORS_ONLN \
+ || echo 1
+ } 2>/dev/null
+}
+
+# Save the ./configure command line for bfs --version
+export CONFFLAGS="$*"
+
+# Default to `make`
+MAKE="${MAKE-make}"
+
+# Parse the command-line arguments
for arg; do
+ shift
+
# --[(enable|disable|with|without)-]$name[=$value]
value="${arg#*=}"
name="${arg%%=*}"
@@ -80,67 +139,13 @@ for arg; do
case "$arg" in
-h|--help)
- cat <<EOF
-Usage:
-
- \$ $0 [--enable-*|--disable-*] [--with-*|--without-*] [CC=...] [...]
- \$ $MAKE $j
-
-Variables set in the environment or on the command line will be picked up:
-
- MAKE
- The make implementation to use
- CC
- The C compiler to use
-
- CPPFLAGS="-I... -D..."
- CFLAGS="-W... -f..."
- LDFLAGS="-L... -Wl,..."
- Preprocessor/compiler/linker flags
-
- LDLIBS="-l... -l..."
- Dynamic libraries to link
-
- EXTRA_{CPPFLAGS,CFLAGS,LDFLAGS,LDLIBS}
- Adds to the default flags, instead of replacing them
-
-The default flags result in a plain debug build. Other build profiles include:
-
- --enable-release
- Enable optimizations, disable assertions
- --enable-{asan,lsan,msan,tsan,ubsan}
- Enable sanitizers
- --enable-gcov
- Enable code coverage instrumentation
-
-External dependencies are auto-detected by default, but you can build --with or
---without them explicitly:
-
- --with-libacl --without-libacl
- --with-libcap --without-libcap
- --with-libselinux --without-libselinux
- --with-liburing --without-liburing
- --with-oniguruma --without-oniguruma
-
-Packaging:
-
- --prefix=/path
- Set the installation prefix (default: /usr)
- --mandir=/path
- Set the man page directory (default: \$PREFIX/share/man)
-
-This script is a thin wrapper around a makefile-based configuration system.
-Any other arguments will be passed directly to the $MAKE invocation, e.g.
-
- \$ $0 $j V=1
-EOF
+ help
exit 0
;;
--enable-*|--disable-*)
case "$name" in
release|asan|lsan|msan|tsan|ubsan|lint|gcov)
- shift
set -- "$@" "$NAME=$yn"
;;
*)
@@ -152,7 +157,6 @@ EOF
--with-*|--without-*)
case "$name" in
libacl|libcap|libselinux|liburing|oniguruma)
- shift
set -- "$@" "WITH_$NAME=$yn"
;;
*)
@@ -162,23 +166,20 @@ EOF
;;
--prefix=*|--mandir=*)
- shift
set -- "$@" "$NAME=$value"
;;
--infodir=*|--build=*|--host=*|--target=*)
- shift
printf 'warning: Ignoring option "%s"\n' "$arg" >&2
;;
MAKE=*)
- shift
MAKE="$value"
;;
# make flag (-j2) or variable (CC=clang)
-*|*=*)
- continue
+ set -- "$@" "$arg"
;;
*)
@@ -196,6 +197,6 @@ for f in Makefile build completions docs src tests; do
done
# Set MAKEFLAGS to -j$(nproc) if it's unset
-export MAKEFLAGS="${MAKEFLAGS-$j}"
+export MAKEFLAGS="${MAKEFLAGS--j$(nproc)}"
$MAKE -rf build/config.mk "$@"
diff --git a/docs/SECURITY.md b/docs/SECURITY.md
new file mode 100644
index 0000000..f26efc5
--- /dev/null
+++ b/docs/SECURITY.md
@@ -0,0 +1,126 @@
+Security
+========
+
+Threat model
+------------
+
+`bfs` is a command line program running on multi-user operating systems.
+Those other users may be malicious, but `bfs` should not allow them to do anything they couldn't already do.
+That includes situations where one user (especially `root`) is running `bfs` on files owned or controlled by another user.
+
+On the other hand, `bfs` implicitly trusts the user running it.
+Anyone with enough control over the command line of `bfs` or any `find`-compatible tool can wreak havoc with dangerous actions like `-exec`, `-delete`, etc.
+
+> [!CAUTION]
+> The only untrusted input that should *ever* be passed on the `bfs` command line are **file paths**.
+> It is *always* unsafe to allow *any* other part of the command line to be affected by untrusted input.
+> Use the `-f` flag, or `-files0-from`, to ensure that the input is interpreted as a path.
+
+This still has security implications, incuding:
+
+- **Information disclosure:** an attacker may learn whether particular files exist by observing `bfs`'s output, exit status, or even side channels like execution time.
+- **Denial of service:** large directory trees or slow/network storage may cause `bfs` to consume excessive system resources.
+
+> [!TIP]
+> When in doubt, do not pass any untrusted input to `bfs`.
+
+
+Executing commands
+------------------
+
+The `-exec` family of actions execute commands, passing the matched paths as arguments.
+File names that begin with a dash may be misinterpreted as options, so `bfs` adds a leading `./` in some instances:
+
+```console
+user@host$ bfs -execdir echo {} \;
+./-rf
+```
+
+This might save you from accidentally running `rm -rf` (for example) when you didn't mean to.
+This mitigation applies to `-execdir`, but not `-exec`, because the full path typically does not begin with a dash.
+But it is possible, so be careful:
+
+```console
+user@host$ bfs -f -rf -exec echo {} \;
+-rf
+```
+
+
+Race conditions
+---------------
+
+Like many programs that interface with the file system, `bfs` can be affected by race conditions&mdash;in particular, "[time-of-check to time-of-use](https://en.wikipedia.org/wiki/Time-of-check_to_time-of-use)" (TOCTTOU) issues.
+For example,
+
+```console
+user@host$ bfs / -user user -exec dangerous_command {} \;
+```
+
+is not guaranteed to only run `dangerous_command` on files you own, because another user may run
+
+```console
+evil@host$ mv /path/to/file /path/to/exile
+evil@host$ mv ~/malicious /path/to/file
+```
+
+in between checking `-user user` and executing the command.
+
+> [!WARNING]
+> Be careful when running `bfs` on directories that other users have write access to, because they can modify the directory tree while `bfs` is running, leading to unpredictable results and possible TOCTTOU issues.
+
+
+Output sanitization
+-------------------
+
+In general, printing arbitrary data to a terminal may have [security](https://hdm.io/writing/termulation.txt) [implications](https://dgl.cx/2023/09/ansi-terminal-security#vulnerabilities-using-known-replies).
+On many platforms, file paths may be completely arbitrary data (except for NUL (`\0`) bytes).
+Therefore, when `bfs` is writing output to a terminal, it will escape non-printable characters:
+
+<pre>
+user@host$ touch $'\e[1mBOLD\e[0m'
+user@host$ bfs
+.
+./$'\e[1mBOLD\e[0m'
+</pre>
+
+However, this is fragile as it only applies when outputting directly to a terminal:
+
+<pre>
+user@host$ bfs | grep BOLD
+<strong>BOLD</strong>
+</pre>
+
+
+Code quality
+------------
+
+Every correctness issue in `bfs` is a potential security issue, because acting on the wrong path may do arbitrarily bad things.
+For example:
+
+```console
+root@host# bfs /etc -name passwd -exec cat {} \;
+```
+
+should print `/etc/passwd` but not `/etc/shadow`.
+`bfs` tries to ensure correct behavior through careful programming practice, an extensive testsuite, and static analysis.
+
+`bfs` is written in C, which is a memory unsafe language.
+Bugs that lead to memory corruption are likely to be exploitable due to the nature of C.
+We use [sanitizers](https://github.com/google/sanitizers) to try to detect these bugs.
+Fuzzing has also been applied in the past, and deploying continuous fuzzing is a work in progress.
+
+
+Supported versions
+------------------
+
+`bfs` comes with [no warranty](/LICENSE), and is maintained by [me](https://tavianator.com/) and [other volunteers](https://github.com/tavianator/bfs/graphs/contributors) in our spare time.
+In that sense, there are no *supported* versions.
+However, as long as I maintain `bfs` I will attempt to address any security issues swiftly.
+In general, security fixes will we part of the latest release, though for significant issues I may backport fixes to older release series.
+
+
+Reporting a vulnerability
+-------------------------
+
+If you think you have found a sensitive security issue in `bfs`, you can [report it privately](https://github.com/tavianator/bfs/security/advisories/new).
+Or you can [report it publicly](https://github.com/tavianator/bfs/issues/new); I won't judge you.
diff --git a/docs/bfs.1 b/docs/bfs.1
index 29c0478..cc9504e 100644
--- a/docs/bfs.1
+++ b/docs/bfs.1
@@ -1,4 +1,4 @@
-.TH BFS 1
+.TH BFS 1 2024-06-17 "bfs 3.3.1"
.SH NAME
bfs \- breadth-first search for your files
.SH SYNOPSIS
@@ -45,7 +45,7 @@ For example,
.RE
.fi
.PP
-will print the all the paths that are either .txt files or symbolic links to .txt files.
+will print all the paths that are either .txt files or symbolic links to .txt files.
.B \-and
is implied between two consecutive expressions, so this is equivalent:
.PP
@@ -71,7 +71,7 @@ will also accept
.I \-N
or
.IR +N .
-.IR \-N
+.I \-N
means "less than
.IR N ,"
and
@@ -917,7 +917,7 @@ Finds broken symbolic links.
.TP
.B bfs \-name config \-exclude \-name .git
Finds all files named
-.BR config,
+.BR config ,
skipping every
.B .git
directory.
diff --git a/src/bftw.c b/src/bftw.c
index b3fd62a..f9ef2a1 100644
--- a/src/bftw.c
+++ b/src/bftw.c
@@ -1529,11 +1529,28 @@ static bool bftw_pop_file(struct bftw_state *state) {
return bftw_pop(state, &state->fileq);
}
+/** Add a path component to the path. */
+static void bftw_prepend_path(char *path, size_t nameoff, size_t namelen, const char *name) {
+ if (nameoff > 0) {
+ path[nameoff - 1] = '/';
+ }
+ memcpy(path + nameoff, name, namelen);
+}
+
/** Build the path to the current file. */
static int bftw_build_path(struct bftw_state *state, const char *name) {
const struct bftw_file *file = state->file;
- size_t pathlen = file ? file->nameoff + file->namelen : 0;
+ size_t nameoff, namelen;
+ if (name) {
+ nameoff = file ? bftw_child_nameoff(file) : 0;
+ namelen = strlen(name);
+ } else {
+ nameoff = file->nameoff;
+ namelen = file->namelen;
+ }
+
+ size_t pathlen = nameoff + namelen;
if (dstresize(&state->path, pathlen) != 0) {
state->error = errno;
return -1;
@@ -1546,11 +1563,11 @@ static int bftw_build_path(struct bftw_state *state, const char *name) {
}
// Build the path backwards
+ if (name) {
+ bftw_prepend_path(state->path, nameoff, namelen, name);
+ }
while (file && file != ancestor) {
- if (file->nameoff > 0) {
- state->path[file->nameoff - 1] = '/';
- }
- memcpy(state->path + file->nameoff, file->name, file->namelen);
+ bftw_prepend_path(state->path, file->nameoff, file->namelen, file->name);
if (ancestor && ancestor->depth == file->depth) {
ancestor = ancestor->parent;
@@ -1559,20 +1576,6 @@ static int bftw_build_path(struct bftw_state *state, const char *name) {
}
state->previous = state->file;
-
- if (name) {
- if (pathlen > 0 && state->path[pathlen - 1] != '/') {
- if (dstrapp(&state->path, '/') != 0) {
- state->error = errno;
- return -1;
- }
- }
- if (dstrcat(&state->path, name) != 0) {
- state->error = errno;
- return -1;
- }
- }
-
return 0;
}
diff --git a/src/color.c b/src/color.c
index 81f28bb..701a89c 100644
--- a/src/color.c
+++ b/src/color.c
@@ -583,7 +583,7 @@ static int parse_gnu_ls_colors(struct colors *colors, const char *ls_colors) {
break;
}
- if (dstrncpy(&key, chunk, equals - chunk) != 0) {
+ if (dstrxcpy(&key, chunk, equals - chunk) != 0) {
goto fail;
}
if (unescape(&value, equals + 1, ':', &next) != 0) {
@@ -968,7 +968,7 @@ static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf,
if (path == ftwbuf->path) {
if (ftwbuf->depth == 0) {
at_fd = AT_FDCWD;
- at_path = dstrndup(path, max);
+ at_path = dstrxdup(path, max);
} else {
// The parent must have existed to get here
goto out;
@@ -977,13 +977,13 @@ static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf,
// We're in print_link_target(), so resolve relative to the link's parent directory
at_fd = ftwbuf->at_fd;
if (at_fd == (int)AT_FDCWD && path[0] != '/') {
- at_path = dstrndup(ftwbuf->path, ftwbuf->nameoff);
- if (at_path && dstrncat(&at_path, path, max) != 0) {
+ at_path = dstrxdup(ftwbuf->path, ftwbuf->nameoff);
+ if (at_path && dstrxcat(&at_path, path, max) != 0) {
ret = -1;
goto out_path;
}
} else {
- at_path = dstrndup(path, max);
+ at_path = dstrxdup(path, max);
}
}
@@ -1206,7 +1206,7 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) {
for (const char *i = format; *i; ++i) {
size_t verbatim = strcspn(i, "%$");
- if (dstrncat(&cfile->buffer, i, verbatim) != 0) {
+ if (dstrxcat(&cfile->buffer, i, verbatim) != 0) {
return -1;
}
i += verbatim;
diff --git a/src/exec.c b/src/exec.c
index cd73d6c..2faa731 100644
--- a/src/exec.c
+++ b/src/exec.c
@@ -234,7 +234,7 @@ static char *bfs_exec_format_arg(char *arg, const char *path) {
char *last = arg;
do {
- if (dstrncat(&ret, last, match - last) != 0) {
+ if (dstrxcat(&ret, last, match - last) != 0) {
goto err;
}
if (dstrcat(&ret, path) != 0) {
diff --git a/src/ioq.c b/src/ioq.c
index 4057a5e..6bb1ceb 100644
--- a/src/ioq.c
+++ b/src/ioq.c
@@ -313,9 +313,11 @@ static void ioq_slot_wake(struct ioqq *ioqq, ioq_slot *slot) {
cond_broadcast(&monitor->cond);
}
-/** Branch-free (slot & IOQ_SKIP) ? ~IOQ_BLOCKED : 0 */
-static uintptr_t ioq_skip_mask(uintptr_t slot) {
- return -(slot >> IOQ_SKIP_BIT) << 1;
+/** Branch-free ((slot & IOQ_SKIP) ? skip : full) & ~IOQ_BLOCKED */
+static uintptr_t ioq_slot_blend(uintptr_t slot, uintptr_t skip, uintptr_t full) {
+ uintptr_t mask = -(slot >> IOQ_SKIP_BIT);
+ uintptr_t ret = (skip & mask) | (full & ~mask);
+ return ret & ~IOQ_BLOCKED;
}
/** Push an entry into a slot. */
@@ -323,19 +325,18 @@ static bool ioq_slot_push(struct ioqq *ioqq, ioq_slot *slot, struct ioq_ent *ent
uintptr_t prev = load(slot, relaxed);
while (true) {
- size_t skip_mask = ioq_skip_mask(prev);
- size_t full_mask = ~skip_mask & ~IOQ_BLOCKED;
- if (prev & full_mask) {
+ uintptr_t full = ioq_slot_blend(prev, 0, prev);
+ if (full) {
// full(ptr) → wait
prev = ioq_slot_wait(ioqq, slot, prev);
continue;
}
// empty → full(ptr)
- uintptr_t next = ((uintptr_t)ent >> 1) & full_mask;
+ uintptr_t next = (uintptr_t)ent >> 1;
// skip(1) → empty
// skip(n) → skip(n - 1)
- next |= (prev - IOQ_SKIP_ONE) & skip_mask;
+ next = ioq_slot_blend(prev, prev - IOQ_SKIP_ONE, next);
if (compare_exchange_weak(slot, &prev, next, release, relaxed)) {
break;
@@ -357,9 +358,8 @@ static struct ioq_ent *ioq_slot_pop(struct ioqq *ioqq, ioq_slot *slot, bool bloc
// skip(n) → skip(n + 1)
// full(ptr) → full(ptr - 1)
uintptr_t next = prev + IOQ_SKIP_ONE;
- // skip(n) → ~IOQ_BLOCKED
// full(ptr) → 0
- next &= ioq_skip_mask(next);
+ next = ioq_slot_blend(next, next, 0);
if (block && next) {
prev = ioq_slot_wait(ioqq, slot, prev);
@@ -378,7 +378,7 @@ static struct ioq_ent *ioq_slot_pop(struct ioqq *ioqq, ioq_slot *slot, bool bloc
// empty → 0
// skip(n) → 0
// full(ptr) → ptr
- prev &= ioq_skip_mask(~prev);
+ prev = ioq_slot_blend(prev, 0, prev);
return (struct ioq_ent *)(prev << 1);
}
diff --git a/src/parse.c b/src/parse.c
index a626391..86ce72c 100644
--- a/src/parse.c
+++ b/src/parse.c
@@ -2961,11 +2961,12 @@ static struct bfs_expr *parse_version(struct bfs_parser *parser, int arg1, int a
printf("Copyright © Tavian Barnes and the bfs contributors\n");
printf("No rights reserved (https://opensource.org/license/0BSD)\n\n");
- printf("CONFIG := %s\n", bfs_config);
- printf("CPPFLAGS := %s\n", bfs_cppflags);
- printf("CFLAGS := %s\n", bfs_cflags);
- printf("LDFLAGS := %s\n", bfs_ldflags);
- printf("LDLIBS := %s\n", bfs_ldlibs);
+ printf("CONFFLAGS := %s\n", bfs_confflags);
+ printf("CC := %s\n", bfs_cc);
+ printf("CPPFLAGS := %s\n", bfs_cppflags);
+ printf("CFLAGS := %s\n", bfs_cflags);
+ printf("LDFLAGS := %s\n", bfs_ldflags);
+ printf("LDLIBS := %s\n", bfs_ldlibs);
printf("\n%s\n", BFS_HOMEPAGE);
diff --git a/src/prelude.h b/src/prelude.h
index faa84ec..34ce803 100644
--- a/src/prelude.h
+++ b/src/prelude.h
@@ -40,7 +40,8 @@
// when the version number changes
extern const char bfs_version[];
-extern const char bfs_config[];
+extern const char bfs_confflags[];
+extern const char bfs_cc[];
extern const char bfs_cppflags[];
extern const char bfs_cflags[];
extern const char bfs_ldflags[];
diff --git a/src/version.c b/src/version.c
index e2d4c87..8383350 100644
--- a/src/version.c
+++ b/src/version.c
@@ -7,8 +7,12 @@ const char bfs_version[] = {
#include "version.i"
};
-const char bfs_config[] = {
-#include "config.i"
+const char bfs_confflags[] = {
+#include "confflags.i"
+};
+
+const char bfs_cc[] = {
+#include "cc.i"
};
const char bfs_cppflags[] = {