From 21f6c460fcda0161993f75421614efb6971af23b Mon Sep 17 00:00:00 2001 From: Tavian Barnes Date: Sat, 11 Mar 2017 14:06:42 -0500 Subject: Make a printf()-style API for colored messages --- Makefile | 5 +- bfs.h | 8 +- color.c | 302 +++++++++++++++++++++++++++++++++------------------------------ color.h | 67 +++++++------- eval.c | 16 ++-- parse.c | 185 +++++++++++++++++--------------------- printf.c | 39 +++------ printf.h | 6 +- 8 files changed, 306 insertions(+), 322 deletions(-) diff --git a/Makefile b/Makefile index e1d302f..ebd929a 100644 --- a/Makefile +++ b/Makefile @@ -16,7 +16,8 @@ VERSION := $(shell git describe --always) endif CC ?= gcc -CFLAGS ?= -g -Wall +WFLAGS ?= -Wall -Wmissing-declarations +CFLAGS ?= -g $(WFLAGS) LDFLAGS ?= DEPFLAGS ?= -MD -MP -MF $(@:.o=.d) RM ?= rm -f @@ -46,7 +47,7 @@ all: bfs bfs: bftw.o color.o dstring.o eval.o main.o parse.o printf.o typo.o util.o $(CC) $(ALL_LDFLAGS) $^ -o $@ -release: CFLAGS := -O3 -flto -Wall -DNDEBUG +release: CFLAGS := -O3 -flto $(WFLAGS) -DNDEBUG release: bfs %.o: %.c diff --git a/bfs.h b/bfs.h index b589def..708deaa 100644 --- a/bfs.h +++ b/bfs.h @@ -84,10 +84,10 @@ struct cmdline { /** Color data. */ struct colors *colors; - /** Colors to use for stdout. */ - const struct colors *stdout_colors; - /** Colors to use for stderr. */ - const struct colors *stderr_colors; + /** Colored stdout. */ + CFILE *cout; + /** Colored stderr. */ + CFILE *cerr; /** -mindepth option. */ int mindepth; diff --git a/color.c b/color.c index 129d032..b8acc32 100644 --- a/color.c +++ b/color.c @@ -11,6 +11,7 @@ #include "color.h" #include "bftw.h" +#include #include #include #include @@ -58,6 +59,69 @@ struct colors { char *data; }; +struct color_name { + const char *name; + size_t offset; +}; + +#define COLOR_NAME(name, field) {name, offsetof(struct colors, field)} + +static const struct color_name color_names[] = { + COLOR_NAME("bd", block), + COLOR_NAME("ca", capable), + COLOR_NAME("cd", chardev), + COLOR_NAME("di", dir), + COLOR_NAME("do", door), + COLOR_NAME("er", error), + COLOR_NAME("ex", exec), + COLOR_NAME("fi", file), + COLOR_NAME("ln", link), + COLOR_NAME("mh", multi_hard), + COLOR_NAME("mi", missing), + COLOR_NAME("no", normal), + COLOR_NAME("or", orphan), + COLOR_NAME("ow", ow), + COLOR_NAME("pi", pipe), + COLOR_NAME("rs", reset), + COLOR_NAME("sg", setgid), + COLOR_NAME("so", socket), + COLOR_NAME("st", sticky), + COLOR_NAME("su", setuid), + COLOR_NAME("tw", sticky_ow), + COLOR_NAME("wr", warning), + {0}, +}; + +static const char **look_up_color(const struct colors *colors, const char *name) { + if (!colors) { + return NULL; + } + + for (const struct color_name *entry = color_names; entry->name; ++entry) { + if (strcmp(name, entry->name) == 0) { + return (const char **)((char *)colors + entry->offset); + } + } + + return NULL; +} + +static const char *get_color(const struct colors *colors, const char *name) { + const char **color = look_up_color(colors, name); + if (color) { + return *color; + } else { + return NULL; + } +} + +static void set_color(struct colors *colors, const char *name, const char *value) { + const char **color = look_up_color(colors, name); + if (color) { + *color = value; + } +} + struct colors *parse_colors(const char *ls_colors) { struct colors *colors = malloc(sizeof(struct colors)); if (!colors) { @@ -117,100 +181,7 @@ struct colors *parse_colors(const char *ls_colors) { continue; } - switch (key[0]) { - case 'b': - if (strcmp(key, "bd") == 0) { - colors->block = value; - } - break; - - case 'c': - if (strcmp(key, "ca") == 0) { - colors->capable = value; - } else if (strcmp(key, "cd") == 0) { - colors->chardev = value; - } - break; - - case 'd': - if (strcmp(key, "di") == 0) { - colors->dir = value; - } else if (strcmp(key, "do") == 0) { - colors->door = value; - } - break; - - case 'e': - if (strcmp(key, "ex") == 0) { - colors->exec = value; - } - break; - - case 'f': - if (strcmp(key, "fi") == 0) { - colors->file = value; - } - break; - - case 'l': - if (strcmp(key, "ln") == 0) { - colors->link = value; - } - break; - - case 'm': - if (strcmp(key, "mh") == 0) { - colors->multi_hard = value; - } else if (strcmp(key, "mi") == 0) { - colors->missing = value; - } - break; - - case 'n': - if (strcmp(key, "no") == 0) { - colors->normal = value; - } - break; - - case 'o': - if (strcmp(key, "or") == 0) { - colors->orphan = value; - } else if (strcmp(key, "ow") == 0) { - colors->ow = value; - } - break; - - case 'p': - if (strcmp(key, "pi") == 0) { - colors->pipe = value; - } - break; - - case 'r': - if (strcmp(key, "rs") == 0) { - colors->reset = value; - } - break; - - case 's': - if (strcmp(key, "sg") == 0) { - colors->setgid = value; - } else if (strcmp(key, "so") == 0) { - colors->socket = value; - } else if (strcmp(key, "st") == 0) { - colors->sticky = value; - } else if (strcmp(key, "su") == 0) { - colors->setuid = value; - } - break; - - case 't': - if (strcmp(key, "tw") == 0) { - colors->sticky_ow = value; - } - break; - - case '*': + if (key[0] == '*') { ext = malloc(sizeof(struct ext_color)); if (ext) { ext->ext = key + 1; @@ -219,6 +190,8 @@ struct colors *parse_colors(const char *ls_colors) { ext->next = colors->ext_list; colors->ext_list = ext; } + } else { + set_color(colors, key, value); } } @@ -226,6 +199,38 @@ done: return colors; } +void free_colors(struct colors *colors) { + if (colors) { + struct ext_color *ext = colors->ext_list; + while (ext) { + struct ext_color *saved = ext; + ext = ext->next; + free(saved); + } + + free(colors->data); + free(colors); + } +} + +CFILE *cfdup(FILE *file, const struct colors *colors) { + CFILE *cfile = malloc(sizeof(*cfile)); + if (cfile) { + cfile->file = file; + if (isatty(fileno(file))) { + cfile->colors = colors; + } else { + cfile->colors = NULL; + } + } + return cfile; +} + +int cfclose(CFILE *cfile) { + free(cfile); + return 0; +} + static const char *file_color(const struct colors *colors, const char *filename, const struct BFTW *ftwbuf) { const struct stat *sb = ftwbuf->statbuf; if (!sb) { @@ -331,102 +336,111 @@ static int print_esc(const char *esc, FILE *file) { return 0; } -int pretty_print(const struct colors *colors, const struct BFTW *ftwbuf) { +static int print_path(CFILE *cfile, const struct BFTW *ftwbuf) { + const struct colors *colors = cfile->colors; + FILE *file = cfile->file; const char *path = ftwbuf->path; if (!colors) { - return puts(path) == EOF ? -1 : 0; + return fputs(path, file) == EOF ? -1 : 0; } const char *filename = path + ftwbuf->nameoff; if (colors->dir) { - if (print_esc(colors->dir, stdout) != 0) { + if (print_esc(colors->dir, file) != 0) { return -1; } } - if (fwrite(path, 1, ftwbuf->nameoff, stdout) != ftwbuf->nameoff) { + if (fwrite(path, 1, ftwbuf->nameoff, file) != ftwbuf->nameoff) { return -1; } if (colors->dir) { - if (print_esc(colors->reset, stdout) != 0) { + if (print_esc(colors->reset, file) != 0) { return -1; } } const char *color = file_color(colors, filename, ftwbuf); if (color) { - if (print_esc(color, stdout) != 0) { + if (print_esc(color, file) != 0) { return -1; } } - if (fputs(filename, stdout) == EOF) { + if (fputs(filename, file) == EOF) { return -1; } if (color) { - if (print_esc(colors->reset, stdout) != 0) { + if (print_esc(colors->reset, file) != 0) { return -1; } } - if (fputs("\n", stdout) == EOF) { - return -1; - } return 0; } -static int pretty_format(const struct colors *colors, const char *color, const char *format, va_list args) { - if (color) { - if (print_esc(color, stderr) != 0) { - return -1; - } - } - - if (vfprintf(stderr, format, args) < 0) { - return -1; - } - - if (color) { - if (print_esc(colors->reset, stderr) != 0) { - return -1; - } - } +int cfprintf(CFILE *cfile, const char *format, ...) { + const struct colors *colors = cfile->colors; + FILE *file = cfile->file; - return 0; -} + int ret = -1; -int pretty_warning(const struct colors *colors, const char *format, ...) { va_list args; va_start(args, format); - int ret = pretty_format(colors, colors ? colors->warning : NULL, format, args); + char name[3] = {0}; + const char *esc; - va_end(args); + for (const char *i = format; *i; ++i) { + if (*i == '%') { + switch (*++i) { + case '%': + goto one_char; - return ret; -} + case 'c': + if (fputc(va_arg(args, int), file) == EOF) { + goto done; + } + break; -int pretty_error(const struct colors *colors, const char *format, ...) { - va_list args; - va_start(args, format); + case 's': + if (fputs(va_arg(args, const char *), file) == EOF) { + goto done; + } + break; - int ret = pretty_format(colors, colors ? colors->error : NULL, format, args); + case 'P': + if (print_path(cfile, va_arg(args, const struct BFTW *)) != 0) { + goto done; + } + break; - va_end(args); + case '{': + memcpy(name, i + 1, 2); + esc = get_color(colors, name); + if (esc && print_esc(esc, file) != 0) { + goto done; + } + i += 3; + break; - return ret; -} + default: + errno = EINVAL; + goto done; + } -void free_colors(struct colors *colors) { - if (colors) { - struct ext_color *ext = colors->ext_list; - while (ext) { - struct ext_color *saved = ext; - ext = ext->next; - free(saved); + continue; } - free(colors->data); - free(colors); + one_char: + if (fputc(*i, file) == EOF) { + goto done; + } } + + ret = 0; + +done: + va_end(args); + return ret; } diff --git a/color.h b/color.h index 8ba397d..c3b3e41 100644 --- a/color.h +++ b/color.h @@ -13,6 +13,7 @@ #define BFS_COLOR_H #include "bftw.h" +#include /** * A lookup table for colors. @@ -29,54 +30,58 @@ struct colors; struct colors *parse_colors(const char *ls_colors); /** - * Pretty-print a file path. + * Free a color table. * * @param colors - * The color table to use. - * @param ftwbuf - * The bftw() data for the current path. - * @return 0 on success, -1 on failure. + * The color table to free. */ -int pretty_print(const struct colors *colors, const struct BFTW *ftwbuf); +void free_colors(struct colors *colors); -#if __GNUC__ -# define BFS_PRINTF_ATTRIBUTE(f, v) __attribute__((format(printf, f, v))) -#else -# define BFS_PRINTF_ATTRIBUTE(f, v) -#endif +/** + * A file/stream with associated colors. + */ +typedef struct CFILE { + /** The underlying file/stream. */ + FILE *file; + /** The color table to use, if any. */ + const struct colors *colors; +} CFILE; /** - * Pretty-print a warning message. + * Make a colored copy of an open file. * + * @param file + * The underlying file. * @param colors - * The color table to use. - * @param format - * The format string. - * @param ... - * The format string's arguments. - * @return 0 on success, -1 on failure. + * The color table to use if file is a TTY. + * @return A colored wrapper around file. */ -int pretty_warning(const struct colors *colors, const char *format, ...) BFS_PRINTF_ATTRIBUTE(2, 3); +CFILE *cfdup(FILE *file, const struct colors *colors); /** - * Pretty-print an error message. + * Close a colored file. * - * @param colors - * The color table to use. - * @param format - * The format string. - * @param ... - * The format string's arguments. + * @param cfile + * The colored file to close. * @return 0 on success, -1 on failure. */ -int pretty_error(const struct colors *colors, const char *format, ...) BFS_PRINTF_ATTRIBUTE(2, 3); +int cfclose(CFILE *cfile); /** - * Free a color table. + * Colored, formatted output. * - * @param colors - * The color table to free. + * @param cfile + * The colored stream to print to. + * @param format + * A printf()-style format string, supporting these format specifiers: + * + * %%: A literal '%' + * %c: A single character + * %s: A string + * %P: A colored file path, from a const struct BFTW * argument + * %{cc}: Change the color to 'cc' + * @return 0 on success, -1 on failure. */ -void free_colors(struct colors *colors); +int cfprintf(CFILE *cfile, const char *format, ...); #endif // BFS_COLOR_H diff --git a/eval.c b/eval.c index 512a05a..03dd41d 100644 --- a/eval.c +++ b/eval.c @@ -58,8 +58,7 @@ static bool eval_should_ignore(const struct eval_state *state, int error) { */ static void eval_error(struct eval_state *state) { if (!eval_should_ignore(state, errno)) { - pretty_error(state->cmdline->stderr_colors, - "'%s': %s\n", state->ftwbuf->path, strerror(errno)); + cfprintf(state->cmdline->cerr, "%{er}'%s': %s%{rs}\n", state->ftwbuf->path, strerror(errno)); *state->ret = -1; } } @@ -735,12 +734,12 @@ done: * -print action. */ bool eval_print(const struct expr *expr, struct eval_state *state) { - const struct colors *colors = state->cmdline->stdout_colors; - if (colors) { + CFILE *cout = state->cmdline->cout; + if (cout->colors) { fill_statbuf(state); } - if (pretty_print(colors, state->ftwbuf) != 0) { + if (cfprintf(cout, "%P\n", state->ftwbuf) < 0) { eval_error(state); } @@ -785,8 +784,7 @@ bool eval_regex(const struct expr *expr, struct eval_state *state) { } else if (err != REG_NOMATCH) { char *str = xregerror(err, expr->regex); if (str) { - pretty_error(state->cmdline->stderr_colors, - "'%s': %s\n", path, str); + cfprintf(state->cmdline->cerr, "%{er}'%s': %s%{rs}\n", path, str); free(str); } else { perror("xregerror()"); @@ -1006,7 +1004,7 @@ bool eval_comma(const struct expr *expr, struct eval_state *state) { /** * Debug stat() calls. */ -void debug_stat(const struct eval_state *state) { +static void debug_stat(const struct eval_state *state) { struct BFTW *ftwbuf = state->ftwbuf; fprintf(stderr, "fstatat("); @@ -1061,7 +1059,7 @@ static enum bftw_action cmdline_callback(struct BFTW *ftwbuf, void *ptr) { if (ftwbuf->typeflag == BFTW_ERROR) { if (!eval_should_ignore(&state, ftwbuf->error)) { args->ret = -1; - pretty_error(cmdline->stderr_colors, "'%s': %s\n", ftwbuf->path, strerror(ftwbuf->error)); + cfprintf(cmdline->cerr, "%{er}'%s': %s%{rs}\n", ftwbuf->path, strerror(ftwbuf->error)); } state.action = BFTW_SKIP_SUBTREE; goto done; diff --git a/parse.c b/parse.c index 81f2875..7bed320 100644 --- a/parse.c +++ b/parse.c @@ -188,6 +188,8 @@ void free_cmdline(struct cmdline *cmdline) { if (cmdline) { free_expr(cmdline->expr); + cfclose(cmdline->cerr); + cfclose(cmdline->cout); free_colors(cmdline->colors); struct root *root = cmdline->roots; @@ -295,9 +297,7 @@ static int stat_arg(const struct parser_state *state, struct expr *expr, struct int ret = fstatat(AT_FDCWD, expr->sdata, sb, flags); if (ret != 0) { - pretty_error(cmdline->stderr_colors, - "error: '%s': %s\n", expr->sdata, strerror(errno)); - free_expr(expr); + cfprintf(cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", expr->sdata, strerror(errno)); } return ret; } @@ -452,8 +452,7 @@ static const char *parse_int(const struct parser_state *state, const char *str, bad: if (!(flags & IF_QUIET)) { - pretty_error(state->cmdline->stderr_colors, - "error: '%s' is not a valid integer.\n", str); + cfprintf(state->cmdline->cerr, "%{er}error: '%s' is not a valid integer.%{rs}\n", str); } return NULL; } @@ -518,10 +517,10 @@ static struct expr *parse_option(struct parser_state *state, size_t argc) { const char *arg = *parser_advance(state, T_OPTION, argc); if (state->warn && state->non_option_seen) { - pretty_warning(state->cmdline->stderr_colors, - "warning: The '%s' option applies to the entire command line. For clarity, place\n" - "it before any non-option arguments.\n\n", - arg); + cfprintf(state->cmdline->cerr, + "%{wr}warning: The '%s' option applies to the entire command line. For clarity, place\n" + "it before any non-option arguments.%{rs}\n\n", + arg); } @@ -564,8 +563,7 @@ static struct expr *parse_unary_positional_option(struct parser_state *state, co const char *arg = state->argv[0]; *value = state->argv[1]; if (!*value) { - pretty_error(state->cmdline->stderr_colors, - "error: %s needs a value.\n", arg); + cfprintf(state->cmdline->cerr, "%{er}error: %s needs a value.%{rs}\n", arg); return NULL; } @@ -594,8 +592,7 @@ static struct expr *parse_unary_test(struct parser_state *state, eval_fn *eval) const char *arg = state->argv[0]; const char *value = state->argv[1]; if (!value) { - pretty_error(state->cmdline->stderr_colors, - "error: %s needs a value.\n", arg); + cfprintf(state->cmdline->cerr, "%{er}error: %s needs a value.%{rs}\n", arg); return NULL; } @@ -632,8 +629,7 @@ static struct expr *parse_unary_action(struct parser_state *state, eval_fn *eval const char *arg = state->argv[0]; const char *value = state->argv[1]; if (!value) { - pretty_error(state->cmdline->stderr_colors, - "error: %s needs a value.\n", arg); + cfprintf(state->cmdline->cerr, "%{er}error: %s needs a value.%{rs}\n", arg); return NULL; } @@ -670,8 +666,7 @@ static struct expr *parse_debug(struct parser_state *state, int arg1, int arg2) const char *arg = state->argv[0]; const char *flag = state->argv[1]; if (!flag) { - pretty_error(cmdline->stderr_colors, - "error: %s needs a flag.\n", arg); + cfprintf(cmdline->cerr, "%{er}error: %s needs a flag.%{rs}\n", arg); return NULL; } @@ -695,8 +690,7 @@ static struct expr *parse_debug(struct parser_state *state, int arg1, int arg2) } else if (strcmp(flag, "tree") == 0) { cmdline->debug |= DEBUG_TREE; } else { - pretty_warning(cmdline->stderr_colors, - "warning: Unrecognized debug flag '%s'.\n\n", flag); + cfprintf(cmdline->cerr, "%{wr}warning: Unrecognized debug flag '%s'.%{rs}\n\n", flag); } return parse_unary_flag(state); @@ -715,9 +709,7 @@ static struct expr *parse_optlevel(struct parser_state *state, int arg1, int arg } if (*optlevel > 4) { - pretty_warning(state->cmdline->stderr_colors, - "warning: %s is the same as -O4.\n\n", - state->argv[0]); + cfprintf(state->cmdline->cerr, "%{wr}warning: %s is the same as -O4.%{rs}\n\n", state->argv[0]); } return parse_nullary_flag(state); @@ -772,6 +764,7 @@ static struct expr *parse_acnewer(struct parser_state *state, int field, int arg struct stat sb; if (stat_arg(state, expr, &sb) != 0) { + free_expr(expr); return NULL; } @@ -786,12 +779,13 @@ static struct expr *parse_acnewer(struct parser_state *state, int field, int arg */ static struct expr *parse_color(struct parser_state *state, int color, int arg2) { struct cmdline *cmdline = state->cmdline; + struct colors *colors = cmdline->colors; if (color) { - cmdline->stdout_colors = cmdline->colors; - cmdline->stderr_colors = cmdline->colors; + cmdline->cout->colors = colors; + cmdline->cerr->colors = colors; } else { - cmdline->stdout_colors = NULL; - cmdline->stderr_colors = NULL; + cmdline->cout->colors = NULL; + cmdline->cerr->colors = NULL; } return parse_nullary_option(state); } @@ -872,8 +866,7 @@ static struct expr *parse_depth_limit(struct parser_state *state, int is_min, in const char *arg = state->argv[0]; const char *value = state->argv[1]; if (!value) { - pretty_error(cmdline->stderr_colors, - "error: %s needs a value.\n", arg); + cfprintf(cmdline->cerr, "%{er}error: %s needs a value.%{rs}\n", arg); return NULL; } @@ -908,14 +901,12 @@ static struct expr *parse_exec(struct parser_state *state, int flags, int arg2) } if (!arg) { - pretty_error(state->cmdline->stderr_colors, - "error: %s: Expected ';' or '+'.\n", state->argv[0]); + cfprintf(state->cmdline->cerr, "%{er}error: %s: Expected ';' or '+'.%{rs}\n", state->argv[0]); return NULL; } if (flags & EXEC_MULTI) { - pretty_error(state->cmdline->stderr_colors, - "error: %s ... {} + is not supported yet.\n", state->argv[0]); + cfprintf(state->cmdline->cerr, "%{er}error: %s ... {} + is not supported yet.%{rs}\n", state->argv[0]); return NULL; } @@ -934,8 +925,7 @@ static struct expr *parse_f(struct parser_state *state, int arg1, int arg2) { const char *path = state->argv[0]; if (!path) { - pretty_error(state->cmdline->stderr_colors, - "error: -f requires a path.\n"); + cfprintf(state->cmdline->cerr, "%{er}error: -f requires a path.%{rs}\n"); return NULL; } @@ -953,8 +943,7 @@ static struct expr *parse_f(struct parser_state *state, int arg1, int arg2) { static int expr_open(struct parser_state *state, struct expr *expr, const char *path) { expr->file = fopen(path, "wb"); if (!expr->file) { - pretty_error(state->cmdline->stderr_colors, - "error: '%s': %s\n", path, strerror(errno)); + cfprintf(state->cmdline->cerr, "%{er}error: '%s': %s%{rs}\n", path, strerror(errno)); free_expr(expr); return -1; } @@ -997,15 +986,13 @@ static struct expr *parse_fprintf(struct parser_state *state, int arg1, int arg2 const char *file = state->argv[1]; if (!file) { - pretty_error(state->cmdline->stderr_colors, - "error: %s needs a file.\n", arg); + cfprintf(state->cmdline->cerr, "%{er}error: %s needs a file.%{rs}\n", arg); return NULL; } const char *format = state->argv[2]; if (!format) { - pretty_error(state->cmdline->stderr_colors, - "error: %s needs a format string.\n", arg); + cfprintf(state->cmdline->cerr, "%{er}error: %s needs a format string.%{rs}\n", arg); return NULL; } @@ -1018,7 +1005,7 @@ static struct expr *parse_fprintf(struct parser_state *state, int arg1, int arg2 return NULL; } - expr->printf = parse_bfs_printf(format, state->cmdline->stderr_colors); + expr->printf = parse_bfs_printf(format, state->cmdline->cerr); if (!expr->printf) { free_expr(expr); return NULL; @@ -1047,8 +1034,7 @@ static struct expr *parse_group(struct parser_state *state, int arg1, int arg2) goto fail; } } else { - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: No such group.\n", arg, expr->sdata); + cfprintf(state->cmdline->cerr, "%{er}error: %s %s: No such group.%{rs}\n", arg, expr->sdata); goto fail; } @@ -1086,8 +1072,7 @@ static struct expr *parse_user(struct parser_state *state, int arg1, int arg2) { goto fail; } } else { - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: No such user.\n", arg, expr->sdata); + cfprintf(state->cmdline->cerr, "%{er}error: %s %s: No such user.%{rs}\n", arg, expr->sdata); goto fail; } @@ -1144,8 +1129,7 @@ static struct expr *set_fnm_casefold(const struct parser_state *state, struct ex #ifdef FNM_CASEFOLD expr->idata = FNM_CASEFOLD; #else - pretty_error(state->cmdline->stderr_colors, - "error: %s is missing platform support.\n", expr->argv[0]); + cfprintf(state->cmdline->cerr, "%{er}error: %s is missing platform support.%{rs}\n", expr->argv[0]); free_expr(expr); return NULL; #endif @@ -1186,14 +1170,13 @@ static struct expr *parse_lname(struct parser_state *state, int casefold, int ar static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2) { const char *arg = state->argv[0]; if (strlen(arg) != 8) { - pretty_error(state->cmdline->stderr_colors, - "error: Expected -newerXY; found %s.\n", arg); + cfprintf(state->cmdline->cerr, "%{er}error: Expected -newerXY; found %s.%{rs}\n", arg); return NULL; } struct expr *expr = parse_unary_test(state, eval_acnewer); if (!expr) { - return NULL; + goto fail; } switch (arg[6]) { @@ -1208,27 +1191,21 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2 break; case 'B': - pretty_error(state->cmdline->stderr_colors, - "error: %s: File birth times ('B') are not supported.\n", arg); - free_expr(expr); - return NULL; + cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times ('B') are not supported.%{rs}\n", arg); + goto fail; default: - pretty_error(state->cmdline->stderr_colors, - "error: %s: For -newerXY, X should be 'a', 'c', 'm', or 'B'.\n", arg); - free_expr(expr); - return NULL; + cfprintf(state->cmdline->cerr, "%{er}error: %s: For -newerXY, X should be 'a', 'c', 'm', or 'B'.%{rs}\n", arg); + goto fail; } if (arg[7] == 't') { - pretty_error(state->cmdline->stderr_colors, - "error: %s: Explicit reference times ('t') are not supported.\n", arg); - free_expr(expr); - return NULL; + cfprintf(state->cmdline->cerr, "%{er}error: %s: Explicit reference times ('t') are not supported.%{rs}\n", arg); + goto fail; } else { struct stat sb; if (stat_arg(state, expr, &sb) != 0) { - return NULL; + goto fail; } switch (arg[7]) { @@ -1243,20 +1220,21 @@ static struct expr *parse_newerxy(struct parser_state *state, int arg1, int arg2 break; case 'B': - pretty_error(state->cmdline->stderr_colors, - "error: %s: File birth times ('B') are not supported.\n", arg); - free_expr(expr); - return NULL; + cfprintf(state->cmdline->cerr, "%{er}error: %s: File birth times ('B') are not supported.%{rs}\n", arg); + goto fail; default: - pretty_error(state->cmdline->stderr_colors, - "error: %s: For -newerXY, Y should be 'a', 'c', 'm', 'B', or 't'.\n", arg); - free_expr(expr); - return NULL; + cfprintf(state->cmdline->cerr, + "%{er}error: %s: For -newerXY, Y should be 'a', 'c', 'm', 'B', or 't'.%{rs}\n", arg); + goto fail; } } return expr; + +fail: + free_expr(expr); + return NULL; } /** @@ -1278,9 +1256,9 @@ static struct expr *parse_nohidden(struct parser_state *state, int arg1, int arg */ static struct expr *parse_noleaf(struct parser_state *state, int arg1, int arg2) { if (state->warn) { - pretty_warning(state->cmdline->stderr_colors, - "warning: bfs does not apply the optimization that %s inhibits.\n\n", - state->argv[0]); + cfprintf(state->cmdline->cerr, + "%{wr}warning: bfs does not apply the optimization that %s inhibits.%{rs}\n\n", + state->argv[0]); } return parse_nullary_option(state); @@ -1497,9 +1475,7 @@ done: return 0; fail: - pretty_error(state->cmdline->stderr_colors, - "error: '%s' is an invalid mode.\n\n", - mode); + cfprintf(state->cmdline->cerr, "%{er}error: '%s' is an invalid mode.%{rs}\n", mode); return -1; } @@ -1567,7 +1543,7 @@ static struct expr *parse_printf(struct parser_state *state, int arg1, int arg2) expr->file = stdout; - expr->printf = parse_bfs_printf(expr->sdata, state->cmdline->stderr_colors); + expr->printf = parse_bfs_printf(expr->sdata, state->cmdline->cerr); if (!expr->printf) { free_expr(expr); return NULL; @@ -1609,9 +1585,7 @@ static struct expr *parse_regex(struct parser_state *state, int flags, int arg2) if (err != 0) { char *str = xregerror(err, expr->regex); if (str) { - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: %s.\n", - expr->argv[0], expr->argv[1], str); + cfprintf(state->cmdline->cerr, "%{er}error: %s %s: %s.%{rs}\n", expr->argv[0], expr->argv[1], str); free(str); } else { perror("xregerror()"); @@ -1664,8 +1638,7 @@ static struct expr *parse_regextype(struct parser_state *state, int arg1, int ar return expr; fail_bad_type: - pretty_error(state->cmdline->stderr_colors, - "error: Unsupported -regextype '%s'.\n\n", type); + cfprintf(state->cmdline->cerr, "%{er}error: Unsupported -regextype '%s'.%{rs}\n\n", type); fail_list_types: fputs("Supported types are:\n\n", file); fputs(" posix-basic: POSIX basic regular expressions (BRE)\n", file); @@ -1686,6 +1659,7 @@ static struct expr *parse_samefile(struct parser_state *state, int arg1, int arg struct stat sb; if (stat_arg(state, expr, &sb) != 0) { + free_expr(expr); return NULL; } @@ -1747,9 +1721,9 @@ static struct expr *parse_size(struct parser_state *state, int arg1, int arg2) { return expr; bad_unit: - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: Expected a size unit (one of bcwkMGTP); found %s.\n", - expr->argv[0], expr->argv[1], unit); + cfprintf(state->cmdline->cerr, + "%{er}error: %s %s: Expected a size unit (one of bcwkMGTP); found %s.%{rs}\n", + expr->argv[0], expr->argv[1], unit); fail: free_expr(expr); return NULL; @@ -1803,15 +1777,15 @@ static struct expr *parse_type(struct parser_state *state, int x, int arg2) { break; case '\0': - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: Expected a type flag.\n", - expr->argv[0], expr->argv[1]); + cfprintf(state->cmdline->cerr, + "%{er}error: %s %s: Expected a type flag.%{rs}\n", + expr->argv[0], expr->argv[1]); goto fail; default: - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: Unknown type flag '%c' (expected one of [bcdpflsD]).\n", - expr->argv[0], expr->argv[1], *c); + cfprintf(state->cmdline->cerr, + "%{er}error: %s %s: Unknown type flag '%c' (expected one of [bcdpflsD]).%{rs}\n", + expr->argv[0], expr->argv[1], *c); goto fail; } @@ -1822,9 +1796,9 @@ static struct expr *parse_type(struct parser_state *state, int x, int arg2) { ++c; continue; } else { - pretty_error(state->cmdline->stderr_colors, - "error: %s %s: Types must be comma-separated.\n", - expr->argv[0], expr->argv[1]); + cfprintf(state->cmdline->cerr, + "%{er}error: %s %s: Types must be comma-separated.%{rs}\n", + expr->argv[0], expr->argv[1]); goto fail; } } @@ -2070,13 +2044,13 @@ static struct expr *parse_literal(struct parser_state *state) { } match = table_lookup_fuzzy(arg + 1); - pretty_error(cmdline->stderr_colors, - "error: Unknown argument '%s'; did you mean '-%s'?\n", arg, match->arg); + cfprintf(cmdline->cerr, + "%{er}error: Unknown argument '%s'; did you mean '-%s'?%{rs}\n", + arg, match->arg); return NULL; unexpected: - pretty_error(cmdline->stderr_colors, - "error: Expected a predicate; found '%s'.\n", arg); + cfprintf(cmdline->cerr, "%{er}error: Expected a predicate; found '%s'.%{rs}\n", arg); return NULL; } @@ -2477,8 +2451,7 @@ static struct expr *parse_whole_expr(struct parser_state *state) { } if (state->argv[0]) { - pretty_error(state->cmdline->stderr_colors, - "error: Unexpected argument '%s'.\n", state->argv[0]); + cfprintf(state->cmdline->cerr, "%{er}error: Unexpected argument '%s'.%{rs}\n", state->argv[0]); goto fail; } @@ -2539,7 +2512,7 @@ void dump_cmdline(const struct cmdline *cmdline, bool verbose) { fprintf(stderr, "%s ", root->path); } - if (cmdline->stdout_colors) { + if (cmdline->cout->colors) { fputs("-color ", stderr); } else { fputs("-nocolor ", stderr); @@ -2609,8 +2582,12 @@ struct cmdline *parse_cmdline(int argc, char *argv[]) { cmdline->nopen_files = 0; cmdline->colors = parse_colors(getenv("LS_COLORS")); - cmdline->stdout_colors = isatty(STDOUT_FILENO) ? cmdline->colors : NULL; - cmdline->stderr_colors = isatty(STDERR_FILENO) ? cmdline->colors : NULL; + cmdline->cout = cfdup(stdout, cmdline->colors); + cmdline->cerr = cfdup(stderr, cmdline->colors); + if (!cmdline->cout || !cmdline->cerr) { + perror("cfdup()"); + goto fail; + } struct parser_state state = { .cmdline = cmdline, diff --git a/printf.c b/printf.c index b0f708f..b1ef710 100644 --- a/printf.c +++ b/printf.c @@ -496,7 +496,7 @@ static int append_literal(struct bfs_printf_directive ***tail, struct bfs_printf return 0; } -struct bfs_printf *parse_bfs_printf(const char *format, const struct colors *stderr_colors) { +struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr) { struct bfs_printf *command = malloc(sizeof(*command)); if (!command) { perror("malloc()"); @@ -551,15 +551,11 @@ struct bfs_printf *parse_bfs_printf(const char *format, const struct colors *std goto done; case '\0': - pretty_error(stderr_colors, - "error: '%s': Incomplete escape sequence '\\'.\n", - format); + cfprintf(cerr, "%{er}error: '%s': Incomplete escape sequence '\\'.%{rs}\n", format); goto error; default: - pretty_error(stderr_colors, - "error: '%s': Unrecognized escape sequence '\\%c'.\n", - format, c); + cfprintf(cerr, "%{er}error: '%s': Unrecognized escape sequence '\\%c'.%{rs}\n", format, c); goto error; } } else if (c == '%') { @@ -592,9 +588,7 @@ struct bfs_printf *parse_bfs_printf(const char *format, const struct colors *std case ' ': case '-': if (strchr(directive->str, c)) { - pretty_error(stderr_colors, - "error: '%s': Duplicate flag '%c'.\n", - format, c); + cfprintf(cerr, "%{er}error: '%s': Duplicate flag '%c'.%{rs}\n", format, c); goto directive_error; } if (dstrncat(&directive->str, &c, 1) != 0) { @@ -774,36 +768,31 @@ struct bfs_printf *parse_bfs_printf(const char *format, const struct colors *std break; case '\0': - pretty_error(stderr_colors, - "error: '%s': Incomplete time specifier '%s%c'.\n", - format, directive->str, i[-1]); + cfprintf(cerr, "%{er}error: '%s': Incomplete time specifier '%s%c'.%{rs}\n", + format, directive->str, i[-1]); goto directive_error; default: - pretty_error(stderr_colors, - "error: '%s': Unrecognized time specifier '%%%c%c'.\n", - format, i[-1], c); + cfprintf(cerr, "%{er}error: '%s': Unrecognized time specifier '%%%c%c'.%{rs}\n", + format, i[-1], c); goto directive_error; } break; case '\0': - pretty_error(stderr_colors, - "error: '%s': Incomplete format specifier '%s'.\n", - format, directive->str); + cfprintf(cerr, "%{er}error: '%s': Incomplete format specifier '%s'.%{rs}\n", + format, directive->str); goto directive_error; default: - pretty_error(stderr_colors, - "error: '%s': Unrecognized format specifier '%%%c'.\n", - format, c); + cfprintf(cerr, "%{er}error: '%s': Unrecognized format specifier '%%%c'.%{rs}\n", + format, c); goto directive_error; } if (must_be_numeric && strcmp(specifier, "s") == 0) { - pretty_error(stderr_colors, - "error: '%s': Invalid flags '%s' for string format '%%%c'.\n", - format, directive->str + 1, c); + cfprintf(cerr, "%{er}error: '%s': Invalid flags '%s' for string format '%%%c'.%{rs}\n", + format, directive->str + 1, c); goto directive_error; } diff --git a/printf.h b/printf.h index 34eda0a..32171ca 100644 --- a/printf.h +++ b/printf.h @@ -34,11 +34,11 @@ struct bfs_printf { * * @param format * The format string to parse. - * @param stderr_colors - * Color table for printing error messages. + * @param cerr + * For error messages. * @return The parsed printf command, or NULL on failure. */ -struct bfs_printf *parse_bfs_printf(const char *format, const struct colors *stderr_colors); +struct bfs_printf *parse_bfs_printf(const char *format, CFILE *cerr); /** * Evaluate a parsed format string. -- cgit v1.2.3