summaryrefslogtreecommitdiffstats
path: root/src/color.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/color.c')
-rw-r--r--src/color.c212
1 files changed, 181 insertions, 31 deletions
diff --git a/src/color.c b/src/color.c
index a026831..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. */
@@ -607,6 +615,109 @@ fail:
return ret;
}
+/** Parse the FreeBSD $LSCOLORS format. */
+static int parse_bsd_ls_colors(struct colors *colors, const char *lscolors) {
+ static const char *fg_codes[256] = {
+ // 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: bold default foreground
+ ['X'] = "1;39",
+ };
+
+ static const char *bg_codes[256] = {
+ // 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[] = "exfxcxdxbxegedabagacadah";
+
+ // For short $LSCOLORS, use the default colors for the rest
+ size_t max = strlen(complete_colors);
+ 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;
+ }
+
+ int ret = -1;
+ for (size_t i = 0; i < countof(keys); ++i) {
+ 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];
+
+ dstrshrink(buf, 0);
+ if (fg_code) {
+ if (dstrcat(&buf, fg_code) != 0) {
+ goto fail;
+ }
+ }
+ if (fg_code && bg_code) {
+ if (dstrcat(&buf, ";") != 0) {
+ goto fail;
+ }
+ }
+ if (bg_code) {
+ if (dstrcat(&buf, bg_code) != 0) {
+ goto fail;
+ }
+ }
+
+ const dchar *value = dstrlen(buf) > 0 ? buf : NULL;
+ if (set_esc_field(colors, keys[i], value) != 0) {
+ goto fail;
+ }
+ }
+
+ ret = 0;
+fail:
+ dstrfree(buf);
+ return ret;
+}
+
+static bool str_isset(const char *str) {
+ return str && *str;
+}
+
struct colors *parse_colors(void) {
struct colors *colors = ALLOC(struct colors);
if (!colors) {
@@ -672,16 +783,28 @@ 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;
}
- 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;
}
@@ -949,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;
@@ -963,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)) {
@@ -986,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;
@@ -999,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)) {