X-Git-Url: http://git.refcnt.org/?a=blobdiff_plain;f=colorize.c;h=a25ec59b19636b0e64eca49d2fe615406f3c18a8;hb=d735e9ff2fe6f739365b4f0b17c8512ab2ca8ae2;hp=5b5d40373a664857a56b61fdefb046123ab1468c;hpb=a41e21dd8e950267ce0be2dfc0ad444904764c23;p=colorize.git diff --git a/colorize.c b/colorize.c index 5b5d403..a25ec59 100644 --- a/colorize.c +++ b/colorize.c @@ -34,7 +34,9 @@ #include #include -#define DEBUG 0 +#ifndef DEBUG +# define DEBUG 0 +#endif #define str(arg) #arg #define to_str(arg) str(arg) @@ -42,11 +44,13 @@ #define streq(s1, s2) (strcmp (s1, s2) == 0) #if DEBUG -# define xmalloc(size) malloc_wrap_debug(size, __FILE__, __LINE__) -# define xrealloc(ptr, size) realloc_wrap_debug(ptr, size, __FILE__, __LINE__) +# define xmalloc(size) malloc_wrap_debug(size, __FILE__, __LINE__) +# define xcalloc(nmemb, size) calloc_wrap_debug(nmemb, size, __FILE__, __LINE__) +# define xrealloc(ptr, size) realloc_wrap_debug(ptr, size, __FILE__, __LINE__) #else -# define xmalloc(size) malloc_wrap(size) -# define xrealloc(ptr, size) realloc_wrap(ptr, size) +# define xmalloc(size) malloc_wrap(size) +# define xcalloc(nmemb, size) calloc_wrap(nmemb, size) +# define xrealloc(ptr, size) realloc_wrap(ptr, size) #endif #define free_null(ptr) free_wrap((void **)&ptr) @@ -90,7 +94,7 @@ #define COLOR_SEP_CHAR '/' -#define VERSION "0.49" +#define VERSION "0.50" typedef unsigned short bool; @@ -159,25 +163,14 @@ static const struct { { bg_colors, sizeof (bg_colors) / sizeof (struct color), "background" }, }; -enum stream_mode { SCAN_FIRST = 1, SCAN_ALWAYS }; - -struct ending { - unsigned int flags; - const char newline[3]; -}; - -static const struct ending endings[] = { - { CR & LF, "\r\n" }, - { CR, "\r" }, - { LF, "\n" }, -}; - static FILE *stream = NULL; static unsigned int stacked_vars = 0; static void **vars_list = NULL; static bool clean = false; +static bool clean_all = false; + static char *exclude = NULL; static const char *program_name; @@ -188,23 +181,31 @@ static void cleanup (void); static void free_color_names (struct color_name **); static void process_options (unsigned int, char **, bool *, const struct color **, const char **, FILE **); static void process_file_option (const char *, const char **, FILE **); -static void read_print_stream (bool, const struct color **, const char *, FILE *, enum stream_mode); +static void read_print_stream (bool, const struct color **, const char *, FILE *); static void find_color_entries (struct color_name **, const struct color **); -static void find_color_entry (const char *const, unsigned int, const struct color **); +static void find_color_entry (const struct color_name *, unsigned int, const struct color **); static void print_line (const struct color **, bool, const char * const, unsigned int); static void print_clean (const char *); static void print_free_offsets (const char *, char ***, unsigned int); static void *malloc_wrap (size_t); +static void *calloc_wrap (size_t, size_t); static void *realloc_wrap (void *, size_t); static void *malloc_wrap_debug (size_t, const char *, unsigned int); +static void *calloc_wrap_debug (size_t, size_t, const char *, unsigned int); static void *realloc_wrap_debug (void *, size_t, const char *, unsigned int); static void free_wrap (void **); static char *strdup_wrap (const char *); static char *str_concat (const char *, const char *); +static void vfprintf_diag (const char *, ...); static void vfprintf_fail (const char *, ...); static void stack_var (void ***, unsigned int *, unsigned int, void *); static void release_var (void **, unsigned int, void **); +#define SET_OPT_TYPE(type) \ + opt_type = type; \ + opt = 0; \ + goto PARSE_OPT; \ + extern char *optarg; extern int optind; @@ -213,13 +214,22 @@ main (int argc, char **argv) { unsigned int arg_cnt = 0; - int opt; + enum { + OPT_CLEAN = 1, + OPT_CLEAN_ALL, + OPT_EXCLUDE_RANDOM, + OPT_HELP, + OPT_VERSION + }; + + int opt, opt_type = 0; struct option long_opts[] = { - { "clean", no_argument, NULL, 'c' }, - { "exclude-random", required_argument, NULL, 'e' }, - { "help", no_argument, NULL, 'h' }, - { "version", no_argument, NULL, 'v' }, - { 0, 0, 0, 0 }, + { "clean", no_argument, &opt_type, OPT_CLEAN }, + { "clean-all", no_argument, &opt_type, OPT_CLEAN_ALL }, + { "exclude-random", required_argument, &opt_type, OPT_EXCLUDE_RANDOM }, + { "help", no_argument, &opt_type, OPT_HELP }, + { "version", no_argument, &opt_type, OPT_VERSION }, + { 0, 0, 0, 0 }, }; bool bold = false; @@ -231,8 +241,6 @@ main (int argc, char **argv) const char *file; - enum stream_mode mode = SCAN_FIRST; - program_name = argv[0]; atexit (cleanup); @@ -240,27 +248,42 @@ main (int argc, char **argv) while ((opt = getopt_long (argc, argv, "hv", long_opts, NULL)) != -1) { + PARSE_OPT: switch (opt) { - case 'c': - clean = true; - break; - case 'e': { - char *p; - exclude = xstrdup (optarg); - STACK_VAR (exclude); - for (p = exclude; *p; p++) - *p = tolower (*p); - if (streq (exclude, "random")) - vfprintf_fail (formats[FMT_GENERIC], "--exclude-random switch must be provided a color"); + case 0: /* long opts */ + switch (opt_type) + { + case OPT_CLEAN: + clean = true; + break; + case OPT_CLEAN_ALL: + clean_all = true; + break; + case OPT_EXCLUDE_RANDOM: { + char *p; + exclude = xstrdup (optarg); + STACK_VAR (exclude); + for (p = exclude; *p; p++) + *p = tolower (*p); + if (streq (exclude, "random")) + vfprintf_fail (formats[FMT_GENERIC], "--exclude-random switch must be provided a color"); + break; + } + case OPT_HELP: + print_help (); + exit (EXIT_SUCCESS); + case OPT_VERSION: + print_version (); + exit (EXIT_SUCCESS); + default: /* never reached */ + ABORT_TRACE (); + } break; - } case 'h': - print_help (); - exit (EXIT_SUCCESS); + SET_OPT_TYPE (OPT_HELP); case 'v': - print_version (); - exit (EXIT_SUCCESS); + SET_OPT_TYPE (OPT_VERSION); case '?': print_help (); exit (EXIT_FAILURE); @@ -271,10 +294,19 @@ main (int argc, char **argv) arg_cnt = argc - optind; - if (clean) + 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) - vfprintf_fail (formats[FMT_GENERIC], "--clean switch cannot be used with more than one file"); + { + 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 { @@ -285,11 +317,11 @@ main (int argc, char **argv) } } - if (clean) + if (clean || clean_all) process_file_option (argv[optind], &file, &stream); else process_options (arg_cnt, &argv[optind], &bold, colors, &file, &stream); - read_print_stream (bold, colors, file, stream, mode); + read_print_stream (bold, colors, file, stream); RELEASE_VAR (exclude); @@ -301,7 +333,7 @@ print_help (void) { unsigned int i; - printf ("Usage: %s (foreground) OR (foreground)%c(background) OR --clean [-|file]\n\n", program_name, COLOR_SEP_CHAR); + printf ("Usage: %s (foreground) OR (foreground)%c(background) OR --clean[-all] [-|file]\n\n", program_name, COLOR_SEP_CHAR); printf ("\tColors (foreground) (background)\n"); for (i = 0; i < tables[FOREGROUND].count; i++) { @@ -321,6 +353,7 @@ print_help (void) printf ("\n\tOptions\n"); printf ("\t\t --clean\n"); + printf ("\t\t --clean-all\n"); printf ("\t\t --exclude-random\n"); printf ("\t\t-h, --help\n"); printf ("\t\t-v, --version\n\n"); @@ -354,6 +387,7 @@ cleanup (void) for (i = 0; i < stacked_vars; i++) if (vars_list[i]) free_null (vars_list[i]); + free_null (vars_list); } } @@ -483,7 +517,7 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const } } - color_names[index] = xmalloc (sizeof (struct color_name)); + color_names[index] = xcalloc (1, sizeof (struct color_name)); color_names[index]->orig = xstrdup (color); @@ -514,7 +548,12 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const free_color_names (color_names); if (!colors[FOREGROUND]->code && colors[BACKGROUND] && colors[BACKGROUND]->code) - find_color_entry ("default", FOREGROUND, colors); + { + struct color_name color_name; + color_name.name = color_name.orig = "default"; + + find_color_entry (&color_name, FOREGROUND, colors); + } process_file_option (file_string, file, stream); } @@ -560,35 +599,24 @@ process_file_option (const char *file_string, const char **file, FILE **stream) assert (*stream); } -#define MERGE_PRINT_LINE(part_line, line, flags) do { \ - char *merged_line = NULL; \ - if (part_line) \ - { \ - merged_line = str_concat (part_line, line); \ - free_null (part_line); \ - } \ - print_line (colors, bold, merged_line ? merged_line : line, flags); \ - free (merged_line); \ +#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 (colors, bold, current_line, flags); \ + free (merged_line); \ } while (false); static void -read_print_stream (bool bold, const struct color **colors, const char *file, FILE *stream, enum stream_mode mode) +read_print_stream (bool bold, const struct color **colors, const char *file, FILE *stream) { char buf[BUF_SIZE], *part_line = NULL; unsigned int flags = 0; - bool first = false, always = false; - - switch (mode) - { - case SCAN_FIRST: - first = true; - break; - case SCAN_ALWAYS: - always = true; - break; - default: /* never reached */ - ABORT_TRACE (); - } while (!feof (stream)) { @@ -600,56 +628,31 @@ read_print_stream (bool bold, const struct color **colors, const char *file, FIL if (bytes_read != (BUF_SIZE - 1) && ferror (stream)) vfprintf_fail (formats[FMT_ERROR], BUF_SIZE - 1, "read"); line = buf; - LOOP: while ((eol = strpbrk (line, "\n\r"))) + while ((eol = strpbrk (line, "\n\r"))) { char *p; - if (first || always) + flags &= ~(CR|LF); + if (*eol == '\r') { - first = false; - flags &= ~(CR|LF); - if (*eol == '\r') - { - flags |= CR; - if (*(eol + 1) == '\n') - flags |= LF; - } - else if (*eol == '\n') + flags |= CR; + if (*(eol + 1) == '\n') flags |= LF; - else - vfprintf_fail (formats[FMT_FILE], file, "unrecognized line ending"); - } - if (always) - p = eol + SKIP_LINE_ENDINGS (flags); - else /* first */ - { - unsigned int i; - unsigned int count = sizeof (endings) / sizeof (struct ending); - for (i = 0; i < count; i++) - { - if (flags & endings[i].flags) - { - char *p; - if ((p = strstr (eol, endings[i].newline)) && p == eol) - break; - else - { - always = true; - goto LOOP; - } - } - } - p = eol + SKIP_LINE_ENDINGS (flags); } + else if (*eol == '\n') + flags |= LF; + else + vfprintf_fail (formats[FMT_FILE], file, "unrecognized line ending"); + p = eol + SKIP_LINE_ENDINGS (flags); *eol = '\0'; - MERGE_PRINT_LINE (part_line, line, flags); + MERGE_PRINT_LINE (part_line, line, flags, false); line = p; } if (feof (stream)) { - MERGE_PRINT_LINE (part_line, line, 0); + MERGE_PRINT_LINE (part_line, line, 0, true); } - else + else if (*line != '\0') { - if (!clean) /* efficiency */ + if (!clean && !clean_all) /* efficiency */ print_line (colors, bold, line, 0); else if (!part_line) part_line = xstrdup (line); @@ -707,12 +710,12 @@ find_color_entries (struct color_name **color_names, const struct color **colors colors[index] = (struct color *)&color_entries[i]; } else - find_color_entry (color_name, index, colors); + find_color_entry (color_names[index], index, colors); } } static void -find_color_entry (const char *const color_name, unsigned int index, const struct color **colors) +find_color_entry (const struct color_name *color_name, unsigned int index, const struct color **colors) { bool found = false; unsigned int i; @@ -721,24 +724,25 @@ find_color_entry (const char *const color_name, unsigned int index, const struct const struct color *const color_entries = tables[index].entries; for (i = 0; i < count; i++) - if (streq (color_name, color_entries[i].name)) + if (streq (color_name->name, color_entries[i].name)) { colors[index] = (struct color *)&color_entries[i]; found = true; break; } if (!found) - vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color_name, "not recognized"); + vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color_name->orig, "not recognized"); } static void print_line (const struct color **colors, bool bold, const char *const line, unsigned int flags) { - /* --clean */ - if (clean) + /* --clean[-all] */ + if (clean || clean_all) print_clean (line); else { + /* Foreground color code is guaranteed to be set when background color code is present. */ if (colors[BACKGROUND] && colors[BACKGROUND]->code) printf ("\033[%s", colors[BACKGROUND]->code); if (colors[FOREGROUND]->code) @@ -764,44 +768,69 @@ print_clean (const char *line) /* ESC[ */ if (*p == 27 && *(p + 1) == '[') { - bool check_values, first = true; const char *begin = p; p += 2; - if (!isdigit (*p)) - goto END; - do { - const char *digit; - check_values = false; - if (!first && !isdigit (*p)) - goto DISCARD; - digit = p; - while (isdigit (*p)) - p++; - if (p - digit > 2) - goto DISCARD; - else /* check range */ - { - char val[3]; - int value; - unsigned int i; - const unsigned int digits = p - digit; - for (i = 0; i < digits; i++) - val[i] = *digit++; - val[i] = '\0'; - value = atoi (val); - if (!((value >= 0 && value <= 8) /* attributes */ - || (value >= 30 && value <= 37) /* foreground colors */ - || (value >= 40 && value <= 47) /* background colors */ - || (value == 39 || value == 49))) /* default colors */ - goto DISCARD; - } - if (*p == ';') - { + if (clean_all) + { + while (isdigit (*p) || *p == ';') p++; - check_values = true; - } - first = false; - } while (check_values); + } + else if (clean) + { + bool check_values; + unsigned int iter = 0; + const char *digit; + do { + check_values = false; + iter++; + if (!isdigit (*p)) + goto DISCARD; + digit = p; + while (isdigit (*p)) + p++; + if (p - digit > 2) + goto DISCARD; + else /* check range */ + { + char val[3]; + int value; + unsigned int i; + const unsigned int digits = p - digit; + for (i = 0; i < digits; i++) + val[i] = *digit++; + val[i] = '\0'; + value = atoi (val); + if (value == 0) /* reset */ + { + if (iter > 1) + goto DISCARD; + goto END; + } + else if (value == 1) /* bold */ + { + bool discard = false; + if (iter > 1) + discard = true; + else if (*p != ';') + discard = true; + if (discard) + goto DISCARD; + p++; + check_values = true; + } + else if ((value >= 30 && value <= 37) || value == 39) /* foreground colors */ + goto END; + else if ((value >= 40 && value <= 47) || value == 49) /* background colors */ + { + if (iter > 1) + goto DISCARD; + goto END; + } + else + goto DISCARD; + } + } while (iter == 1 && check_values); + } END: if (*p == 'm') { const char *end = p++; @@ -871,6 +900,15 @@ malloc_wrap (size_t size) return p; } +static void * +calloc_wrap (size_t nmemb, size_t size) +{ + void *p = calloc (nmemb, size); + if (!p) + MEM_ALLOC_FAIL (); + return p; +} + static void * realloc_wrap (void *ptr, size_t size) { @@ -889,6 +927,15 @@ malloc_wrap_debug (size_t size, const char *file, unsigned int line) return p; } +static void * +calloc_wrap_debug (size_t nmemb, size_t size, const char *file, unsigned int line) +{ + void *p = calloc (nmemb, size); + if (!p) + MEM_ALLOC_FAIL_DEBUG (file, line); + return p; +} + static void * realloc_wrap_debug (void *ptr, size_t size, const char *file, unsigned int line) { @@ -930,15 +977,24 @@ str_concat (const char *str1, const char *str2) return str; } +#define DO_VFPRINTF(fmt) \ + va_list ap; \ + fprintf (stderr, "%s: ", program_name); \ + va_start (ap, fmt); \ + vfprintf (stderr, fmt, ap); \ + va_end (ap); \ + fprintf (stderr, "\n"); \ + +static void +vfprintf_diag (const char *fmt, ...) +{ + DO_VFPRINTF (fmt); +} + static void vfprintf_fail (const char *fmt, ...) { - va_list ap; - fprintf (stderr, "%s: ", program_name); - va_start (ap, fmt); - vfprintf (stderr, fmt, ap); - va_end (ap); - fprintf (stderr, "\n"); + DO_VFPRINTF (fmt); exit (EXIT_FAILURE); }