summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--build/flags.mk9
-rwxr-xr-xconfigure6
-rw-r--r--src/bfs.h5
-rw-r--r--src/color.c189
-rw-r--r--src/diag.h4
-rw-r--r--src/prelude.h3
-rw-r--r--tests/bfs/color_bsd.out54
-rw-r--r--tests/bfs/color_bsd.sh2
-rw-r--r--tests/bfs/color_bsd_fail.sh2
9 files changed, 180 insertions, 94 deletions
diff --git a/build/flags.mk b/build/flags.mk
index 3748a8a..d6f9499 100644
--- a/build/flags.mk
+++ b/build/flags.mk
@@ -26,6 +26,7 @@ _ASAN := ${TRUTHY,${ASAN}}
_LSAN := ${TRUTHY,${LSAN}}
_MSAN := ${TRUTHY,${MSAN}}
_TSAN := ${TRUTHY,${TSAN}}
+_TYSAN := ${TRUTHY,${TYSAN}}
_UBSAN := ${TRUTHY,${UBSAN}}
_GCOV := ${TRUTHY,${GCOV}}
_LINT := ${TRUTHY,${LINT}}
@@ -38,21 +39,23 @@ ASAN_CFLAGS,y := -fsanitize=address
LSAN_CFLAGS,y := -fsanitize=leak
MSAN_CFLAGS,y := -fsanitize=memory -fsanitize-memory-track-origins
TSAN_CFLAGS,y := -fsanitize=thread
+TYSAN_CFLAGS,y := -fsanitize=type
UBSAN_CFLAGS,y := -fsanitize=undefined
_CFLAGS += ${ASAN_CFLAGS,${_ASAN}}
_CFLAGS += ${LSAN_CFLAGS,${_LSAN}}
_CFLAGS += ${MSAN_CFLAGS,${_MSAN}}
_CFLAGS += ${TSAN_CFLAGS,${_TSAN}}
+_CFLAGS += ${TYSAN_CFLAGS,${_TYSAN}}
_CFLAGS += ${UBSAN_CFLAGS,${_UBSAN}}
SAN_CFLAGS,y := -fno-sanitize-recover=all
-INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_UBSAN}}
+INSANE := ${NOT,${_ASAN}${_LSAN}${_MSAN}${_TSAN}${_TYSAN}${_UBSAN}}
SAN := ${NOT,${INSANE}}
_CFLAGS += ${SAN_CFLAGS,${SAN}}
-# MSAN and TSAN both need all code to be instrumented
-YESLIBS := ${NOT,${_MSAN}${_TSAN}}
+# MSan, TSan, and TySan need all code to be instrumented
+YESLIBS := ${NOT,${_MSAN}${_TSAN}${_TYSAN}}
NOLIBS ?= ${NOT,${YESLIBS}}
# gcov only intercepts fork()/exec() with -std=gnu*
diff --git a/configure b/configure
index 7f0bd04..51b543e 100755
--- a/configure
+++ b/configure
@@ -40,7 +40,7 @@ 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-{asan,lsan,msan,tsan,tysan,ubsan}
Enable sanitizers
--enable-gcov
Enable code coverage instrumentation
@@ -164,7 +164,7 @@ for arg; do
--enable-*|--disable-*)
case "$name" in
- release|lto|asan|lsan|msan|tsan|ubsan|lint|gcov)
+ release|lto|asan|lsan|msan|tsan|tysan|ubsan|lint|gcov)
set -- "$@" "$NAME=$yn"
;;
*)
@@ -197,7 +197,7 @@ for arg; do
;;
# Warn about MAKE variables that have documented configure flags
- RELEASE=*|LTO=*|ASAN=*|LSAN=*|MSAN=*|TSAN=*|UBSAN=*|LINT=*|GCOV=*)
+ RELEASE=*|LTO=*|ASAN=*|LSAN=*|MSAN=*|TSAN=*|TYSAN=*|UBSAN=*|LINT=*|GCOV=*)
name=$(printf '%s' "$NAME" | tr 'A-Z_' 'a-z-')
warn '"%s" is deprecated; use --enable-%s' "$arg" "$name"
set -- "$@" "$arg"
diff --git a/src/bfs.h b/src/bfs.h
index 3cee727..70a7282 100644
--- a/src/bfs.h
+++ b/src/bfs.h
@@ -202,7 +202,10 @@ extern const char bfs_ldlibs[];
* Disabled on TSan due to https://github.com/google/sanitizers/issues/342.
*/
#ifndef BFS_USE_TARGET_CLONES
-# if __has_attribute(target_clones) && (__GLIBC__ || __FreeBSD__) && !__SANITIZE_THREAD__
+# if __has_attribute(target_clones) \
+ && (__GLIBC__ || __FreeBSD__) \
+ && !__SANITIZE_THREAD__ \
+ && !__SANITIZE_TYPE__
# define BFS_USE_TARGET_CLONES true
# else
# define BFS_USE_TARGET_CLONES false
diff --git a/src/color.c b/src/color.c
index f77877d..926cf2b 100644
--- a/src/color.c
+++ b/src/color.c
@@ -103,6 +103,8 @@ struct colors {
struct esc_seq *pipe;
struct esc_seq *socket;
+ struct esc_seq *dataless;
+
/** A mapping from color names (fi, di, ln, etc.) to struct fields. */
struct trie names;
@@ -161,26 +163,32 @@ static int cat_esc(dchar **dstr, const struct esc_seq *seq) {
return dstrxcat(dstr, seq->seq, seq->len);
}
-/** Set a named escape sequence. */
-static int set_esc(struct colors *colors, const char *name, dchar *value) {
- struct esc_seq **field = get_esc(colors, name);
- if (!field) {
- return 0;
+/** Set an escape sequence field. */
+static int set_esc_field(struct colors *colors, struct esc_seq **field, const dchar *value) {
+ struct esc_seq *seq = NULL;
+ if (value) {
+ seq = new_esc(colors, value, dstrlen(value));
+ if (!seq) {
+ return -1;
+ }
}
if (*field) {
free_esc(colors, *field);
- *field = NULL;
}
+ *field = seq;
- if (value) {
- *field = new_esc(colors, value, dstrlen(value));
- if (!*field) {
- return -1;
- }
+ return 0;
+}
+
+/** Set a named escape sequence. */
+static int set_esc(struct colors *colors, const char *name, const dchar *value) {
+ struct esc_seq **field = get_esc(colors, name);
+ if (!field) {
+ return 0;
}
- return 0;
+ return set_esc_field(colors, field, value);
}
/** Reverse a string, to turn suffix matches into prefix matches. */
@@ -609,59 +617,101 @@ fail:
/** Parse the FreeBSD $LSCOLORS format. */
static int parse_bsd_ls_colors(struct colors *colors, const char *lscolors) {
- static const char *keys[] = {
- "di", "ln", "so", "pi", "ex", "bd", "cd", "su", "sg", "tw", "ow"
- };
-
static const char *fg_codes[256] = {
- ['a'] = "30", ['b'] = "31", ['c'] = "32", ['d'] = "33",
- ['e'] = "34", ['f'] = "35", ['g'] = "36", ['h'] = "37", ['x'] = "39",
+ // 0-7: deprecated aliases for a-h
+ ['0'] = "30", ['1'] = "31", ['2'] = "32", ['3'] = "33",
+ ['4'] = "34", ['5'] = "35", ['6'] = "36", ['7'] = "37",
+ // a-h: first 8 ANSI foreground colors
+ ['a'] = "30", ['b'] = "31", ['c'] = "32", ['d'] = "33",
+ ['e'] = "34", ['f'] = "35", ['g'] = "36", ['h'] = "37",
+ // x: default foreground
+ ['x'] = "39",
+ // A-H: bold foreground colors
['A'] = "1;30", ['B'] = "1;31", ['C'] = "1;32", ['D'] = "1;33",
- ['E'] = "1;34", ['F'] = "1;35", ['G'] = "1;36", ['H'] = "1;37", ['X'] = "1"
+ ['E'] = "1;34", ['F'] = "1;35", ['G'] = "1;36", ['H'] = "1;37",
+ // X: bold default foreground
+ ['X'] = "1;39",
};
static const char *bg_codes[256] = {
- ['a'] = "40", ['b'] = "41", ['c'] = "42", ['d'] = "43",
- ['e'] = "44", ['f'] = "45", ['g'] = "46", ['h'] = "47", ['x'] = "49",
- ['A'] = "4;100", ['B'] = "4;101", ['C'] = "4;102", ['D'] = "4;103",
- ['E'] = "4;104", ['F'] = "4;105", ['G'] = "4;106", ['H'] = "4;107", ['X'] = "4;49"
+ // 0-7: deprecated aliases for a-h
+ ['0'] = "40", ['1'] = "41", ['2'] = "42", ['3'] = "43",
+ ['4'] = "44", ['5'] = "45", ['6'] = "46", ['7'] = "47",
+ // a-h: first 8 ANSI background colors
+ ['a'] = "40", ['b'] = "41", ['c'] = "42", ['d'] = "43",
+ ['e'] = "44", ['f'] = "45", ['g'] = "46", ['h'] = "47",
+ // x: default background
+ ['x'] = "49",
+ // A-H: background colors + underline
+ ['A'] = "4;40", ['B'] = "4;41", ['C'] = "4;42", ['D'] = "4;43",
+ ['E'] = "4;44", ['F'] = "4;45", ['G'] = "4;46", ['H'] = "4;47",
+ // X: default background + underline
+ ['X'] = "4;49",
};
// Please refer to https://man.freebsd.org/cgi/man.cgi?ls(1)#ENVIRONMENT
- char complete_colors[] = "exfxcxdxbxegedabagacad";
+ char complete_colors[] = "exfxcxdxbxegedabagacadah";
+ // For short $LSCOLORS, use the default colors for the rest
size_t max = strlen(complete_colors);
- size_t len = strnlen(lscolors, max + 1);
- if (len == 0 || len % 2 != 0 || len > max) {
- errno = EINVAL;
+ size_t len = strnlen(lscolors, max);
+ memcpy(complete_colors, lscolors, len);
+
+ struct esc_seq **keys[] = {
+ &colors->directory,
+ &colors->link,
+ &colors->socket,
+ &colors->pipe,
+ &colors->executable,
+ &colors->blockdev,
+ &colors->chardev,
+ &colors->setuid,
+ &colors->setgid,
+ &colors->sticky_other_writable,
+ &colors->other_writable,
+ &colors->dataless,
+ };
+
+ dchar *buf = dstralloc(8);
+ if (!buf) {
return -1;
}
- memcpy(complete_colors, lscolors, len);
+ int ret = -1;
for (size_t i = 0; i < countof(keys); ++i) {
- uint8_t fg = complete_colors[i * 2];
- uint8_t bg = complete_colors[(i * 2) + 1];
+ uint8_t fg = complete_colors[2 * i];
+ uint8_t bg = complete_colors[2 * i + 1];
const char *fg_code = fg_codes[fg];
const char *bg_code = bg_codes[bg];
- if (!fg_code || !bg_code) {
- continue;
+ dstrshrink(buf, 0);
+ if (fg_code) {
+ if (dstrcat(&buf, fg_code) != 0) {
+ goto fail;
+ }
}
-
- dchar *esc = dstrprintf("%s;%s", fg_code, bg_code);
- if (!esc) {
- return -1;
+ if (fg_code && bg_code) {
+ if (dstrcat(&buf, ";") != 0) {
+ goto fail;
+ }
+ }
+ if (bg_code) {
+ if (dstrcat(&buf, bg_code) != 0) {
+ goto fail;
+ }
}
- int ret = set_esc(colors, keys[i], esc);
- dstrfree(esc);
- if (ret != 0) {
- return -1;
+ const dchar *value = dstrlen(buf) > 0 ? buf : NULL;
+ if (set_esc_field(colors, keys[i], value) != 0) {
+ goto fail;
}
}
- return 0;
+ ret = 0;
+fail:
+ dstrfree(buf);
+ return ret;
}
static bool str_isset(const char *str) {
@@ -733,6 +783,8 @@ struct colors *parse_colors(void) {
fail = fail || init_esc(colors, "pi", "33", &colors->pipe);
fail = fail || init_esc(colors, "so", "01;35", &colors->socket);
+ colors->dataless = NULL;
+
if (fail) {
goto fail;
}
@@ -1020,6 +1072,34 @@ static bool cpath_is_broken(const struct cpath *cpath) {
}
}
+/** Check if we need a statbuf to colorize a file. */
+static bool must_stat(const struct colors *colors, enum bfs_type type) {
+ switch (type) {
+ case BFS_REG:
+ if (colors->setuid || colors->setgid || colors->executable || colors->multi_hard) {
+ return true;
+ }
+
+#ifdef ST_DATALESS
+ if (colors->dataless) {
+ return true;
+ }
+#endif
+
+ return false;
+
+ case BFS_DIR:
+ if (colors->sticky_other_writable || colors->other_writable || colors->sticky) {
+ return true;
+ }
+
+ return false;
+
+ default:
+ return false;
+ }
+}
+
/** Get the color for a file. */
static const struct esc_seq *file_color(const struct colors *colors, const struct cpath *cpath) {
enum bfs_type type;
@@ -1034,17 +1114,17 @@ static const struct esc_seq *file_color(const struct colors *colors, const struc
}
const struct bfs_stat *statbuf = NULL;
+ if (must_stat(colors, type)) {
+ statbuf = cpath_stat(cpath);
+ if (!statbuf) {
+ goto error;
+ }
+ }
+
const struct esc_seq *color = NULL;
switch (type) {
case BFS_REG:
- if (colors->setuid || colors->setgid || colors->executable || colors->multi_hard) {
- statbuf = cpath_stat(cpath);
- if (!statbuf) {
- goto error;
- }
- }
-
if (colors->setuid && (statbuf->mode & 04000)) {
color = colors->setuid;
} else if (colors->setgid && (statbuf->mode & 02000)) {
@@ -1057,6 +1137,12 @@ static const struct esc_seq *file_color(const struct colors *colors, const struc
color = colors->multi_hard;
}
+#ifdef SF_DATALESS
+ if (!color && colors->dataless && (statbuf->attrs & SF_DATALESS)) {
+ color = colors->dataless;
+ }
+#endif
+
if (!color) {
const char *name = cpath->path + cpath->nameoff;
size_t namelen = cpath->valid - cpath->nameoff;
@@ -1070,13 +1156,6 @@ static const struct esc_seq *file_color(const struct colors *colors, const struc
break;
case BFS_DIR:
- if (colors->sticky_other_writable || colors->other_writable || colors->sticky) {
- statbuf = cpath_stat(cpath);
- if (!statbuf) {
- goto error;
- }
- }
-
if (colors->sticky_other_writable && (statbuf->mode & 01002) == 01002) {
color = colors->sticky_other_writable;
} else if (colors->other_writable && (statbuf->mode & 00002)) {
diff --git a/src/diag.h b/src/diag.h
index 645dbb1..0a4d3b9 100644
--- a/src/diag.h
+++ b/src/diag.h
@@ -18,6 +18,7 @@
*
* bfs: func@src/file.c:0: Message
*/
+// Use (format) ? "..." : "" so the format string is required
#define BFS_DIAG_FORMAT_(format) \
((format) ? "%s: %s@%s:%d: " format "%s" : "")
@@ -75,7 +76,7 @@ void bfs_abortf(const char *format, ...);
bfs_eabort_(__VA_ARGS__, )
#define bfs_eabort_(format, ...) \
- ((format) ? bfs_abort_(format ": %s", __VA_ARGS__ errstr(), ) : (void)0)
+ bfs_abort_(format "%s%s", __VA_ARGS__ (sizeof("" format) > 1 ? ": " : ""), errstr(), )
/**
* Abort in debug builds; no-op in release builds.
@@ -116,7 +117,6 @@ void bfs_abortf(const char *format, ...);
#define bfs_everify(...) \
bfs_everify_(#__VA_ARGS__, __VA_ARGS__, "", )
-
#define bfs_everify_(str, cond, format, ...) \
((cond) ? (void)0 : bfs_everify__(format, BFS_DIAG_MSG_(format, str), __VA_ARGS__))
diff --git a/src/prelude.h b/src/prelude.h
index de89a6c..3b1c4e5 100644
--- a/src/prelude.h
+++ b/src/prelude.h
@@ -126,5 +126,8 @@
#if __has_feature(thread_sanitizer) && !defined(__SANITIZE_THREAD__)
# define __SANITIZE_THREAD__ true
#endif
+#if __has_feature(type_sanitizer) && !defined(__SANITIZE_TYPE__)
+# define __SANITIZE_TYPE__ true
+#endif
#endif // BFS_PRELUDE_H
diff --git a/tests/bfs/color_bsd.out b/tests/bfs/color_bsd.out
index f7c577c..2ad656f 100644
--- a/tests/bfs/color_bsd.out
+++ b/tests/bfs/color_bsd.out
@@ -1,27 +1,27 @@
-$'rainbow/\e[1m'
-$'rainbow/\e[1m/'$'\e[0m'
-rainbow
-rainbow/sugid
-rainbow/suid
-rainbow/sticky_ow
-rainbow/ow
-rainbow/sgid
-rainbow/exec.sh
-rainbow/socket
-rainbow/pipe
-rainbow/broken
-rainbow/chardev_link
-rainbow/link.txt
-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
+$'rainbow/\e[1m'
+$'rainbow/\e[1m/'$'\e[0m'
+rainbow
+rainbow/pipe
+rainbow/broken
+rainbow/chardev_link
+rainbow/link.txt
+rainbow/sugid
+rainbow/suid
+rainbow/sticky_ow
+rainbow/ow
+rainbow/sgid
+rainbow/exec.sh
+rainbow/socket
+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_bsd.sh b/tests/bfs/color_bsd.sh
index f8a777f..2e99f0b 100644
--- a/tests/bfs/color_bsd.sh
+++ b/tests/bfs/color_bsd.sh
@@ -1 +1 @@
-LSCOLORS="eB" bfs_diff rainbow -color
+LSCOLORS="exFxcXDXbxeGxdXb" bfs_diff rainbow -color
diff --git a/tests/bfs/color_bsd_fail.sh b/tests/bfs/color_bsd_fail.sh
deleted file mode 100644
index 94e1209..0000000
--- a/tests/bfs/color_bsd_fail.sh
+++ /dev/null
@@ -1,2 +0,0 @@
-# LSCOLORS can be at most 22 characters long (11 color pairs); this one has 24.
-! LSCOLORS="exfxcxdxbxegedabagacadeB" invoke_bfs rainbow -color