X-Git-Url: http://git.refcnt.org/?a=blobdiff_plain;f=colorize.c;h=eb2be2f5080eb9acfa3a58f52fd977d29efe671f;hb=189af18c6bd1f1f01d410fff4bc9d3996136b373;hp=ea2a780e19938febe5c679e291d3912806024a2c;hpb=85e7ff2cdccb0e16ef15aad7bb4fa469f82a8410;p=colorize.git diff --git a/colorize.c b/colorize.c index ea2a780..eb2be2f 100644 --- a/colorize.c +++ b/colorize.c @@ -2,7 +2,7 @@ * colorize - Read text from standard input stream or file and print * it colorized through use of ANSI escape sequences * - * Copyright (c) 2011-2015 Steven Schubiger + * Copyright (c) 2011-2016 Steven Schubiger * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by @@ -104,11 +104,19 @@ && (streq (color_names[color2]->name, "none") \ || streq (color_names[color2]->name, "default")) \ -#define COLOR_SEP_CHAR '/' +#define ALLOC_COMPLETE_PART_LINE 8 + +#if defined(COLOR_SEP_CHAR_COLON) +# define COLOR_SEP_CHAR ':' +#elif defined(COLOR_SEP_CHAR_SLASH) +# define COLOR_SEP_CHAR '/' +#else +# define COLOR_SEP_CHAR '/' +#endif #define DEBUG_FILE "debug.txt" -#define VERSION "0.55" +#define VERSION "0.58" typedef enum { false, true } bool; @@ -186,21 +194,22 @@ static const struct { { bg_colors, sizeof (bg_colors) / sizeof (struct color), "background" }, }; -static FILE *stream = NULL; +static FILE *stream; #if DEBUG -static FILE *log = NULL; +static FILE *log; #endif -static unsigned int stacked_vars = 0; -static void **vars_list = NULL; +static unsigned int stacked_vars; +static void **vars_list; -static bool clean = false; -static bool clean_all = false; +static bool clean; +static bool clean_all; -static char *exclude = NULL; +static char *exclude; static const char *program_name; +static void process_opts (int, char **); static void print_hint (void); static void print_help (void); static void print_version (void); @@ -208,7 +217,13 @@ static void cleanup (void); static void free_color_names (struct color_name **); static void process_args (unsigned int, char **, bool *, const struct color **, const char **, FILE **); static void process_file_arg (const char *, const char **, FILE **); +static void skip_path_colors (const char *, const char *, const struct stat *); +static void gather_color_names (const char *, bool *, struct color_name **); static void read_print_stream (bool, const struct color **, const char *, FILE *); +static void merge_print_line (const char *, const char *, FILE *); +static void complete_part_line (const char *, char **, FILE *); +static bool get_next_char (char *, const char **, FILE *, bool *); +static void save_char (char, char **, size_t *, size_t *); static void find_color_entries (struct color_name **, const struct color **); static void find_color_entry (const struct color_name *, unsigned int, const struct color **); static void print_line (bool, const struct color **, const char * const, unsigned int); @@ -245,21 +260,81 @@ static void vfprintf_fail (const char *, ...); static void stack_var (void ***, unsigned int *, unsigned int, void *); static void release_var (void **, unsigned int, void **); +extern int optind; + +int +main (int argc, char **argv) +{ + unsigned int arg_cnt; + + bool bold = false; + + const struct color *colors[2] = { + NULL, /* foreground */ + NULL, /* background */ + }; + + const char *file = NULL; + + program_name = argv[0]; + atexit (cleanup); + + setvbuf (stdout, NULL, _IOLBF, 0); + +#if DEBUG + log = open_file (DEBUG_FILE, "w"); +#endif + + process_opts (argc, argv); + + arg_cnt = argc - optind; + + if (clean || clean_all) + { + if (clean && clean_all) + vfprintf_fail (formats[FMT_GENERIC], "--clean and --clean-all switch are mutually exclusive"); + if (arg_cnt > 1) + { + const char *format = "%s %s"; + const char *message = "switch cannot be used with more than one file"; + if (clean) + vfprintf_fail (format, "--clean", message); + else if (clean_all) + vfprintf_fail (format, "--clean-all", message); + } + } + else + { + if (arg_cnt == 0 || arg_cnt > 2) + { + vfprintf_diag ("%u arguments provided, expected 1-2 arguments or clean option", arg_cnt); + print_hint (); + exit (EXIT_FAILURE); + } + } + + if (clean || clean_all) + process_file_arg (argv[optind], &file, &stream); + else + process_args (arg_cnt, &argv[optind], &bold, colors, &file, &stream); + read_print_stream (bold, colors, file, stream); + + RELEASE_VAR (exclude); + + exit (EXIT_SUCCESS); +} + #define SET_OPT_TYPE(type) \ opt_type = type; \ opt = 0; \ goto PARSE_OPT; \ extern char *optarg; -extern int optind; - -static int opt_type = 0; +static int opt_type; -int -main (int argc, char **argv) +static void +process_opts (int argc, char **argv) { - unsigned int arg_cnt = 0; - enum { OPT_CLEAN = 1, OPT_CLEAN_ALL, @@ -278,24 +353,6 @@ main (int argc, char **argv) { NULL, 0, NULL, 0 }, }; - bool bold = false; - - const struct color *colors[2] = { - NULL, /* foreground */ - NULL, /* background */ - }; - - const char *file = NULL; - - program_name = argv[0]; - atexit (cleanup); - - setvbuf (stdout, NULL, _IOLBF, 0); - -#if DEBUG - log = open_file (DEBUG_FILE, "w"); -#endif - while ((opt = getopt_long (argc, argv, "hV", long_opts, NULL)) != -1) { PARSE_OPT: @@ -349,42 +406,6 @@ main (int argc, char **argv) ABORT_TRACE (); } } - - arg_cnt = argc - optind; - - if (clean || clean_all) - { - if (clean && clean_all) - vfprintf_fail (formats[FMT_GENERIC], "--clean and --clean-all switch are mutually exclusive"); - if (arg_cnt > 1) - { - const char *format = "%s %s"; - const char *message = "switch cannot be used with more than one file"; - if (clean) - vfprintf_fail (format, "--clean", message); - else if (clean_all) - vfprintf_fail (format, "--clean-all", message); - } - } - else - { - if (arg_cnt == 0 || arg_cnt > 2) - { - vfprintf_diag ("%u arguments provided, expected 1-2 arguments or clean option", arg_cnt); - print_hint (); - exit (EXIT_FAILURE); - } - } - - if (clean || clean_all) - process_file_arg (argv[optind], &file, &stream); - else - process_args (arg_cnt, &argv[optind], &bold, colors, &file, &stream); - read_print_stream (bold, colors, file, stream); - - RELEASE_VAR (exclude); - - exit (EXIT_SUCCESS); } static void @@ -461,6 +482,7 @@ print_version (void) } else printf ("Buffer size: %lu byte%s\n", (unsigned long)BUF_SIZE, BUF_SIZE > 1 ? "s" : ""); + printf ("Color separator: '%c'\n", COLOR_SEP_CHAR); printf ("Debugging: %s\n", debug ? "yes" : "no"); } @@ -480,9 +502,7 @@ cleanup (void) { unsigned int i; for (i = 0; i < stacked_vars; i++) - if (vars_list[i]) - free (vars_list[i]); - + free (vars_list[i]); free_null (vars_list); } } @@ -503,8 +523,7 @@ static void process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct color **colors, const char **file, FILE **stream) { int ret; - unsigned int index; - char *color, *p, *str; + char *p; struct stat sb; const char *color_string = arg_cnt >= 1 ? arg_strings[0] : NULL; @@ -525,53 +544,7 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct /* Ensure that we don't fail if there's a file with one or more color names in its path. */ if (ret == 0) /* success */ - { - bool have_file; - unsigned int c; - const char *color = color_string; - const mode_t mode = sb.st_mode; - - for (c = 1; c <= 2 && *color; c++) - { - bool matched = false; - unsigned int i; - for (i = 0; i < tables[FOREGROUND].count; i++) - { - const struct color *entry = &tables[FOREGROUND].entries[i]; - if (has_color_name (color, entry->name)) - { - color += strlen (entry->name); - matched = true; - break; - } - } - if (!matched && has_color_name (color, "random")) - { - color += strlen ("random"); - matched = true; - } - if (matched && *color == COLOR_SEP_CHAR && *(color + 1)) - color++; - else - break; - } - - have_file = (*color != '\0'); - - if (have_file) - { - const char *file_exists = color_string; - if (file_string) - vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "cannot be used as color string"); - else - { - if (VALID_FILE_TYPE (mode)) - vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "must be preceeded by color string"); - else - vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "is not a valid file type"); - } - } - } + skip_path_colors (color_string, file_string, &sb); if ((p = strchr (color_string, COLOR_SEP_CHAR))) { @@ -583,59 +556,7 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct vfprintf_fail (formats[FMT_STRING], "one color pair allowed only for string", color_string); } - str = xstrdup (color_string); - STACK_VAR (str); - - for (index = 0, color = str; *color; index++, color = p) - { - char *ch, *sep; - - p = NULL; - if ((sep = strchr (color, COLOR_SEP_CHAR))) - { - *sep = '\0'; - p = sep + 1; - } - else - p = color + strlen (color); - assert (p); - - for (ch = color; *ch; ch++) - if (!isalpha (*ch)) - vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be made of non-alphabetic characters"); - - for (ch = color + 1; *ch; ch++) - if (!islower (*ch)) - vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be in mixed lower/upper case"); - - if (streq (color, "None")) - vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be bold"); - - if (isupper (*color)) - { - switch (index) - { - case FOREGROUND: - *bold = true; - break; - case BACKGROUND: - vfprintf_fail (formats[FMT_COLOR], tables[BACKGROUND].desc, color, "cannot be bold"); - default: /* never reached */ - ABORT_TRACE (); - } - } - - color_names[index] = xcalloc (1, sizeof (struct color_name)); - - color_names[index]->orig = xstrdup (color); - - for (ch = color; *ch; ch++) - *ch = tolower (*ch); - - color_names[index]->name = xstrdup (color); - } - - RELEASE_VAR (str); + gather_color_names (color_string, bold, color_names); assert (color_names[FOREGROUND]); @@ -702,23 +623,121 @@ process_file_arg (const char *file_string, const char **file, FILE **stream) assert (*file); } -#define MERGE_PRINT_LINE(part_line, line, flags, check_eof) do { \ - char *current_line, *merged_line = NULL; \ - if (part_line) \ - { \ - merged_line = str_concat (part_line, line); \ - free_null (part_line); \ - } \ - current_line = merged_line ? merged_line : (char *)line; \ - if (!check_eof || *current_line != '\0') \ - print_line (bold, colors, current_line, flags); \ - free (merged_line); \ -} while (false) +static void +skip_path_colors (const char *color_string, const char *file_string, const struct stat *sb) +{ + bool have_file; + unsigned int c; + const char *color = color_string; + const mode_t mode = sb->st_mode; + + for (c = 1; c <= 2 && *color; c++) + { + bool matched = false; + unsigned int i; + for (i = 0; i < tables[FOREGROUND].count; i++) + { + const struct color *entry = &tables[FOREGROUND].entries[i]; + if (has_color_name (color, entry->name)) + { + color += strlen (entry->name); + matched = true; + break; + } + } + if (!matched && has_color_name (color, "random")) + { + color += strlen ("random"); + matched = true; + } + if (matched && *color == COLOR_SEP_CHAR && *(color + 1)) + color++; + else + break; + } + + have_file = (*color != '\0'); + + if (have_file) + { + const char *file_exists = color_string; + if (file_string) + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "cannot be used as color string"); + else + { + if (VALID_FILE_TYPE (mode)) + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "must be preceeded by color string"); + else + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "is not a valid file type"); + } + } +} + +static void +gather_color_names (const char *color_string, bool *bold, struct color_name **color_names) +{ + unsigned int index; + char *color, *p, *str; + + str = xstrdup (color_string); + STACK_VAR (str); + + for (index = 0, color = str; *color; index++, color = p) + { + char *ch, *sep; + + p = NULL; + if ((sep = strchr (color, COLOR_SEP_CHAR))) + { + *sep = '\0'; + p = sep + 1; + } + else + p = color + strlen (color); + assert (p); + + for (ch = color; *ch; ch++) + if (!isalpha (*ch)) + vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be made of non-alphabetic characters"); + + for (ch = color + 1; *ch; ch++) + if (!islower (*ch)) + vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be in mixed lower/upper case"); + + if (streq (color, "None")) + vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be bold"); + + if (isupper (*color)) + { + switch (index) + { + case FOREGROUND: + *bold = true; + break; + case BACKGROUND: + vfprintf_fail (formats[FMT_COLOR], tables[BACKGROUND].desc, color, "cannot be bold"); + default: /* never reached */ + ABORT_TRACE (); + } + } + + color_names[index] = xcalloc (1, sizeof (struct color_name)); + + color_names[index]->orig = xstrdup (color); + + for (ch = color; *ch; ch++) + *ch = tolower (*ch); + + color_names[index]->name = xstrdup (color); + } + + RELEASE_VAR (str); +} static void read_print_stream (bool bold, const struct color **colors, const char *file, FILE *stream) { - char buf[BUF_SIZE + 1], *part_line = NULL; + char buf[BUF_SIZE + 1]; unsigned int flags = 0; while (!feof (stream)) @@ -747,28 +766,158 @@ read_print_stream (bool bold, const struct color **colors, const char *file, FIL vfprintf_fail (formats[FMT_FILE], file, "unrecognized line ending"); p = eol + SKIP_LINE_ENDINGS (flags); *eol = '\0'; - MERGE_PRINT_LINE (part_line, line, flags, false); + print_line (bold, colors, line, flags); line = p; } - if (feof (stream)) { - MERGE_PRINT_LINE (part_line, line, 0, true); - } - else if (*line != '\0') + if (feof (stream)) { - if (!clean && !clean_all) /* efficiency */ + if (*line != '\0') print_line (bold, colors, line, 0); - else if (!part_line) - part_line = xstrdup (line); + } + else if (*line != '\0') + { + char *p; + if ((clean || clean_all) && (p = strrchr (line, '\033'))) + merge_print_line (line, p, stream); else - { - char *merged_line = str_concat (part_line, line); - free (part_line); - part_line = merged_line; - } + print_line (bold, colors, line, 0); } } } +static void +merge_print_line (const char *line, const char *p, FILE *stream) +{ + char *buf = NULL; + char *merged_esc = NULL; + const char *esc = ""; + const char char_restore = *p; + + complete_part_line (p + 1, &buf, stream); + + if (buf) + { + /* form escape sequence */ + esc = merged_esc = str_concat (p, buf); + /* shorten partial line accordingly */ + *(char *)p = '\0'; + free (buf); + } + +#ifdef TEST_MERGE_PART_LINE + printf ("%s%s", line, esc); + fflush (stdout); + _exit (EXIT_SUCCESS); +#else + print_clean (line); + *(char *)p = char_restore; + print_clean (esc); + free (merged_esc); +#endif +} + +static void +complete_part_line (const char *p, char **buf, FILE *stream) +{ + bool got_next_char = false, read_from_stream; + char ch; + size_t i = 0, size; + + if (get_next_char (&ch, &p, stream, &read_from_stream)) + { + if (ch == '[') + { + if (read_from_stream) + save_char (ch, buf, &i, &size); + } + else + { + if (read_from_stream) + ungetc ((int)ch, stream); + return; /* cancel */ + } + } + else + return; /* cancel */ + + while (get_next_char (&ch, &p, stream, &read_from_stream)) + { + if (isdigit (ch) || ch == ';') + { + if (read_from_stream) + save_char (ch, buf, &i, &size); + } + else /* read next character */ + { + got_next_char = true; + break; + } + } + + if (got_next_char) + { + if (ch == 'm') + { + if (read_from_stream) + save_char (ch, buf, &i, &size); + } + else + { + if (read_from_stream) + ungetc ((int)ch, stream); + return; /* cancel */ + } + } + else + return; /* cancel */ +} + +static bool +get_next_char (char *ch, const char **p, FILE *stream, bool *read_from_stream) +{ + if (**p == '\0') + { + int c; + if ((c = fgetc (stream)) != EOF) + { + *ch = (char)c; + *read_from_stream = true; + return true; + } + else + { + *read_from_stream = false; + return false; + } + } + else + { + *ch = **p; + (*p)++; + *read_from_stream = false; + return true; + } +} + +static void +save_char (char ch, char **buf, size_t *i, size_t *size) +{ + if (!*buf) + { + *size = ALLOC_COMPLETE_PART_LINE; + *buf = xmalloc (*size); + } + /* +1: effective occupied size of buffer */ + else if ((*i + 1) == *size) + { + *size *= 2; + *buf = xrealloc (*buf, *size); + } + (*buf)[*i] = ch; + (*buf)[*i + 1] = '\0'; + (*i)++; +} + static void find_color_entries (struct color_name **color_names, const struct color **colors) {