]> git.refcnt.org Git - colorize.git/blobdiff - colorize.c
Adjust format specifier for unsigned integers
[colorize.git] / colorize.c
index 041ecdb2fc560230f27353b462324597523cf6df..027249448ac631cf914225f44ce34941a08d1c8e 100644 (file)
 
 #define streq(s1, s2) (strcmp (s1, s2) == 0)
 
-#define xmalloc(size)       malloc_wrap(size,       __FILE__, __LINE__)
-#define xrealloc(ptr, size) realloc_wrap(ptr, size, __FILE__, __LINE__)
-#define xstrdup(str)        strdup_wrap(str,        __FILE__, __LINE__)
+#if DEBUG
+# define xmalloc(size)       malloc_wrap_debug(size,       __FILE__, __LINE__)
+# define xrealloc(ptr, size) realloc_wrap_debug(ptr, size, __FILE__, __LINE__)
+#else
+# define xmalloc(size)       malloc_wrap(size)
+# define xrealloc(ptr, size) realloc_wrap(ptr, size)
+#endif
+
+#define free_null(ptr) free_wrap((void **)&ptr)
+#define xstrdup(str)   strdup_wrap(str)
 
 #if !defined BUF_SIZE || BUF_SIZE <= 0
 # undef BUF_SIZE
     release_var (vars_list, stacked_vars, (void **)&ptr); \
 } while (false);
 
-#if DEBUG
-# define MEM_ALLOC_FAIL(file, line) do {                                                    \
-    fprintf (stderr, "Memory allocation failure in source file %s, line %d\n", file, line); \
+#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);
-#else
-# define MEM_ALLOC_FAIL(file, line) do {                               \
+#define MEM_ALLOC_FAIL() do {                                          \
     fprintf (stderr, "%s: memory allocation failure\n", program_name); \
     exit (2);                                                          \
 } while (false);
-#endif
 
 #define ABORT_TRACE()                                                              \
-    fprintf (stderr, "Aborting in source file %s, line %d\n", __FILE__, __LINE__); \
+    fprintf (stderr, "Aborting in source file %s, line %u\n", __FILE__, __LINE__); \
     abort ();                                                                      \
 
 #define CHECK_COLORS_RANDOM(color1, color2)        \
@@ -84,7 +88,7 @@
  && (streq (color_names[color2]->name, "none")     \
   || streq (color_names[color2]->name, "default")) \
 
-#define VERSION "0.48"
+#define VERSION "0.49"
 
 typedef unsigned short bool;
 
@@ -138,7 +142,7 @@ static const char *formats[] = {
     "%s",                    /* generic */
     "%s color '%s' %s",      /* color   */
     "%s color '%s' %s '%s'", /* random  */
-    "less than %d bytes %s", /* error   */
+    "less than %u bytes %s", /* error   */
     "%s: %s",                /* file    */
 };
 
@@ -171,6 +175,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 +185,20 @@ 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 *malloc_wrap (size_t, const char *, unsigned int);
-static void *realloc_wrap (void *, size_t, const char *, unsigned int);
-static char *strdup_wrap (const char *, const char *, unsigned int);
+static void print_clean (const char *);
+static void print_free_offsets (const char *, char ***, unsigned int);
+static void *malloc_wrap (size_t);
+static void *realloc_wrap (void *, size_t);
+static void *malloc_wrap_debug (size_t, const char *, unsigned int);
+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 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 +211,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 +240,9 @@ main (int argc, char **argv)
       {
         switch (opt)
           {
+            case 'c':
+              clean = true;
+              break;
             case 'e': {
               char *p;
               exclude = xstrdup (optarg);
@@ -246,8 +260,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 +269,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 +299,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 +318,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");
 }
@@ -308,7 +335,7 @@ print_version (void)
     c_flags = "unknown";
 #endif
     printf ("Compiler flags: %s\n", c_flags);
-    printf ("Buffer size: %d bytes\n", BUF_SIZE - 1);
+    printf ("Buffer size: %u bytes\n", BUF_SIZE - 1);
 }
 
 static void
@@ -324,12 +351,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 +362,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]);
       }
 }
 
@@ -403,7 +423,12 @@ process_options (unsigned int arg_cnt, char **option_strings, bool *bold, const
         have_file = (*color != '\0');
 
         if (have_file)
-          vfprintf_fail (formats[FMT_GENERIC], "file must be preceeded by color string");
+          {
+            if (file_string)
+              vfprintf_fail (formats[FMT_GENERIC], "file cannot be used as color string");
+            else
+              vfprintf_fail (formats[FMT_GENERIC], "file must be preceeded by color string");
+          }
       }
 
     if ((p = strchr (color_string, '/')))
@@ -489,6 +514,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, "-"))
@@ -527,10 +558,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;
 
@@ -597,10 +639,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;
+              }
+          }
       }
 }
 
@@ -675,47 +732,202 @@ 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;)
+      {
+        /* 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++;
+                continue;
+              }
+            DISCARD:
+              continue;
+          }
+        p++;
+      }
+
+    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)
+malloc_wrap (size_t size)
 {
     void *p = malloc (size);
     if (!p)
-      MEM_ALLOC_FAIL (file, line);
+      MEM_ALLOC_FAIL ();
     return p;
 }
 
 static void *
-realloc_wrap (void *ptr, size_t size, const char *file, unsigned int line)
+realloc_wrap (void *ptr, size_t size)
 {
     void *p = realloc (ptr, size);
     if (!p)
-      MEM_ALLOC_FAIL (file, line);
+      MEM_ALLOC_FAIL ();
     return p;
 }
 
-static char *
-strdup_wrap (const char *str, const char *file, unsigned int line)
+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);
+    return p;
+}
+
+static void *
+realloc_wrap_debug (void *ptr, size_t size, const char *file, unsigned int line)
 {
-    const unsigned int len = strlen (str) + 1;
-    char *p = malloc (len);
+    void *p = realloc (ptr, size);
     if (!p)
-      MEM_ALLOC_FAIL (file, line);
+      MEM_ALLOC_FAIL_DEBUG (file, line);
+    return p;
+}
+
+static void
+free_wrap (void **ptr)
+{
+    free (*ptr);
+    *ptr = NULL;
+}
+
+static char *
+strdup_wrap (const char *str)
+{
+    const size_t len = strlen (str) + 1;
+    char *p = xmalloc (len);
     strncpy (p, str, len);
     return p;
 }
 
+static char *
+str_concat (const char *str1, const char *str2)
+{
+    const size_t 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, ...)
 {