summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2025-07-03 08:55:35 -0400
committerGitHub <noreply@github.com>2025-07-03 08:55:35 -0400
commitf1fa639bdfb6e8da91b57f892acf26d438d30fd3 (patch)
tree5f16fa741463837e53316d0db6f8776152637dfc
parent42235435aab09bc48765707b6706eaa1ecf1a0a7 (diff)
parentb4a8afc2cfdbd91dedf2f387427c0984cad6d54a (diff)
downloadbfs-f1fa639bdfb6e8da91b57f892acf26d438d30fd3.tar.xz
Merge pull request #157 from vinxcls/freebsd-coloring
Support for FreeBSD-style ls Coloring
-rw-r--r--src/color.c81
-rw-r--r--tests/bfs/color_bsd.out27
-rw-r--r--tests/bfs/color_bsd.sh1
-rw-r--r--tests/bfs/color_bsd_fail.sh2
-rw-r--r--tests/util.sh1
5 files changed, 107 insertions, 5 deletions
diff --git a/src/color.c b/src/color.c
index a026831..f77877d 100644
--- a/src/color.c
+++ b/src/color.c
@@ -607,6 +607,67 @@ fail:
return ret;
}
+/** 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",
+ ['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"
+ };
+
+ 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"
+ };
+
+ // Please refer to https://man.freebsd.org/cgi/man.cgi?ls(1)#ENVIRONMENT
+ char complete_colors[] = "exfxcxdxbxegedabagacad";
+
+ size_t max = strlen(complete_colors);
+ size_t len = strnlen(lscolors, max + 1);
+ if (len == 0 || len % 2 != 0 || len > max) {
+ errno = EINVAL;
+ return -1;
+ }
+ memcpy(complete_colors, lscolors, len);
+
+ for (size_t i = 0; i < countof(keys); ++i) {
+ uint8_t fg = complete_colors[i * 2];
+ uint8_t bg = complete_colors[(i * 2) + 1];
+
+ const char *fg_code = fg_codes[fg];
+ const char *bg_code = bg_codes[bg];
+
+ if (!fg_code || !bg_code) {
+ continue;
+ }
+
+ dchar *esc = dstrprintf("%s;%s", fg_code, bg_code);
+ if (!esc) {
+ return -1;
+ }
+
+ int ret = set_esc(colors, keys[i], esc);
+ dstrfree(esc);
+ if (ret != 0) {
+ return -1;
+ }
+ }
+
+ return 0;
+}
+
+static bool str_isset(const char *str) {
+ return str && *str;
+}
+
struct colors *parse_colors(void) {
struct colors *colors = ALLOC(struct colors);
if (!colors) {
@@ -676,12 +737,22 @@ struct colors *parse_colors(void) {
goto fail;
}
- if (parse_gnu_ls_colors(colors, getenv("LS_COLORS")) != 0) {
- goto fail;
- }
- if (parse_gnu_ls_colors(colors, getenv("BFS_COLORS")) != 0) {
- goto fail;
+ const char *gnu_colors = getenv("LS_COLORS");
+ const char *bfs_colors = getenv("BFS_COLORS");
+ const char *bsd_colors = getenv("LSCOLORS");
+ if (str_isset(gnu_colors) || str_isset(bfs_colors)) {
+ if (parse_gnu_ls_colors(colors, gnu_colors) != 0) {
+ goto fail;
+ }
+ if (parse_gnu_ls_colors(colors, bfs_colors) != 0) {
+ goto fail;
+ }
+ } else if (str_isset(bsd_colors)) {
+ if (parse_bsd_ls_colors(colors, bsd_colors) != 0) {
+ goto fail;
+ }
}
+
if (build_iext_trie(colors) != 0) {
goto fail;
}
diff --git a/tests/bfs/color_bsd.out b/tests/bfs/color_bsd.out
new file mode 100644
index 0000000..f7c577c
--- /dev/null
+++ b/tests/bfs/color_bsd.out
@@ -0,0 +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
diff --git a/tests/bfs/color_bsd.sh b/tests/bfs/color_bsd.sh
new file mode 100644
index 0000000..f8a777f
--- /dev/null
+++ b/tests/bfs/color_bsd.sh
@@ -0,0 +1 @@
+LSCOLORS="eB" bfs_diff rainbow -color
diff --git a/tests/bfs/color_bsd_fail.sh b/tests/bfs/color_bsd_fail.sh
new file mode 100644
index 0000000..94e1209
--- /dev/null
+++ b/tests/bfs/color_bsd_fail.sh
@@ -0,0 +1,2 @@
+# LSCOLORS can be at most 22 characters long (11 color pairs); this one has 24.
+! LSCOLORS="exfxcxdxbxegedabagacadeB" invoke_bfs rainbow -color
diff --git a/tests/util.sh b/tests/util.sh
index 1718a1a..c998927 100644
--- a/tests/util.sh
+++ b/tests/util.sh
@@ -34,6 +34,7 @@ stdenv() {
export LS_COLORS=""
unset BFS_COLORS
+ unset LSCOLORS
if [ "$UNAME" = Darwin ]; then
# ASan on macOS likes to report