* 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
*
*/
-#define _POSIX_C_SOURCE 200809L
+#define _BSD_SOURCE
+#define _XOPEN_SOURCE 700
#include <assert.h>
#include <ctype.h>
#include <errno.h>
#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__)
-#else
+#if !DEBUG
# define xmalloc(size) malloc_wrap(size)
# define xcalloc(nmemb, size) calloc_wrap(nmemb, size)
# define xrealloc(ptr, size) realloc_wrap(ptr, size)
+#else
+# 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__)
#endif
#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 || BUF_SIZE > 65536
# undef BUF_SIZE
-# define BUF_SIZE 4096 + 1
+#endif
+#ifndef BUF_SIZE
+# define BUF_SIZE 4096
#endif
#define LF 0x01
#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);
-#define MEM_ALLOC_FAIL() do { \
+#if !DEBUG
+# define MEM_ALLOC_FAIL() do { \
fprintf (stderr, "%s: memory allocation failure\n", program_name); \
exit (2); \
-} while (false);
+} 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 (2); \
+} while (false)
+#endif
#define ABORT_TRACE() \
fprintf (stderr, "Aborting in source file %s, line %u\n", __FILE__, __LINE__); \
#define COLOR_SEP_CHAR '/'
-#define VERSION "0.51"
-
-typedef unsigned short bool;
+#define VERSION "0.54"
-enum { false, true };
+typedef enum { false, true } bool;
struct color_name {
char *name;
{ "green", "32m" },
{ "yellow", "33m" },
{ "blue", "34m" },
- { "cyan", "35m" },
- { "magenta", "36m" },
+ { "magenta", "35m" },
+ { "cyan", "36m" },
{ "white", "37m" },
{ "default", "39m" },
};
{ "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 };
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 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 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 void vfprintf_diag (const char *, ...);
static void vfprintf_fail (const char *, ...);
extern char *optarg;
extern int optind;
+static int opt_type = 0;
+
int
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;
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:
case 'v':
SET_OPT_TYPE (OPT_VERSION);
case '?':
- print_help ();
+ print_hint ();
exit (EXIT_FAILURE);
default: /* never reached */
ABORT_TRACE ();
{
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);
}
}
exit (EXIT_SUCCESS);
}
+static void
+print_hint (void)
+{
+ fprintf (stderr, "Type `%s --help' for help screen.\n", program_name);
+}
+
static void
print_help (void)
{
static void
print_version (void)
{
+#ifdef HAVE_VERSION
+# include "version.h"
+#else
+ const char *version = NULL;
+#endif
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
+ if (version)
+ printf ("colorize %s (compiled at %s, %s)\n", version, __DATE__, __TIME__);
+ else
+ printf ("colorize v%s (compiled at %s, %s)\n", VERSION, __DATE__, __TIME__);
printf ("Compiler flags: %s\n", c_flags);
- printf ("Buffer size: %u bytes\n", BUF_SIZE - 1);
+ 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
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. */
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++)
{
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);
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");
+ if (!VALID_FILE_TYPE (sb.st_mode))
+ vfprintf_fail (formats[FMT_TYPE], file, "unrecognized type", get_file_type (sb.st_mode));
errno = 0;
if (!check_eof || *current_line != '\0') \
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))
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")))
{
free_null (offsets);
}
+#if !DEBUG
static void *
malloc_wrap (size_t size)
{
MEM_ALLOC_FAIL ();
return p;
}
-
+#else
static void *
malloc_wrap_debug (size_t size, const char *file, unsigned int line)
{
MEM_ALLOC_FAIL_DEBUG (file, line);
return p;
}
+#endif
static void
free_wrap (void **ptr)
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)
{