X-Git-Url: http://git.refcnt.org/?a=blobdiff_plain;f=colorize.c;h=abeb665f474bf2de57789a3b784686cadafbc543;hb=f18acf8d9ca24308161afa186a505cd74eea646e;hp=6c998ab6c5778a3a10eba3d74ddd3a2799fa40c8;hpb=0029079c8eb98e122286fb05f8fb798476a1b7a8;p=colorize.git diff --git a/colorize.c b/colorize.c index 6c998ab..abeb665 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-2014 Steven Schubiger + * Copyright (c) 2011-2015 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 @@ -21,6 +21,7 @@ #define _BSD_SOURCE #define _XOPEN_SOURCE 700 +#define _FILE_OFFSET_BITS 64 #include #include #include @@ -44,20 +45,23 @@ #define streq(s1, s2) (strcmp (s1, s2) == 0) -#if DEBUG -# 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__) +#if !DEBUG +# define xmalloc(size) malloc_wrap(size) +# define xcalloc(nmemb, size) calloc_wrap(nmemb, size) +# define xrealloc(ptr, size) realloc_wrap(ptr, size) +# define xstrdup(str) strdup_wrap(str, NULL, 0) +# define str_concat(str1, str2) str_concat_wrap(str1, str2, NULL, 0) #else -# define xmalloc(size) malloc_wrap(size) -# define xcalloc(nmemb, size) calloc_wrap(nmemb, size) -# define xrealloc(ptr, size) realloc_wrap(ptr, size) +# 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__) +# define xstrdup(str) strdup_wrap(str, __FILE__, __LINE__) +# define str_concat(str1, str2) str_concat_wrap(str1, str2, __FILE__, __LINE__) #endif #define free_null(ptr) free_wrap((void **)&ptr) -#define xstrdup(str) strdup_wrap(str) -#if BUF_SIZE <= 0 +#if defined(BUF_SIZE) && (BUF_SIZE <= 0 || BUF_SIZE > 65536) # undef BUF_SIZE #endif #ifndef BUF_SIZE @@ -79,14 +83,17 @@ release_var (vars_list, stacked_vars, (void **)&ptr); \ } while (false) -#define MEM_ALLOC_FAIL_DEBUG(file, line) do { \ - fprintf (stderr, "Memory allocation failure in source file %s, line %u\n", file, line); \ - exit (2); \ -} while (false) -#define MEM_ALLOC_FAIL() do { \ +#if !DEBUG +# define MEM_ALLOC_FAIL() do { \ fprintf (stderr, "%s: memory allocation failure\n", program_name); \ - exit (2); \ + exit (EXIT_FAILURE); \ } while (false) +#else +# define MEM_ALLOC_FAIL_DEBUG(file, line) do { \ + fprintf (stderr, "Memory allocation failure in source file %s, line %u\n", file, line); \ + exit (EXIT_FAILURE); \ +} while (false) +#endif #define ABORT_TRACE() \ fprintf (stderr, "Aborting in source file %s, line %u\n", __FILE__, __LINE__); \ @@ -99,7 +106,9 @@ #define COLOR_SEP_CHAR '/' -#define VERSION "0.53" +#define DEBUG_FILE "debug.txt" + +#define VERSION "0.56" typedef enum { false, true } bool; @@ -140,8 +149,14 @@ static const struct color bg_colors[] = { { "default", "49m" }, }; +struct bytes_size { + unsigned int size; + char unit; +}; + enum fmts { FMT_GENERIC, + FMT_STRING, FMT_QUOTE, FMT_COLOR, FMT_RANDOM, @@ -150,13 +165,14 @@ enum fmts { FMT_TYPE }; static const char *formats[] = { - "%s", /* generic */ - "%s `%s' %s", /* quote */ - "%s color '%s' %s", /* color */ - "%s color '%s' %s '%s'", /* random */ - "less than %u bytes %s", /* error */ - "%s: %s", /* file */ - "%s: %s: %s", /* type */ + "%s", /* generic */ + "%s '%s'", /* string */ + "%s `%s' %s", /* quote */ + "%s color '%s' %s", /* color */ + "%s color '%s' %s '%s'", /* random */ + "less than %lu bytes %s", /* error */ + "%s: %s", /* file */ + "%s: %s: %s", /* type */ }; enum { FOREGROUND, BACKGROUND }; @@ -170,15 +186,18 @@ static const struct { { bg_colors, sizeof (bg_colors) / sizeof (struct color), "background" }, }; -static FILE *stream = NULL; +static FILE *stream; +#if DEBUG +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; @@ -194,18 +213,33 @@ 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); static void print_clean (const char *); -static void print_free_offsets (const char *, char ***, unsigned int); +static bool is_esc (const char *); +static const char *get_end_of_esc (const char *); +static const char *get_end_of_text (const char *); +static void print_text (const char *, size_t); +static bool gather_esc_offsets (const char *, const char **, const char **); +static bool validate_esc_clean_all (const char **); +static bool validate_esc_clean (int, unsigned int, const char **, bool *); +static bool is_reset (int, unsigned int, const char **); +static bool is_bold (int, unsigned int, const char **); +static bool is_fg_color (int, const char **); +static bool is_bg_color (int, unsigned int, const char **); +#if !DEBUG static void *malloc_wrap (size_t); static void *calloc_wrap (size_t, size_t); static void *realloc_wrap (void *, size_t); +#else 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); +#endif static void free_wrap (void **); -static char *strdup_wrap (const char *); -static char *str_concat (const char *, const char *); +static char *strdup_wrap (const char *, const char *, unsigned int); +static char *str_concat_wrap (const char *, const char *, const char *, unsigned int); +static bool get_bytes_size (unsigned long, struct bytes_size *); static char *get_file_type (mode_t); static bool has_color_name (const char *, const char *); +static FILE *open_file (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 *); @@ -219,7 +253,7 @@ static void release_var (void **, unsigned int, void **); extern char *optarg; extern int optind; -static int opt_type = 0; +static int opt_type; int main (int argc, char **argv) @@ -258,7 +292,11 @@ main (int argc, char **argv) setvbuf (stdout, NULL, _IOLBF, 0); - while ((opt = getopt_long (argc, argv, "hv", long_opts, NULL)) != -1) +#if DEBUG + log = open_file (DEBUG_FILE, "w"); +#endif + + while ((opt = getopt_long (argc, argv, "hV", long_opts, NULL)) != -1) { PARSE_OPT: switch (opt) @@ -302,7 +340,7 @@ main (int argc, char **argv) break; case 'h': SET_OPT_TYPE (OPT_HELP); - case 'v': + case 'V': SET_OPT_TYPE (OPT_VERSION); case '?': print_hint (); @@ -383,13 +421,20 @@ print_help (void) 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"); + printf ("\t\t-V, --version\n\n"); } static void print_version (void) { +#ifdef HAVE_VERSION +# include "version.h" +#else + const char *version = NULL; +#endif + const char *version_prefix, *version_string; const char *c_flags; + struct bytes_size bytes_size; bool debug; #ifdef CFLAGS c_flags = to_str (CFLAGS); @@ -401,9 +446,21 @@ print_version (void) #else debug = false; #endif - printf ("%s v%s (compiled at %s, %s)\n", "colorize", VERSION, __DATE__, __TIME__); + version_prefix = version ? "" : "v"; + version_string = version ? version : VERSION; + printf ("colorize %s%s (compiled at %s, %s)\n", version_prefix, version_string, __DATE__, __TIME__); + printf ("Compiler flags: %s\n", c_flags); - printf ("Buffer size: %u bytes\n", BUF_SIZE); + if (get_bytes_size (BUF_SIZE, &bytes_size)) + { + if (BUF_SIZE % 1024 == 0) + printf ("Buffer size: %u%c\n", bytes_size.size, bytes_size.unit); + else + printf ("Buffer size: %u%c, %u byte%s\n", bytes_size.size, bytes_size.unit, + BUF_SIZE % 1024, BUF_SIZE % 1024 > 1 ? "s" : ""); + } + else + printf ("Buffer size: %lu byte%s\n", (unsigned long)BUF_SIZE, BUF_SIZE > 1 ? "s" : ""); printf ("Debugging: %s\n", debug ? "yes" : "no"); } @@ -414,13 +471,17 @@ cleanup (void) if (stream && fileno (stream) != STDIN_FILENO) fclose (stream); +#if DEBUG + if (log) + fclose (log); +#endif if (vars_list) { unsigned int i; for (i = 0; i < stacked_vars; i++) if (vars_list[i]) - free_null (vars_list[i]); + free (vars_list[i]); free_null (vars_list); } @@ -432,8 +493,8 @@ free_color_names (struct color_name **color_names) unsigned int i; for (i = 0; color_names[i]; i++) { - free_null (color_names[i]->name); - free_null (color_names[i]->orig); + free (color_names[i]->name); + free (color_names[i]->orig); free_null (color_names[i]); } } @@ -463,7 +524,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 != -1) + if (ret == 0) /* success */ { bool have_file; unsigned int c; @@ -499,14 +560,15 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct if (have_file) { + const char *file_exists = color_string; if (file_string) - vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), color_string, "cannot be used as color 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), color_string, "must be preceeded by color string"); + 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), color_string, "is not a valid file type"); + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "is not a valid file type"); } } } @@ -514,11 +576,11 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct if ((p = strchr (color_string, COLOR_SEP_CHAR))) { if (p == color_string) - vfprintf_fail (formats[FMT_GENERIC], "foreground color missing"); + vfprintf_fail (formats[FMT_STRING], "foreground color missing in string", color_string); else if (p == color_string + strlen (color_string) - 1) - vfprintf_fail (formats[FMT_GENERIC], "background color missing"); + vfprintf_fail (formats[FMT_STRING], "background color missing in string", color_string); else if (strchr (++p, COLOR_SEP_CHAR)) - vfprintf_fail (formats[FMT_GENERIC], "one color pair allowed only"); + vfprintf_fail (formats[FMT_STRING], "one color pair allowed only for string", color_string); } str = xstrdup (color_string); @@ -558,7 +620,6 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct break; case BACKGROUND: vfprintf_fail (formats[FMT_COLOR], tables[BACKGROUND].desc, color, "cannot be bold"); - break; default: /* never reached */ ABORT_TRACE (); } @@ -581,11 +642,11 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct if (color_names[BACKGROUND]) { unsigned int i; - unsigned int color_sets[2][2] = { { FOREGROUND, BACKGROUND }, { BACKGROUND, FOREGROUND } }; + const unsigned int color_sets[2][2] = { { FOREGROUND, BACKGROUND }, { BACKGROUND, FOREGROUND } }; for (i = 0; i < 2; i++) { - unsigned int color1 = color_sets[i][0]; - unsigned int color2 = color_sets[i][1]; + const unsigned int color1 = color_sets[i][0]; + const unsigned int color2 = color_sets[i][1]; if (CHECK_COLORS_RANDOM (color1, color2)) vfprintf_fail (formats[FMT_RANDOM], tables[color1].desc, color_names[color1]->orig, "cannot be combined with", color_names[color2]->orig); } @@ -614,13 +675,12 @@ process_file_arg (const char *file_string, const char **file, FILE **stream) *stream = stdin; else { - FILE *s; const char *file = file_string; struct stat sb; - int errno, ret; + int ret; errno = 0; - ret = lstat (file, &sb); + ret = stat (file, &sb); if (ret == -1) vfprintf_fail (formats[FMT_FILE], file, strerror (errno)); @@ -628,12 +688,7 @@ process_file_arg (const char *file_string, const char **file, FILE **stream) if (!VALID_FILE_TYPE (sb.st_mode)) vfprintf_fail (formats[FMT_TYPE], file, "unrecognized type", get_file_type (sb.st_mode)); - errno = 0; - - s = fopen (file, "r"); - if (!s) - vfprintf_fail (formats[FMT_FILE], file, strerror (errno)); - *stream = s; + *stream = open_file (file, "r"); } *file = file_string; } @@ -807,138 +862,167 @@ print_line (bool bold, const struct color **colors, const char *const line, unsi static void print_clean (const char *line) { - const char *p; - char ***offsets = NULL; - unsigned int count = 0, i = 0; + const char *p = line; - for (p = line; *p;) + if (is_esc (p)) + p = get_end_of_esc (p); + + while (*p != '\0') { - /* ESC[ */ - if (*p == 27 && *(p + 1) == '[') - { - const char *begin = p; - p += 2; - if (clean_all) - { - while (isdigit (*p) || *p == ';') - p++; - } - 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++; - if (!offsets) - offsets = xmalloc (++count * sizeof (char **)); - else - offsets = xrealloc (offsets, ++count * sizeof (char **)); - offsets[i] = xmalloc (2 * sizeof (char *)); - offsets[i][0] = (char *)begin; /* ESC */ - offsets[i][1] = (char *)end; /* m */ - i++; - continue; - } - DISCARD: - continue; - } - p++; + const char *text_start = p; + const char *text_end = get_end_of_text (p); + print_text (text_start, text_end - text_start); + p = get_end_of_esc (text_end); } +} - if (offsets) - print_free_offsets (line, offsets, count); - else - printf (formats[FMT_GENERIC], line); +static bool +is_esc (const char *p) +{ + return gather_esc_offsets (p, NULL, NULL); } -#define SET_CHAR(offset, new, old) \ - *old = *offset; \ - *offset = new; \ +static const char * +get_end_of_esc (const char *p) +{ + const char *esc; + const char *end = NULL; + while ((esc = strchr (p, '\033'))) + { + if (gather_esc_offsets (esc, NULL, &end)) + break; + p = esc + 1; + } + return end ? end + 1 : p + strlen (p); +} -#define RESTORE_CHAR(offset, old) \ - *offset = old; \ +static const char * +get_end_of_text (const char *p) +{ + const char *esc; + const char *start = NULL; + while ((esc = strchr (p, '\033'))) + { + if (gather_esc_offsets (esc, &start, NULL)) + break; + p = esc + 1; + } + return start ? start : p + strlen (p); +} static void -print_free_offsets (const char *line, char ***offsets, unsigned int count) +print_text (const char *p, size_t len) { - char ch; - unsigned int i; - - SET_CHAR (offsets[0][0], '\0', &ch); - printf (formats[FMT_GENERIC], line); - RESTORE_CHAR (offsets[0][0], ch); + size_t bytes_written; + bytes_written = fwrite (p, 1, len, stdout); + if (bytes_written != len) + vfprintf_fail (formats[FMT_ERROR], (unsigned long)len, "written"); +} - for (i = 0; i < count; i++) +static bool +gather_esc_offsets (const char *p, const char **start, const char **end) +{ + /* ESC[ */ + if (*p == 27 && *(p + 1) == '[') { - char ch; - bool next_offset = false; - if (i + 1 < count) + bool valid = false; + const char *begin = p; + p += 2; + if (clean_all) + valid = validate_esc_clean_all (&p); + else if (clean) + { + bool check_values; + unsigned int iter = 0; + const char *digit; + do { + check_values = false; + iter++; + if (!isdigit (*p)) + break; + digit = p; + while (isdigit (*p)) + p++; + if (p - digit > 2) + break; + 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); + valid = validate_esc_clean (value, iter, &p, &check_values); + } + } while (check_values); + } + if (valid) { - SET_CHAR (offsets[i + 1][0], '\0', &ch); - next_offset = true; + if (start) + *start = begin; + if (end) + *end = p; + return true; } - printf (formats[FMT_GENERIC], offsets[i][1] + 1); - if (next_offset) - RESTORE_CHAR (offsets[i + 1][0], ch); } - for (i = 0; i < count; i++) - free_null (offsets[i]); - free_null (offsets); + return false; +} + +static bool +validate_esc_clean_all (const char **p) +{ + while (isdigit (**p) || **p == ';') + (*p)++; + return (**p == 'm'); +} + +static bool +validate_esc_clean (int value, unsigned int iter, const char **p, bool *check_values) +{ + if (is_reset (value, iter, p)) + return true; + else if (is_bold (value, iter, p)) + { + (*p)++; + *check_values = true; + return false; /* partial escape sequence, need another valid value */ + } + else if (is_fg_color (value, p)) + return true; + else if (is_bg_color (value, iter, p)) + return true; + else + return false; +} + +static bool +is_reset (int value, unsigned int iter, const char **p) +{ + return (value == 0 && iter == 1 && **p == 'm'); +} + +static bool +is_bold (int value, unsigned int iter, const char **p) +{ + return (value == 1 && iter == 1 && **p == ';'); +} + +static bool +is_fg_color (int value, const char **p) +{ + return (((value >= 30 && value <= 37) || value == 39) && **p == 'm'); +} + +static bool +is_bg_color (int value, unsigned int iter, const char **p) +{ + return (((value >= 40 && value <= 47) || value == 49) && iter == 1 && **p == 'm'); } +#if !DEBUG static void * malloc_wrap (size_t size) { @@ -965,13 +1049,14 @@ realloc_wrap (void *ptr, size_t size) MEM_ALLOC_FAIL (); return p; } - +#else static void * malloc_wrap_debug (size_t size, const char *file, unsigned int line) { void *p = malloc (size); if (!p) MEM_ALLOC_FAIL_DEBUG (file, line); + fprintf (log, "%s: malloc'ed %lu bytes [source file %s, line %u]\n", program_name, (unsigned long)size, file, line); return p; } @@ -981,6 +1066,7 @@ calloc_wrap_debug (size_t nmemb, size_t size, const char *file, unsigned int lin void *p = calloc (nmemb, size); if (!p) MEM_ALLOC_FAIL_DEBUG (file, line); + fprintf (log, "%s: calloc'ed %lu bytes [source file %s, line %u]\n", program_name, (unsigned long)(nmemb * size), file, line); return p; } @@ -990,8 +1076,10 @@ realloc_wrap_debug (void *ptr, size_t size, const char *file, unsigned int line) void *p = realloc (ptr, size); if (!p) MEM_ALLOC_FAIL_DEBUG (file, line); + fprintf (log, "%s: realloc'ed %lu bytes [source file %s, line %u]\n", program_name, (unsigned long)size, file, line); return p; } +#endif /* !DEBUG */ static void free_wrap (void **ptr) @@ -1000,22 +1088,28 @@ free_wrap (void **ptr) *ptr = NULL; } +#if !DEBUG +# define do_malloc(len, file, line) malloc_wrap(len) +#else +# define do_malloc(len, file, line) malloc_wrap_debug(len, file, line) +#endif + static char * -strdup_wrap (const char *str) +strdup_wrap (const char *str, const char *file, unsigned int line) { const size_t len = strlen (str) + 1; - char *p = xmalloc (len); + char *p = do_malloc (len, file, line); strncpy (p, str, len); return p; } static char * -str_concat (const char *str1, const char *str2) +str_concat_wrap (const char *str1, const char *str2, const char *file, unsigned int line) { const size_t len = strlen (str1) + strlen (str2) + 1; char *p, *str; - p = str = xmalloc (len); + p = str = do_malloc (len, file, line); strncpy (p, str1, strlen (str1)); p += strlen (str1); strncpy (p, str2, strlen (str2)); @@ -1025,6 +1119,24 @@ str_concat (const char *str1, const char *str2) return str; } +static bool +get_bytes_size (unsigned long bytes, struct bytes_size *bytes_size) +{ + const char *unit, units[] = { '0', 'K', 'M', 'G', '\0' }; + unsigned long size = bytes; + if (bytes < 1024) + return false; + unit = units; + while (size >= 1024 && *(unit + 1)) + { + size /= 1024; + unit++; + } + bytes_size->size = (unsigned int)size; + bytes_size->unit = *unit; + return true; +} + static char * get_file_type (mode_t mode) { @@ -1063,6 +1175,19 @@ has_color_name (const char *str, const char *name) return true; } +static FILE * +open_file (const char *file, const char *mode) +{ + FILE *stream; + + errno = 0; + stream = fopen (file, mode); + if (!stream) + vfprintf_fail (formats[FMT_FILE], file, strerror (errno)); + + return stream; +} + #define DO_VFPRINTF(fmt) \ va_list ap; \ fprintf (stderr, "%s: ", program_name); \