X-Git-Url: http://git.refcnt.org/?a=blobdiff_plain;f=colorize.c;h=6c998ab6c5778a3a10eba3d74ddd3a2799fa40c8;hb=0029079c8eb98e122286fb05f8fb798476a1b7a8;hp=a25ec59b19636b0e64eca49d2fe615406f3c18a8;hpb=d735e9ff2fe6f739365b4f0b17c8512ab2ca8ae2;p=colorize.git diff --git a/colorize.c b/colorize.c index a25ec59..6c998ab 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-2013 Steven Schubiger + * Copyright (c) 2011-2014 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 @@ -19,7 +19,8 @@ * */ -#define _POSIX_SOURCE +#define _BSD_SOURCE +#define _XOPEN_SOURCE 700 #include #include #include @@ -56,9 +57,11 @@ #define free_null(ptr) free_wrap((void **)&ptr) #define xstrdup(str) strdup_wrap(str) -#if !defined BUF_SIZE || BUF_SIZE <= 0 +#if BUF_SIZE <= 0 # undef BUF_SIZE -# define BUF_SIZE 4096 + 1 +#endif +#ifndef BUF_SIZE +# define BUF_SIZE 4096 #endif #define LF 0x01 @@ -66,22 +69,24 @@ #define SKIP_LINE_ENDINGS(flags) (((flags) & CR) && ((flags) & LF) ? 2 : 1) +#define VALID_FILE_TYPE(mode) (S_ISREG (mode) || S_ISLNK (mode) || S_ISFIFO (mode)) + #define STACK_VAR(ptr) do { \ stack_var (&vars_list, &stacked_vars, stacked_vars, ptr); \ -} while (false); +} while (false) #define RELEASE_VAR(ptr) do { \ release_var (vars_list, stacked_vars, (void **)&ptr); \ -} while (false); +} 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); +} while (false) #define MEM_ALLOC_FAIL() do { \ fprintf (stderr, "%s: memory allocation failure\n", program_name); \ exit (2); \ -} while (false); +} while (false) #define ABORT_TRACE() \ fprintf (stderr, "Aborting in source file %s, line %u\n", __FILE__, __LINE__); \ @@ -94,11 +99,9 @@ #define COLOR_SEP_CHAR '/' -#define VERSION "0.50" +#define VERSION "0.53" -typedef unsigned short bool; - -enum { false, true }; +typedef enum { false, true } bool; struct color_name { char *name; @@ -119,8 +122,8 @@ static const struct color fg_colors[] = { { "green", "32m" }, { "yellow", "33m" }, { "blue", "34m" }, - { "cyan", "35m" }, - { "magenta", "36m" }, + { "magenta", "35m" }, + { "cyan", "36m" }, { "white", "37m" }, { "default", "39m" }, }; @@ -131,25 +134,29 @@ static const struct color bg_colors[] = { { "green", "42m" }, { "yellow", "43m" }, { "blue", "44m" }, - { "cyan", "45m" }, - { "magenta", "46m" }, + { "magenta", "45m" }, + { "cyan", "46m" }, { "white", "47m" }, { "default", "49m" }, }; enum fmts { FMT_GENERIC, + FMT_QUOTE, FMT_COLOR, FMT_RANDOM, FMT_ERROR, - FMT_FILE + FMT_FILE, + 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 */ }; enum { FOREGROUND, BACKGROUND }; @@ -175,16 +182,17 @@ static char *exclude = NULL; static const char *program_name; +static void print_hint (void); static void print_help (void); static void print_version (void); 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 process_args (unsigned int, char **, bool *, const struct color **, const char **, FILE **); +static void process_file_arg (const char *, const char **, FILE **); 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 struct color_name *, unsigned int, const struct color **); -static void print_line (const struct color **, bool, const char * const, unsigned int); +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 void *malloc_wrap (size_t); @@ -196,6 +204,8 @@ 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 char *get_file_type (mode_t); +static bool has_color_name (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 *); @@ -209,6 +219,8 @@ static void release_var (void **, unsigned int, void **); extern char *optarg; extern int optind; +static int opt_type = 0; + int main (int argc, char **argv) { @@ -222,14 +234,14 @@ main (int argc, char **argv) OPT_VERSION }; - int opt, opt_type = 0; + int opt; struct option long_opts[] = { { "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 }, + { NULL, 0, NULL, 0 }, }; bool bold = false; @@ -239,7 +251,7 @@ main (int argc, char **argv) NULL, /* background */ }; - const char *file; + const char *file = NULL; program_name = argv[0]; atexit (cleanup); @@ -261,13 +273,21 @@ main (int argc, char **argv) clean_all = true; break; case OPT_EXCLUDE_RANDOM: { - char *p; + bool valid = false; + unsigned int i; 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"); + for (i = 1; i < tables[FOREGROUND].count - 1; i++) /* skip color none and default */ + { + const struct color *entry = &tables[FOREGROUND].entries[i]; + if (streq (exclude, entry->name)) + { + valid = true; + break; + } + } + if (!valid) + vfprintf_fail (formats[FMT_GENERIC], "--exclude-random switch must be provided a plain color"); break; } case OPT_HELP: @@ -285,7 +305,7 @@ main (int argc, char **argv) case 'v': SET_OPT_TYPE (OPT_VERSION); case '?': - print_help (); + print_hint (); exit (EXIT_FAILURE); default: /* never reached */ ABORT_TRACE (); @@ -312,15 +332,16 @@ main (int argc, char **argv) { if (arg_cnt == 0 || arg_cnt > 2) { - print_help (); + 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_option (argv[optind], &file, &stream); + process_file_arg (argv[optind], &file, &stream); else - process_options (arg_cnt, &argv[optind], &bold, colors, &file, &stream); + process_args (arg_cnt, &argv[optind], &bold, colors, &file, &stream); read_print_stream (bold, colors, file, stream); RELEASE_VAR (exclude); @@ -328,6 +349,12 @@ main (int argc, char **argv) exit (EXIT_SUCCESS); } +static void +print_hint (void) +{ + fprintf (stderr, "Type `%s --help' for help screen.\n", program_name); +} + static void print_help (void) { @@ -363,14 +390,21 @@ static void print_version (void) { const char *c_flags; - printf ("%s v%s (compiled at %s, %s)\n", "colorize", VERSION, __DATE__, __TIME__); + bool debug; #ifdef CFLAGS c_flags = to_str (CFLAGS); #else c_flags = "unknown"; #endif +#if DEBUG + debug = true; +#else + debug = false; +#endif + printf ("%s v%s (compiled at %s, %s)\n", "colorize", VERSION, __DATE__, __TIME__); printf ("Compiler flags: %s\n", c_flags); - printf ("Buffer size: %u bytes\n", BUF_SIZE - 1); + printf ("Buffer size: %u bytes\n", BUF_SIZE); + printf ("Debugging: %s\n", debug ? "yes" : "no"); } static void @@ -405,15 +439,15 @@ free_color_names (struct color_name **color_names) } static void -process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const struct color **colors, const char **file, FILE **stream) +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; struct stat sb; - const char *color_string = arg_cnt >= 1 ? option_strings[0] : NULL; - const char *file_string = arg_cnt == 2 ? option_strings[1] : NULL; + const char *color_string = arg_cnt >= 1 ? arg_strings[0] : NULL; + const char *file_string = arg_cnt == 2 ? arg_strings[1] : NULL; assert (color_string); @@ -425,7 +459,7 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const vfprintf_fail (formats[FMT_GENERIC], "hyphen must be preceeded by color string"); } - ret = stat (color_string, &sb); + ret = lstat (color_string, &sb); /* Ensure that we don't fail if there's a file with one or more color names in its path. */ @@ -434,6 +468,7 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const 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++) { @@ -442,14 +477,18 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const for (i = 0; i < tables[FOREGROUND].count; i++) { const struct color *entry = &tables[FOREGROUND].entries[i]; - char *p; - if ((p = strstr (color, entry->name)) && p == color) + if (has_color_name (color, entry->name)) { - color = p + strlen (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 @@ -461,9 +500,14 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const if (have_file) { if (file_string) - vfprintf_fail (formats[FMT_GENERIC], "file cannot be used as color string"); + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), color_string, "cannot be used as color string"); else - vfprintf_fail (formats[FMT_GENERIC], "file must be preceeded by color string"); + { + if (VALID_FILE_TYPE (mode)) + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), color_string, "must be preceeded by color string"); + else + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), color_string, "is not a valid file type"); + } } } @@ -483,6 +527,8 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const for (index = 0, color = str; *color; index++, color = p) { char *ch, *sep; + + p = NULL; if ((sep = strchr (color, COLOR_SEP_CHAR))) { *sep = '\0'; @@ -490,6 +536,7 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const } else p = color + strlen (color); + assert (p); for (ch = color; *ch; ch++) if (!isalpha (*ch)) @@ -555,11 +602,11 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const find_color_entry (&color_name, FOREGROUND, colors); } - process_file_option (file_string, file, stream); + process_file_arg (file_string, file, stream); } static void -process_file_option (const char *file_string, const char **file, FILE **stream) +process_file_arg (const char *file_string, const char **file, FILE **stream) { if (file_string) { @@ -573,13 +620,13 @@ process_file_option (const char *file_string, const char **file, FILE **stream) int errno, ret; errno = 0; - ret = stat (file, &sb); + ret = lstat (file, &sb); if (ret == -1) vfprintf_fail (formats[FMT_FILE], file, strerror (errno)); - if (!(S_ISREG (sb.st_mode) || S_ISLNK (sb.st_mode) || S_ISFIFO (sb.st_mode))) - vfprintf_fail (formats[FMT_FILE], file, "unrecognized file type"); + if (!VALID_FILE_TYPE (sb.st_mode)) + vfprintf_fail (formats[FMT_TYPE], file, "unrecognized type", get_file_type (sb.st_mode)); errno = 0; @@ -597,6 +644,7 @@ process_file_option (const char *file_string, const char **file, FILE **stream) } assert (*stream); + assert (*file); } #define MERGE_PRINT_LINE(part_line, line, flags, check_eof) do { \ @@ -608,14 +656,14 @@ process_file_option (const char *file_string, const char **file, FILE **stream) } \ current_line = merged_line ? merged_line : (char *)line; \ if (!check_eof || *current_line != '\0') \ - print_line (colors, bold, current_line, flags); \ + print_line (bold, colors, current_line, flags); \ free (merged_line); \ -} while (false); +} while (false) static void read_print_stream (bool bold, const struct color **colors, const char *file, FILE *stream) { - char buf[BUF_SIZE], *part_line = NULL; + char buf[BUF_SIZE + 1], *part_line = NULL; unsigned int flags = 0; while (!feof (stream)) @@ -623,10 +671,10 @@ read_print_stream (bool bold, const struct color **colors, const char *file, FIL size_t bytes_read; char *eol; const char *line; - memset (buf, '\0', BUF_SIZE); - bytes_read = fread (buf, 1, BUF_SIZE - 1, stream); - if (bytes_read != (BUF_SIZE - 1) && ferror (stream)) - vfprintf_fail (formats[FMT_ERROR], BUF_SIZE - 1, "read"); + memset (buf, '\0', BUF_SIZE + 1); + bytes_read = fread (buf, 1, BUF_SIZE, stream); + if (bytes_read != BUF_SIZE && ferror (stream)) + vfprintf_fail (formats[FMT_ERROR], BUF_SIZE, "read"); line = buf; while ((eol = strpbrk (line, "\n\r"))) { @@ -653,7 +701,7 @@ read_print_stream (bool bold, const struct color **colors, const char *file, FIL else if (*line != '\0') { if (!clean && !clean_all) /* efficiency */ - print_line (colors, bold, line, 0); + print_line (bold, colors, line, 0); else if (!part_line) part_line = xstrdup (line); else @@ -735,7 +783,7 @@ find_color_entry (const struct color_name *color_name, unsigned int index, const } static void -print_line (const struct color **colors, bool bold, const char *const line, unsigned int flags) +print_line (bool bold, const struct color **colors, const char *const line, unsigned int flags) { /* --clean[-all] */ if (clean || clean_all) @@ -977,6 +1025,44 @@ str_concat (const char *str1, const char *str2) return str; } +static char * +get_file_type (mode_t mode) +{ + if (S_ISREG (mode)) + return "file"; + else if (S_ISDIR (mode)) + return "directory"; + else if (S_ISCHR (mode)) + return "character device"; + else if (S_ISBLK (mode)) + return "block device"; + else if (S_ISFIFO (mode)) + return "named pipe"; + else if (S_ISLNK (mode)) + return "symbolic link"; + else if (S_ISSOCK (mode)) + return "socket"; + else + return "file"; +} + +static bool +has_color_name (const char *str, const char *name) +{ + char *p; + + assert (strlen (str)); + assert (strlen (name)); + + if (!(*str == *name || *str == toupper (*name))) + return false; + else if (*(name + 1) != '\0' + && !((p = strstr (str + 1, name + 1)) && p == str + 1)) + return false; + + return true; +} + #define DO_VFPRINTF(fmt) \ va_list ap; \ fprintf (stderr, "%s: ", program_name); \