X-Git-Url: http://git.refcnt.org/?a=blobdiff_plain;ds=sidebyside;f=colorize.c;h=cc89193ac8db8f5c70d283d704c10bd203ac7874;hb=cc3c8db022b248d3b862757379b4df529d07b6c1;hp=793da296455f6c02d1dc60c023b3a793d3a329b5;hpb=6df8f3534b2f5a99c43fdeefacf33b439591cccc;p=colorize.git diff --git a/colorize.c b/colorize.c index 793da29..cc89193 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-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 @@ -19,7 +19,9 @@ * */ -#define _POSIX_C_SOURCE 200809L +#define _BSD_SOURCE +#define _XOPEN_SOURCE 700 +#define _FILE_OFFSET_BITS 64 #include #include #include @@ -43,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 <= 1 /* BUF_SIZE - 1 */ +#if defined(BUF_SIZE) && (BUF_SIZE <= 0 || BUF_SIZE > 65536) # undef BUF_SIZE #endif #ifndef BUF_SIZE @@ -68,6 +73,8 @@ #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) @@ -76,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__); \ @@ -96,11 +106,11 @@ #define COLOR_SEP_CHAR '/' -#define VERSION "0.52" +#define DEBUG_FILE "debug.txt" -typedef unsigned short bool; +#define VERSION "0.55" -enum { false, true }; +typedef enum { false, true } bool; struct color_name { char *name; @@ -121,8 +131,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" }, }; @@ -133,25 +143,36 @@ static const struct color bg_colors[] = { { "green", "42m" }, { "yellow", "43m" }, { "blue", "44m" }, - { "cyan", "45m" }, - { "magenta", "46m" }, + { "magenta", "45m" }, + { "cyan", "46m" }, { "white", "47m" }, { "default", "49m" }, }; +struct bytes_size { + unsigned int size; + char unit; +}; + enum fmts { FMT_GENERIC, + FMT_STRING, + FMT_QUOTE, FMT_COLOR, FMT_RANDOM, FMT_ERROR, - FMT_FILE + FMT_FILE, + FMT_TYPE }; static const char *formats[] = { "%s", /* generic */ + "%s '%s'", /* string */ + "%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 }; @@ -166,6 +187,9 @@ static const struct { }; static FILE *stream = NULL; +#if DEBUG +static FILE *log = NULL; +#endif static unsigned int stacked_vars = 0; static void **vars_list = NULL; @@ -177,6 +201,7 @@ 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); @@ -189,16 +214,22 @@ static void find_color_entry (const struct color_name *, unsigned int, const str 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); +#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 *); @@ -212,6 +243,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) { @@ -225,7 +258,7 @@ 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 }, @@ -249,6 +282,10 @@ main (int argc, char **argv) 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: @@ -296,7 +333,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 (); @@ -323,8 +360,8 @@ main (int argc, char **argv) { if (arg_cnt == 0 || arg_cnt > 2) { - vfprintf_diag ("%u arguments provided, expected 1-2 arguments or option", arg_cnt); - print_help (); + vfprintf_diag ("%u arguments provided, expected 1-2 arguments or clean option", arg_cnt); + print_hint (); exit (EXIT_FAILURE); } } @@ -340,6 +377,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) { @@ -374,15 +417,41 @@ print_help (void) 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; - printf ("%s v%s (compiled at %s, %s)\n", "colorize", VERSION, __DATE__, __TIME__); + struct bytes_size bytes_size; + bool debug; #ifdef CFLAGS c_flags = to_str (CFLAGS); #else c_flags = "unknown"; #endif +#if DEBUG + debug = true; +#else + debug = false; +#endif + 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"); } static void @@ -392,6 +461,10 @@ cleanup (void) if (stream && fileno (stream) != STDIN_FILENO) fclose (stream); +#if DEBUG + if (log) + fclose (log); +#endif if (vars_list) { @@ -437,15 +510,16 @@ process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct 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. */ - if (ret != -1) + 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++) { @@ -476,21 +550,27 @@ 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_GENERIC], "file cannot be used as color string"); + vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "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), 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"); + } } } 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); @@ -586,26 +666,20 @@ 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 = 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"); - - errno = 0; + if (!VALID_FILE_TYPE (sb.st_mode)) + vfprintf_fail (formats[FMT_TYPE], file, "unrecognized type", get_file_type (sb.st_mode)); - s = fopen (file, "r"); - if (!s) - vfprintf_fail (formats[FMT_FILE], file, strerror (errno)); - *stream = s; + *stream = open_file (file, "r"); } *file = file_string; } @@ -635,7 +709,7 @@ process_file_arg (const char *file_string, const char **file, FILE **stream) 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)) @@ -643,10 +717,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"))) { @@ -911,6 +985,7 @@ print_free_offsets (const char *line, char ***offsets, unsigned int count) free_null (offsets); } +#if !DEBUG static void * malloc_wrap (size_t size) { @@ -937,13 +1012,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; } @@ -953,6 +1029,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; } @@ -962,8 +1039,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) @@ -972,22 +1051,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)); @@ -997,6 +1082,45 @@ 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) +{ + 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) { @@ -1014,6 +1138,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); \