summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTavian Barnes <tavianator@tavianator.com>2017-03-11 14:06:42 -0500
committerTavian Barnes <tavianator@tavianator.com>2017-03-11 14:06:42 -0500
commit21f6c460fcda0161993f75421614efb6971af23b (patch)
tree80ff098bd62d81a694ad22c1dae66cdb01c7e51f
parentd03c58ef1cc8aa932b4f652dea14f762716bc02a (diff)
downloadbfs-21f6c460fcda0161993f75421614efb6971af23b.tar.xz
Make a printf()-style API for colored messages
-rw-r--r--Makefile5
-rw-r--r--bfs.h8
-rw-r--r--color.c302
-rw-r--r--color.h67
-rw-r--r--eval.c16
-rw-r--r--parse.c185
-rw-r--r--printf.c39
-rw-r--r--printf.h6
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 <errno.h>
#include <stdarg.h>
#include <stdbool.h>
#include <stdio.h>
@@ -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 <stdio.h>
/**
* 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.