#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
static unsigned int stacked_vars = 0;
static void **vars_list = NULL;
+static bool clean = false;
static char *exclude = NULL;
static const char *program_name;
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 **);
{
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' },
{
switch (opt)
{
+ case 'c':
+ clean = true;
+ break;
case 'e': {
char *p;
exclude = xstrdup (optarg);
print_version ();
exit (EXIT_SUCCESS);
case '?':
- invalid_opt = true;
- break;
+ print_help ();
+ exit (EXIT_FAILURE);
default: /* never reached */
ABORT_TRACE ();
}
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);
{
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++)
{
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");
}
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);
}
}
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]);
}
}
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, "-"))
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;
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;
+ }
+ }
}
}
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)
{
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)
{
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, ...)
{