aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorGravatar Steven Schubiger <stsc@refcnt.org> 2013-02-07 23:50:09 +0100
committerGravatar Steven Schubiger <stsc@refcnt.org> 2013-02-07 23:50:09 +0100
commit5b543402df3ecb2e5c5cb973ee4951d22f9cd52c (patch)
treeca6011873be4aa9f1119ed952d601574f8028688
parent0802a010b347abd0172beb4411cd2b6af946f73b (diff)
downloadcolorize-5b543402df3ecb2e5c5cb973ee4951d22f9cd52c.tar.gz
colorize-5b543402df3ecb2e5c5cb973ee4951d22f9cd52c.tar.bz2
Clean text from color escape sequences
In order to handle end-of-buffer reads with partial line chunks, merge those when cleaning text. Pass each line then to the offset extracting function which gathers begin/end offsets of escape sequences. Finally, for printing cleaned lines of text, omit all color escape sequences as defined by their set of offsets (and restore characters temporarily overwritten with terminating NUL). Furthermore, add a basic string concatenation function and wrap the free memory function to have it nullify pointers.
-rw-r--r--colorize.c242
1 files changed, 213 insertions, 29 deletions
diff --git a/colorize.c b/colorize.c
index 2f02bf4..661a550 100644
--- a/colorize.c
+++ b/colorize.c
@@ -43,6 +43,7 @@
#define xmalloc(size) malloc_wrap(size, __FILE__, __LINE__)
#define xrealloc(ptr, size) realloc_wrap(ptr, size, __FILE__, __LINE__)
+#define free_null(ptr) free_wrap((void **)&ptr )
#define xstrdup(str) strdup_wrap(str, __FILE__, __LINE__)
#if !defined BUF_SIZE || BUF_SIZE <= 0
@@ -171,6 +172,7 @@ static FILE *stream = NULL;
static unsigned int stacked_vars = 0;
static void **vars_list = NULL;
+static bool clean = false;
static char *exclude = NULL;
static const char *program_name;
@@ -180,13 +182,18 @@ 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 read_print_stream (bool, const struct color **, const char *, FILE *, enum stream_mode);
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 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, const char *, unsigned int);
static void *realloc_wrap (void *, size_t, const char *, unsigned int);
+static void free_wrap (void **);
static char *strdup_wrap (const char *, const char *, unsigned int);
+static char *str_concat (const char *, 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 **);
@@ -199,10 +206,9 @@ main (int argc, char **argv)
{
unsigned int arg_cnt = 0;
- bool invalid_opt = false;
-
int opt;
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' },
@@ -229,6 +235,9 @@ main (int argc, char **argv)
{
switch (opt)
{
+ case 'c':
+ clean = true;
+ break;
case 'e': {
char *p;
exclude = xstrdup (optarg);
@@ -246,8 +255,8 @@ main (int argc, char **argv)
print_version ();
exit (EXIT_SUCCESS);
case '?':
- invalid_opt = true;
- break;
+ print_help ();
+ exit (EXIT_FAILURE);
default: /* never reached */
ABORT_TRACE ();
}
@@ -255,13 +264,24 @@ main (int argc, char **argv)
arg_cnt = argc - optind;
- if (arg_cnt == 0 || arg_cnt > 2 || invalid_opt)
+ if (clean)
{
- print_help ();
- exit (EXIT_FAILURE);
+ if (arg_cnt > 1)
+ vfprintf_fail (formats[FMT_GENERIC], "--clean switch cannot be used with more than one file");
+ }
+ else
+ {
+ if (arg_cnt == 0 || arg_cnt > 2)
+ {
+ print_help ();
+ exit (EXIT_FAILURE);
+ }
}
- process_options (arg_cnt, &argv[optind], &bold, colors, &file, &stream);
+ if (clean)
+ 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);
RELEASE_VAR (exclude);
@@ -274,7 +294,7 @@ print_help (void)
{
unsigned int i;
- printf ("Usage: %s (foreground) OR (foreground)/(background) [-|file]\n\n", program_name);
+ printf ("Usage: %s (foreground) OR (foreground)/(background) OR --clean [-|file]\n\n", program_name);
printf ("\tColors (foreground) (background)\n");
for (i = 0; i < tables[FOREGROUND].count; i++)
{
@@ -293,6 +313,8 @@ print_help (void)
printf ("\twhereas for lower case colors will be of normal intensity.\n");
printf ("\n\tOptions\n");
+ printf ("\t\t --clean\n");
+ printf ("\t\t --exclude-random\n");
printf ("\t\t-h, --help\n");
printf ("\t\t-v, --version\n\n");
}
@@ -324,12 +346,8 @@ cleanup (void)
unsigned int i;
for (i = 0; i < stacked_vars; i++)
if (vars_list[i])
- {
- free (vars_list[i]);
- vars_list[i] = NULL;
- }
- free (vars_list);
- vars_list = NULL;
+ free_null (vars_list[i]);
+ free_null (vars_list);
}
}
@@ -339,12 +357,9 @@ free_color_names (struct color_name **color_names)
unsigned int i;
for (i = 0; color_names[i]; i++)
{
- free (color_names[i]->name);
- color_names[i]->name = NULL;
- free (color_names[i]->orig);
- color_names[i]->orig = NULL;
- free (color_names[i]);
- color_names[i] = NULL;
+ free_null (color_names[i]->name);
+ free_null (color_names[i]->orig);
+ free_null (color_names[i]);
}
}
@@ -494,6 +509,12 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const
if (!colors[FOREGROUND]->code && colors[BACKGROUND] && colors[BACKGROUND]->code)
find_color_entry ("default", FOREGROUND, colors);
+ process_file_option (file_string, file, stream);
+}
+
+static void
+process_file_option (const char *file_string, const char **file, FILE **stream)
+{
if (file_string)
{
if (streq (file_string, "-"))
@@ -532,10 +553,21 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const
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); \
+} while (false);
+
static void
read_print_stream (bool bold, const struct color **colors, const char *file, FILE *stream, enum stream_mode mode)
{
- char buf[BUF_SIZE];
+ char buf[BUF_SIZE], *part_line = NULL;
unsigned int flags = 0;
bool first = false, always = false;
@@ -602,10 +634,25 @@ read_print_stream (bool bold, const struct color **colors, const char *file, FIL
p = eol + SKIP_LINE_ENDINGS (flags);
}
*eol = '\0';
- print_line (colors, bold, line, flags);
+ MERGE_PRINT_LINE (part_line, line, flags);
line = p;
}
- print_line (colors, bold, line, 0);
+ if (feof (stream)) {
+ MERGE_PRINT_LINE (part_line, line, 0);
+ }
+ else
+ {
+ if (!clean) /* efficiency */
+ print_line (colors, bold, line, 0);
+ else if (!part_line)
+ part_line = xstrdup (line);
+ else
+ {
+ char *merged_line = str_concat (part_line, line);
+ free (part_line);
+ part_line = merged_line;
+ }
+ }
}
}
@@ -680,18 +727,132 @@ find_color_entry (const char *const color_name, unsigned int index, const struct
static void
print_line (const struct color **colors, bool bold, const char *const line, unsigned int flags)
{
- if (colors[BACKGROUND] && colors[BACKGROUND]->code)
- printf ("\033[%s", colors[BACKGROUND]->code);
- if (colors[FOREGROUND]->code)
- printf ("\033[%s%s%s\033[0m", bold ? "1;" : "", colors[FOREGROUND]->code, line);
+ /* --clean */
+ if (clean)
+ print_clean (line);
else
- printf (formats[FMT_GENERIC], line);
+ {
+ if (colors[BACKGROUND] && colors[BACKGROUND]->code)
+ printf ("\033[%s", colors[BACKGROUND]->code);
+ if (colors[FOREGROUND]->code)
+ printf ("\033[%s%s%s\033[0m", bold ? "1;" : "", colors[FOREGROUND]->code, line);
+ else
+ printf (formats[FMT_GENERIC], line);
+ }
if (flags & CR)
putchar ('\r');
if (flags & LF)
putchar ('\n');
}
+static void
+print_clean (const char *line)
+{
+ const char *p;
+ char ***offsets = NULL;
+ unsigned int count = 0, i = 0;
+
+ for (p = line; *p; p++)
+ {
+ /* 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 == ';')
+ {
+ p++;
+ check_values = true;
+ }
+ first = false;
+ } while (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++;
+ }
+ DISCARD:
+ continue;
+ }
+ }
+
+ if (offsets)
+ print_free_offsets (line, offsets, count);
+ else
+ printf (formats[FMT_GENERIC], line);
+}
+
+#define SET_CHAR(offset, new, old) \
+ *old = *offset; \
+ *offset = new; \
+
+#define RESTORE_CHAR(offset, old) \
+ *offset = old; \
+
+static void
+print_free_offsets (const char *line, char ***offsets, unsigned int count)
+{
+ char ch;
+ unsigned int i;
+
+ SET_CHAR (offsets[0][0], '\0', &ch);
+ printf (formats[FMT_GENERIC], line);
+ RESTORE_CHAR (offsets[0][0], ch);
+
+ for (i = 0; i < count; i++)
+ {
+ char ch;
+ bool next_offset = false;
+ if (i + 1 < count)
+ {
+ SET_CHAR (offsets[i + 1][0], '\0', &ch);
+ next_offset = 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);
+}
+
static void *
malloc_wrap (size_t size, const char *file, unsigned int line)
{
@@ -710,6 +871,13 @@ realloc_wrap (void *ptr, size_t size, const char *file, unsigned int line)
return p;
}
+static void
+free_wrap (void **ptr)
+{
+ free (*ptr);
+ *ptr = NULL;
+}
+
static char *
strdup_wrap (const char *str, const char *file, unsigned int line)
{
@@ -721,6 +889,22 @@ strdup_wrap (const char *str, const char *file, unsigned int line)
return p;
}
+static char *
+str_concat (const char *str1, const char *str2)
+{
+ const unsigned long len = strlen (str1) + strlen (str2) + 1;
+ char *p, *str;
+
+ p = str = xmalloc (len);
+ strncpy (p, str1, strlen (str1));
+ p += strlen (str1);
+ strncpy (p, str2, strlen (str2));
+ p += strlen (str2);
+ *p = '\0';
+
+ return str;
+}
+
static void
vfprintf_fail (const char *fmt, ...)
{