diff options
author | VinX <vinx83@gmail.com> | 2025-07-03 12:31:19 +0000 |
---|---|---|
committer | VinX <vinx83@gmail.com> | 2025-07-03 12:31:19 +0000 |
commit | b4a8afc2cfdbd91dedf2f387427c0984cad6d54a (patch) | |
tree | 5f16fa741463837e53316d0db6f8776152637dfc | |
parent | 42235435aab09bc48765707b6706eaa1ecf1a0a7 (diff) | |
download | bfs-b4a8afc2cfdbd91dedf2f387427c0984cad6d54a.tar.xz |
Support for FreeBSD-style ls Coloring
If LSCOLORS is undefined or partially defined (missing some of the eleven expected
value pairs), the patch will automatically fill in those missing values with default
colors. However, if the LS_COLORS environment variable is already defined, the
application prefer the LS_COLORS approach, maintaining current working flow of the
program.
-rw-r--r-- | src/color.c | 81 | ||||
-rw-r--r-- | tests/bfs/color_bsd.out | 27 | ||||
-rw-r--r-- | tests/bfs/color_bsd.sh | 1 | ||||
-rw-r--r-- | tests/bfs/color_bsd_fail.sh | 2 | ||||
-rw-r--r-- | tests/util.sh | 1 |
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 @@ +[34;4;101m$'rainbow/\e[1m'[0m +[34;4;101m$'rainbow/\e[1m/'[0m$'\e[0m' +[34;4;101mrainbow[0m +[34;4;101mrainbow/[0m[30;41msugid[0m +[34;4;101mrainbow/[0m[30;41msuid[0m +[34;4;101mrainbow/[0m[30;42msticky_ow[0m +[34;4;101mrainbow/[0m[30;43mow[0m +[34;4;101mrainbow/[0m[30;46msgid[0m +[34;4;101mrainbow/[0m[31;49mexec.sh[0m +[34;4;101mrainbow/[0m[32;49msocket[0m +[34;4;101mrainbow/[0m[33;49mpipe[0m +[34;4;101mrainbow/[0m[35;49mbroken[0m +[34;4;101mrainbow/[0m[35;49mchardev_link[0m +[34;4;101mrainbow/[0m[35;49mlink.txt[0m +[34;4;101mrainbow/[0m[37;44msticky[0m +[34;4;101mrainbow/[0mfile.dat +[34;4;101mrainbow/[0mfile.txt +[34;4;101mrainbow/[0mlower.gz +[34;4;101mrainbow/[0mlower.tar +[34;4;101mrainbow/[0mlower.tar.gz +[34;4;101mrainbow/[0mlu.tar.GZ +[34;4;101mrainbow/[0mmh1 +[34;4;101mrainbow/[0mmh2 +[34;4;101mrainbow/[0mul.TAR.gz +[34;4;101mrainbow/[0mupper.GZ +[34;4;101mrainbow/[0mupper.TAR +[34;4;101mrainbow/[0mupper.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 |