diff options
Diffstat (limited to 'src/color.c')
-rw-r--r-- | src/color.c | 649 |
1 files changed, 467 insertions, 182 deletions
diff --git a/src/color.c b/src/color.c index 7f653b0..926cf2b 100644 --- a/src/color.c +++ b/src/color.c @@ -31,8 +31,8 @@ struct esc_seq { /** The length of the escape sequence. */ size_t len; - /** The escape sequence iteself, without a terminating NUL. */ - char seq[]; + /** The escape sequence itself, without a terminating NUL. */ + char seq[] _counted_by(len); }; /** @@ -48,7 +48,7 @@ struct ext_color { /** Whether the comparison should be case-sensitive. */ bool case_sensitive; /** The extension to match (NUL-terminated). */ - char ext[]; + char ext[]; // _counted_by(len + 1); }; struct colors { @@ -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; @@ -143,13 +145,7 @@ static int init_esc(struct colors *colors, const char *name, const char *value, *field = esc; - struct trie_leaf *leaf = trie_insert_str(&colors->names, name); - if (!leaf) { - return -1; - } - - leaf->value = field; - return 0; + return trie_set_str(&colors->names, name, field); } /** Check if an escape sequence is equal to a string. */ @@ -159,8 +155,7 @@ static bool esc_eq(const struct esc_seq *esc, const char *str, size_t len) { /** Get an escape sequence from the table. */ static struct esc_seq **get_esc(const struct colors *colors, const char *name) { - const struct trie_leaf *leaf = trie_find_str(&colors->names, name); - return leaf ? leaf->value : NULL; + return trie_get_str(&colors->names, name); } /** Append an escape sequence to a string. */ @@ -168,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. */ @@ -225,18 +226,22 @@ static int insert_ext(struct trie *trie, struct ext_color *ext) { } size_t len = ext->len + 1; - leaf = trie_insert_mem(trie, ext->ext, len); - if (!leaf) { - return -1; - } - - leaf->value = ext; - return 0; + return trie_set_mem(trie, ext->ext, len, ext); } /** Set the color for an extension. */ static int set_ext(struct colors *colors, dchar *key, dchar *value) { size_t len = dstrlen(key); + + // Embedded NUL bytes in extensions can lead to a non-prefix-free + // set of strings, e.g. {".gz", "\0.gz"} would be transformed to + // {"zg.\0", "zg.\0\0"} (showing the implicit terminating NUL). + // Our trie implementation only supports prefix-free key sets, but + // luckily '\0' cannot appear in filenames so we can ignore them. + if (memchr(key, '\0', len)) { + return 0; + } + struct ext_color *ext = varena_alloc(&colors->ext_arena, len + 1); if (!ext) { return -1; @@ -277,9 +282,9 @@ fail: /** * The "smart case" algorithm. * - * @param ext + * @ext * The current extension being added. - * @param iext + * @iext * The previous case-insensitive match, if any, for the same extension. * @return * Whether this extension should become case-sensitive. @@ -359,9 +364,8 @@ static int build_iext_trie(struct colors *colors) { /** * Find a color by an extension. */ -static const struct esc_seq *get_ext(const struct colors *colors, const char *filename) { +static const struct esc_seq *get_ext(const struct colors *colors, const char *filename, size_t name_len) { size_t ext_len = colors->ext_len; - size_t name_len = strlen(filename); if (name_len < ext_len) { ext_len = name_len; } @@ -370,7 +374,8 @@ static const struct esc_seq *get_ext(const struct colors *colors, const char *fi char buf[256]; char *copy; if (ext_len < sizeof(buf)) { - copy = memcpy(buf, suffix, ext_len + 1); + copy = memcpy(buf, suffix, ext_len); + copy[ext_len] = '\0'; } else { copy = strndup(suffix, ext_len); if (!copy) { @@ -418,13 +423,13 @@ static const struct esc_seq *get_ext(const struct colors *colors, const char *fi * * See man dir_colors. * - * @param str + * @str * A dstring to fill with the unescaped chunk. - * @param value + * @value * The value to parse. - * @param end + * @end * The character that marks the end of the chunk. - * @param[out] next + * @next[out] * Will be set to the next chunk. * @return * 0 on success, -1 on failure. @@ -610,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) { @@ -675,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; } @@ -771,44 +891,245 @@ int cfclose(CFILE *cfile) { return ret; } +bool colors_need_stat(const struct colors *colors) { + return colors->setuid || colors->setgid || colors->executable || colors->multi_hard + || colors->sticky_other_writable || colors->other_writable || colors->sticky; +} + +/** A colorable file path. */ +struct cpath { + /** The full path to color. */ + const char *path; + /** The basename offset of the last valid component. */ + size_t nameoff; + /** The end offset of the last valid component. */ + size_t valid; + /** The total length of the path. */ + size_t len; + + /** The bftw() buffer. */ + const struct BFTW *ftwbuf; + /** bfs_stat() flags for the final component. */ + enum bfs_stat_flags flags; + /** A bfs_stat() buffer, filled in when 0 < valid < len. */ + struct bfs_stat statbuf; +}; + +/** Move the valid range of a path backwards. */ +static void cpath_retreat(struct cpath *cpath) { + const char *path = cpath->path; + size_t nameoff = cpath->nameoff; + size_t valid = cpath->valid; + + if (valid > 0 && path[valid - 1] == '/') { + // Try without trailing slashes, to distinguish "notdir/" from "notdir" + do { + --valid; + } while (valid > 0 && path[valid - 1] == '/'); + + nameoff = valid; + while (nameoff > 0 && path[nameoff - 1] != '/') { + --nameoff; + } + } else { + // Remove the last component and try again + valid = nameoff; + } + + cpath->nameoff = nameoff; + cpath->valid = valid; +} + +/** Initialize a struct cpath. */ +static int cpath_init(struct cpath *cpath, const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { + // Normally there are only two components to color: + // + // nameoff valid + // v v + // path/to/filename + // --------+------- + // ${di} ${fi} + // + // Error cases also usually have two components: + // + // valid, + // nameoff + // v + // path/to/nowhere + // --------+------ + // ${di} ${mi} + // + // But with ENOTDIR, there may be three: + // + // nameoff valid + // v v + // path/to/filename/nowhere + // --------+-------+------- + // ${di} ${fi} ${mi} + + cpath->path = path; + cpath->len = strlen(path); + cpath->ftwbuf = ftwbuf; + cpath->flags = flags; + + cpath->valid = cpath->len; + if (path == ftwbuf->path) { + cpath->nameoff = ftwbuf->nameoff; + } else { + cpath->nameoff = xbaseoff(path); + } + + if (bftw_type(ftwbuf, flags) != BFS_ERROR) { + return 0; + } + + cpath_retreat(cpath); + + // Find the base path. For symlinks like + // + // path/to/symlink -> nested/file + // + // this will be something like + // + // path/to/nested/file + int at_fd = AT_FDCWD; + dchar *at_path = NULL; + if (path == ftwbuf->path) { + if (ftwbuf->depth > 0) { + // The parent must have existed to get here + return 0; + } + } else { + // 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 = dstrxdup(ftwbuf->path, ftwbuf->nameoff); + if (!at_path) { + return -1; + } + } + } + + if (!at_path) { + at_path = dstralloc(cpath->valid); + if (!at_path) { + return -1; + } + } + if (dstrxcat(&at_path, path, cpath->valid) != 0) { + dstrfree(at_path); + return -1; + } + + size_t at_off = dstrlen(at_path) - cpath->valid; + + // Find the longest valid path prefix + while (cpath->valid > 0) { + if (bfs_stat(at_fd, at_path, BFS_STAT_FOLLOW, &cpath->statbuf) == 0) { + break; + } + + cpath_retreat(cpath); + dstrshrink(at_path, at_off + cpath->valid); + } + + dstrfree(at_path); + return 0; +} + +/** Get the bfs_stat() buffer for the last valid component. */ +static const struct bfs_stat *cpath_stat(const struct cpath *cpath) { + if (cpath->valid == cpath->len) { + return bftw_stat(cpath->ftwbuf, cpath->flags); + } else { + return &cpath->statbuf; + } +} + +/** Check if a path has non-trivial capabilities. */ +static bool cpath_has_capabilities(const struct cpath *cpath) { + if (cpath->valid == cpath->len) { + return bfs_check_capabilities(cpath->ftwbuf) > 0; + } else { + // TODO: implement capability checks for arbitrary paths + return false; + } +} + /** Check if a symlink is broken. */ -static bool is_link_broken(const struct BFTW *ftwbuf) { +static bool cpath_is_broken(const struct cpath *cpath) { + if (cpath->valid < cpath->len) { + // A valid parent can't be a broken link + return false; + } + + const struct BFTW *ftwbuf = cpath->ftwbuf; if (ftwbuf->stat_flags & BFS_STAT_NOFOLLOW) { return xfaccessat(ftwbuf->at_fd, ftwbuf->at_path, F_OK) != 0; } else { + // A link encountered with BFS_STAT_TRYFOLLOW must be broken return true; } } -bool colors_need_stat(const struct colors *colors) { - return colors->setuid || colors->setgid || colors->executable || colors->multi_hard - || colors->sticky_other_writable || colors->other_writable || colors->sticky; +/** 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 char *filename, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { - enum bfs_type type = bftw_type(ftwbuf, flags); +static const struct esc_seq *file_color(const struct colors *colors, const struct cpath *cpath) { + enum bfs_type type; + if (cpath->valid == cpath->len) { + type = bftw_type(cpath->ftwbuf, cpath->flags); + } else { + type = bfs_mode_to_type(cpath->statbuf.mode); + } + if (type == BFS_ERROR) { goto error; } 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 = bftw_stat(ftwbuf, flags); - if (!statbuf) { - goto error; - } - } - if (colors->setuid && (statbuf->mode & 04000)) { color = colors->setuid; } else if (colors->setgid && (statbuf->mode & 02000)) { color = colors->setgid; - } else if (colors->capable && bfs_check_capabilities(ftwbuf) > 0) { + } else if (colors->capable && cpath_has_capabilities(cpath)) { color = colors->capable; } else if (colors->executable && (statbuf->mode & 00111)) { color = colors->executable; @@ -816,8 +1137,16 @@ static const struct esc_seq *file_color(const struct colors *colors, const char color = colors->multi_hard; } +#ifdef SF_DATALESS + if (!color && colors->dataless && (statbuf->attrs & SF_DATALESS)) { + color = colors->dataless; + } +#endif + if (!color) { - color = get_ext(colors, filename); + const char *name = cpath->path + cpath->nameoff; + size_t namelen = cpath->valid - cpath->nameoff; + color = get_ext(colors, name, namelen); } if (!color) { @@ -827,13 +1156,6 @@ static const struct esc_seq *file_color(const struct colors *colors, const char break; case BFS_DIR: - if (colors->sticky_other_writable || colors->other_writable || colors->sticky) { - statbuf = bftw_stat(ftwbuf, flags); - 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)) { @@ -847,7 +1169,7 @@ static const struct esc_seq *file_color(const struct colors *colors, const char break; case BFS_LNK: - if (colors->orphan && is_link_broken(ftwbuf)) { + if (colors->orphan && cpath_is_broken(cpath)) { color = colors->orphan; } else { color = colors->link; @@ -934,6 +1256,10 @@ static int print_wordesc(CFILE *cfile, const char *str, size_t n, enum wesc_flag /** Print a string with an optional color. */ static int print_colored(CFILE *cfile, const struct esc_seq *esc, const char *str, size_t len) { + if (len == 0) { + return 0; + } + if (print_esc(cfile, esc) != 0) { return -1; } @@ -950,112 +1276,42 @@ static int print_colored(CFILE *cfile, const struct esc_seq *esc, const char *st return 0; } -/** Find the offset of the first broken path component. */ -static ssize_t first_broken_offset(const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags, size_t max) { - ssize_t ret = max; - bfs_assert(ret >= 0); - - if (bftw_type(ftwbuf, flags) != BFS_ERROR) { - goto out; - } - - dchar *at_path; - int at_fd; - if (path == ftwbuf->path) { - if (ftwbuf->depth == 0) { - at_fd = AT_FDCWD; - at_path = dstrxdup(path, max); - } else { - // The parent must have existed to get here - goto out; - } - } else { - // 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 = dstrxdup(ftwbuf->path, ftwbuf->nameoff); - if (at_path && dstrxcat(&at_path, path, max) != 0) { - ret = -1; - goto out_path; - } - } else { - at_path = dstrxdup(path, max); - } - } - - if (!at_path) { - ret = -1; - goto out; - } - - while (ret > 0) { - if (xfaccessat(at_fd, at_path, F_OK) == 0) { - break; - } - - size_t len = dstrlen(at_path); - while (ret && at_path[len - 1] == '/') { - --len, --ret; - } - if (errno != ENOTDIR) { - while (ret && at_path[len - 1] != '/') { - --len, --ret; - } - } - - dstresize(&at_path, len); - } - -out_path: - dstrfree(at_path); -out: - return ret; -} - /** Print a path with colors. */ static int print_path_colored(CFILE *cfile, const char *path, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { - size_t nameoff; - if (path == ftwbuf->path) { - nameoff = ftwbuf->nameoff; - } else { - nameoff = xbaseoff(path); - } - - const char *name = path + nameoff; - size_t pathlen = nameoff + strlen(name); - - ssize_t broken = first_broken_offset(path, ftwbuf, flags, nameoff); - if (broken < 0) { + struct cpath cpath; + if (cpath_init(&cpath, path, ftwbuf, flags) != 0) { return -1; } - size_t split = broken; const struct colors *colors = cfile->colors; const struct esc_seq *dirs_color = colors->directory; - const struct esc_seq *name_color; + const struct esc_seq *name_color = NULL; + const struct esc_seq *err_color = colors->missing; + if (!err_color) { + err_color = colors->orphan; + } - if (split < nameoff) { - name_color = colors->missing; - if (!name_color) { - name_color = colors->orphan; - } - } else { - name_color = file_color(cfile->colors, path + nameoff, ftwbuf, flags); + if (cpath.nameoff < cpath.valid) { + name_color = file_color(colors, &cpath); if (name_color == dirs_color) { - split = pathlen; + cpath.nameoff = cpath.valid; } } - if (split > 0) { - if (print_colored(cfile, dirs_color, path, split) != 0) { - return -1; - } + if (print_colored(cfile, dirs_color, path, cpath.nameoff) != 0) { + return -1; } - if (split < pathlen) { - if (print_colored(cfile, name_color, path + split, pathlen - split) != 0) { - return -1; - } + const char *name = path + cpath.nameoff; + size_t name_len = cpath.valid - cpath.nameoff; + if (print_colored(cfile, name_color, name, name_len) != 0) { + return -1; + } + + const char *tail = path + cpath.valid; + size_t tail_len = cpath.len - cpath.valid; + if (print_colored(cfile, err_color, tail, tail_len) != 0) { + return -1; } return 0; @@ -1063,8 +1319,18 @@ static int print_path_colored(CFILE *cfile, const char *path, const struct BFTW /** Print a file name with colors. */ static int print_name_colored(CFILE *cfile, const char *name, const struct BFTW *ftwbuf, enum bfs_stat_flags flags) { - const struct esc_seq *esc = file_color(cfile->colors, name, ftwbuf, flags); - return print_colored(cfile, esc, name, strlen(name)); + size_t len = strlen(name); + const struct cpath cpath = { + .path = name, + .nameoff = 0, + .valid = len, + .len = len, + .ftwbuf = ftwbuf, + .flags = flags, + }; + + const struct esc_seq *esc = file_color(cfile->colors, &cpath); + return print_colored(cfile, esc, name, cpath.len); } /** Print the name of a file with the appropriate colors. */ @@ -1124,6 +1390,33 @@ static int print_link_target(CFILE *cfile, const struct BFTW *ftwbuf) { _printf(2, 3) static int cbuff(CFILE *cfile, const char *format, ...); +/** Print an expression's name, for diagnostics. */ +static int print_expr_name(CFILE *cfile, const struct bfs_expr *expr) { + switch (expr->kind) { + case BFS_FLAG: + return cbuff(cfile, "${cyn}%pq${rs}", expr->argv[0]); + case BFS_OPERATOR: + return cbuff(cfile, "${red}%pq${rs}", expr->argv[0]); + default: + return cbuff(cfile, "${blu}%pq${rs}", expr->argv[0]); + } +} + +/** Print an expression's args, for diagnostics. */ +static int print_expr_args(CFILE *cfile, const struct bfs_expr *expr) { + if (print_expr_name(cfile, expr) != 0) { + return -1; + } + + for (size_t i = 1; i < expr->argc; ++i) { + if (cbuff(cfile, " ${bld}%pq${rs}", expr->argv[i]) < 0) { + return -1; + } + } + + return 0; +} + /** Dump a parsed expression tree, for debugging. */ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose, int depth) { if (depth >= 2) { @@ -1138,28 +1431,10 @@ static int print_expr(CFILE *cfile, const struct bfs_expr *expr, bool verbose, i return -1; } - int ret; - switch (expr->kind) { - case BFS_FLAG: - ret = cbuff(cfile, "${cyn}%pq${rs}", expr->argv[0]); - break; - case BFS_OPERATOR: - ret = cbuff(cfile, "${red}%pq${rs}", expr->argv[0]); - break; - default: - ret = cbuff(cfile, "${blu}%pq${rs}", expr->argv[0]); - break; - } - if (ret < 0) { + if (print_expr_args(cfile, expr) != 0) { return -1; } - for (size_t i = 1; i < expr->argc; ++i) { - if (cbuff(cfile, " ${bld}%pq${rs}", expr->argv[i]) < 0) { - return -1; - } - } - if (verbose) { double rate = 0.0, time = 0.0; if (expr->evaluations) { @@ -1297,6 +1572,16 @@ static int cvbuff(CFILE *cfile, const char *format, va_list args) { return -1; } break; + case 'x': + if (print_expr_args(cfile, va_arg(args, const struct bfs_expr *)) != 0) { + return -1; + } + break; + case 'X': + if (print_expr_name(cfile, va_arg(args, const struct bfs_expr *)) != 0) { + return -1; + } + break; default: goto invalid; @@ -1389,7 +1674,7 @@ int cvfprintf(CFILE *cfile, const char *format, va_list args) { } } - dstresize(&cfile->buffer, 0); + dstrshrink(cfile->buffer, 0); return ret; } |