]> git.refcnt.org Git - colorize.git/blob - colorize.c
Include commit hash in version output
[colorize.git] / colorize.c
1 /*
2 * colorize - Read text from standard input stream or file and print
3 * it colorized through use of ANSI escape sequences
4 *
5 * Copyright (c) 2011-2014 Steven Schubiger
6 *
7 * This program is free software: you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation, either version 3 of the License, or
10 * (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 *
20 */
21
22 #define _BSD_SOURCE
23 #define _XOPEN_SOURCE 700
24 #include <assert.h>
25 #include <ctype.h>
26 #include <errno.h>
27 #include <getopt.h>
28 #include <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32 #include <sys/time.h>
33 #include <sys/types.h>
34 #include <sys/stat.h>
35 #include <time.h>
36 #include <unistd.h>
37
38 #ifndef DEBUG
39 # define DEBUG 0
40 #endif
41
42 #define str(arg) #arg
43 #define to_str(arg) str(arg)
44
45 #define streq(s1, s2) (strcmp (s1, s2) == 0)
46
47 #if !DEBUG
48 # define xmalloc(size) malloc_wrap(size)
49 # define xcalloc(nmemb, size) calloc_wrap(nmemb, size)
50 # define xrealloc(ptr, size) realloc_wrap(ptr, size)
51 #else
52 # define xmalloc(size) malloc_wrap_debug(size, __FILE__, __LINE__)
53 # define xcalloc(nmemb, size) calloc_wrap_debug(nmemb, size, __FILE__, __LINE__)
54 # define xrealloc(ptr, size) realloc_wrap_debug(ptr, size, __FILE__, __LINE__)
55 #endif
56
57 #define free_null(ptr) free_wrap((void **)&ptr)
58 #define xstrdup(str) strdup_wrap(str)
59
60 #if BUF_SIZE <= 0 || BUF_SIZE > 65536
61 # undef BUF_SIZE
62 #endif
63 #ifndef BUF_SIZE
64 # define BUF_SIZE 4096
65 #endif
66
67 #define LF 0x01
68 #define CR 0x02
69
70 #define SKIP_LINE_ENDINGS(flags) (((flags) & CR) && ((flags) & LF) ? 2 : 1)
71
72 #define VALID_FILE_TYPE(mode) (S_ISREG (mode) || S_ISLNK (mode) || S_ISFIFO (mode))
73
74 #define STACK_VAR(ptr) do { \
75 stack_var (&vars_list, &stacked_vars, stacked_vars, ptr); \
76 } while (false)
77
78 #define RELEASE_VAR(ptr) do { \
79 release_var (vars_list, stacked_vars, (void **)&ptr); \
80 } while (false)
81
82 #if !DEBUG
83 # define MEM_ALLOC_FAIL() do { \
84 fprintf (stderr, "%s: memory allocation failure\n", program_name); \
85 exit (2); \
86 } while (false)
87 #else
88 # define MEM_ALLOC_FAIL_DEBUG(file, line) do { \
89 fprintf (stderr, "Memory allocation failure in source file %s, line %u\n", file, line); \
90 exit (2); \
91 } while (false)
92 #endif
93
94 #define ABORT_TRACE() \
95 fprintf (stderr, "Aborting in source file %s, line %u\n", __FILE__, __LINE__); \
96 abort (); \
97
98 #define CHECK_COLORS_RANDOM(color1, color2) \
99 streq (color_names[color1]->name, "random") \
100 && (streq (color_names[color2]->name, "none") \
101 || streq (color_names[color2]->name, "default")) \
102
103 #define COLOR_SEP_CHAR '/'
104
105 #define VERSION "0.54"
106
107 typedef enum { false, true } bool;
108
109 struct color_name {
110 char *name;
111 char *orig;
112 };
113
114 static struct color_name *color_names[3] = { NULL, NULL, NULL };
115
116 struct color {
117 const char *name;
118 const char *code;
119 };
120
121 static const struct color fg_colors[] = {
122 { "none", NULL },
123 { "black", "30m" },
124 { "red", "31m" },
125 { "green", "32m" },
126 { "yellow", "33m" },
127 { "blue", "34m" },
128 { "magenta", "35m" },
129 { "cyan", "36m" },
130 { "white", "37m" },
131 { "default", "39m" },
132 };
133 static const struct color bg_colors[] = {
134 { "none", NULL },
135 { "black", "40m" },
136 { "red", "41m" },
137 { "green", "42m" },
138 { "yellow", "43m" },
139 { "blue", "44m" },
140 { "magenta", "45m" },
141 { "cyan", "46m" },
142 { "white", "47m" },
143 { "default", "49m" },
144 };
145
146 struct bytes_size {
147 unsigned int size;
148 char unit;
149 };
150
151 enum fmts {
152 FMT_GENERIC,
153 FMT_STRING,
154 FMT_QUOTE,
155 FMT_COLOR,
156 FMT_RANDOM,
157 FMT_ERROR,
158 FMT_FILE,
159 FMT_TYPE
160 };
161 static const char *formats[] = {
162 "%s", /* generic */
163 "%s '%s'", /* string */
164 "%s `%s' %s", /* quote */
165 "%s color '%s' %s", /* color */
166 "%s color '%s' %s '%s'", /* random */
167 "less than %u bytes %s", /* error */
168 "%s: %s", /* file */
169 "%s: %s: %s", /* type */
170 };
171
172 enum { FOREGROUND, BACKGROUND };
173
174 static const struct {
175 struct color const *entries;
176 unsigned int count;
177 const char *desc;
178 } tables[] = {
179 { fg_colors, sizeof (fg_colors) / sizeof (struct color), "foreground" },
180 { bg_colors, sizeof (bg_colors) / sizeof (struct color), "background" },
181 };
182
183 static FILE *stream = NULL;
184
185 static unsigned int stacked_vars = 0;
186 static void **vars_list = NULL;
187
188 static bool clean = false;
189 static bool clean_all = false;
190
191 static char *exclude = NULL;
192
193 static const char *program_name;
194
195 static void print_hint (void);
196 static void print_help (void);
197 static void print_version (void);
198 static void cleanup (void);
199 static void free_color_names (struct color_name **);
200 static void process_args (unsigned int, char **, bool *, const struct color **, const char **, FILE **);
201 static void process_file_arg (const char *, const char **, FILE **);
202 static void read_print_stream (bool, const struct color **, const char *, FILE *);
203 static void find_color_entries (struct color_name **, const struct color **);
204 static void find_color_entry (const struct color_name *, unsigned int, const struct color **);
205 static void print_line (bool, const struct color **, const char * const, unsigned int);
206 static void print_clean (const char *);
207 static void print_free_offsets (const char *, char ***, unsigned int);
208 #if !DEBUG
209 static void *malloc_wrap (size_t);
210 static void *calloc_wrap (size_t, size_t);
211 static void *realloc_wrap (void *, size_t);
212 #else
213 static void *malloc_wrap_debug (size_t, const char *, unsigned int);
214 static void *calloc_wrap_debug (size_t, size_t, const char *, unsigned int);
215 static void *realloc_wrap_debug (void *, size_t, const char *, unsigned int);
216 #endif
217 static void free_wrap (void **);
218 static char *strdup_wrap (const char *);
219 static char *str_concat (const char *, const char *);
220 static bool get_bytes_size (unsigned long, struct bytes_size *);
221 static char *get_file_type (mode_t);
222 static bool has_color_name (const char *, const char *);
223 static void vfprintf_diag (const char *, ...);
224 static void vfprintf_fail (const char *, ...);
225 static void stack_var (void ***, unsigned int *, unsigned int, void *);
226 static void release_var (void **, unsigned int, void **);
227
228 #define SET_OPT_TYPE(type) \
229 opt_type = type; \
230 opt = 0; \
231 goto PARSE_OPT; \
232
233 extern char *optarg;
234 extern int optind;
235
236 static int opt_type = 0;
237
238 int
239 main (int argc, char **argv)
240 {
241 unsigned int arg_cnt = 0;
242
243 enum {
244 OPT_CLEAN = 1,
245 OPT_CLEAN_ALL,
246 OPT_EXCLUDE_RANDOM,
247 OPT_HELP,
248 OPT_VERSION
249 };
250
251 int opt;
252 struct option long_opts[] = {
253 { "clean", no_argument, &opt_type, OPT_CLEAN },
254 { "clean-all", no_argument, &opt_type, OPT_CLEAN_ALL },
255 { "exclude-random", required_argument, &opt_type, OPT_EXCLUDE_RANDOM },
256 { "help", no_argument, &opt_type, OPT_HELP },
257 { "version", no_argument, &opt_type, OPT_VERSION },
258 { NULL, 0, NULL, 0 },
259 };
260
261 bool bold = false;
262
263 const struct color *colors[2] = {
264 NULL, /* foreground */
265 NULL, /* background */
266 };
267
268 const char *file = NULL;
269
270 program_name = argv[0];
271 atexit (cleanup);
272
273 setvbuf (stdout, NULL, _IOLBF, 0);
274
275 while ((opt = getopt_long (argc, argv, "hv", long_opts, NULL)) != -1)
276 {
277 PARSE_OPT:
278 switch (opt)
279 {
280 case 0: /* long opts */
281 switch (opt_type)
282 {
283 case OPT_CLEAN:
284 clean = true;
285 break;
286 case OPT_CLEAN_ALL:
287 clean_all = true;
288 break;
289 case OPT_EXCLUDE_RANDOM: {
290 bool valid = false;
291 unsigned int i;
292 exclude = xstrdup (optarg);
293 STACK_VAR (exclude);
294 for (i = 1; i < tables[FOREGROUND].count - 1; i++) /* skip color none and default */
295 {
296 const struct color *entry = &tables[FOREGROUND].entries[i];
297 if (streq (exclude, entry->name))
298 {
299 valid = true;
300 break;
301 }
302 }
303 if (!valid)
304 vfprintf_fail (formats[FMT_GENERIC], "--exclude-random switch must be provided a plain color");
305 break;
306 }
307 case OPT_HELP:
308 print_help ();
309 exit (EXIT_SUCCESS);
310 case OPT_VERSION:
311 print_version ();
312 exit (EXIT_SUCCESS);
313 default: /* never reached */
314 ABORT_TRACE ();
315 }
316 break;
317 case 'h':
318 SET_OPT_TYPE (OPT_HELP);
319 case 'v':
320 SET_OPT_TYPE (OPT_VERSION);
321 case '?':
322 print_hint ();
323 exit (EXIT_FAILURE);
324 default: /* never reached */
325 ABORT_TRACE ();
326 }
327 }
328
329 arg_cnt = argc - optind;
330
331 if (clean || clean_all)
332 {
333 if (clean && clean_all)
334 vfprintf_fail (formats[FMT_GENERIC], "--clean and --clean-all switch are mutually exclusive");
335 if (arg_cnt > 1)
336 {
337 const char *format = "%s %s";
338 const char *message = "switch cannot be used with more than one file";
339 if (clean)
340 vfprintf_fail (format, "--clean", message);
341 else if (clean_all)
342 vfprintf_fail (format, "--clean-all", message);
343 }
344 }
345 else
346 {
347 if (arg_cnt == 0 || arg_cnt > 2)
348 {
349 vfprintf_diag ("%u arguments provided, expected 1-2 arguments or clean option", arg_cnt);
350 print_hint ();
351 exit (EXIT_FAILURE);
352 }
353 }
354
355 if (clean || clean_all)
356 process_file_arg (argv[optind], &file, &stream);
357 else
358 process_args (arg_cnt, &argv[optind], &bold, colors, &file, &stream);
359 read_print_stream (bold, colors, file, stream);
360
361 RELEASE_VAR (exclude);
362
363 exit (EXIT_SUCCESS);
364 }
365
366 static void
367 print_hint (void)
368 {
369 fprintf (stderr, "Type `%s --help' for help screen.\n", program_name);
370 }
371
372 static void
373 print_help (void)
374 {
375 unsigned int i;
376
377 printf ("Usage: %s (foreground) OR (foreground)%c(background) OR --clean[-all] [-|file]\n\n", program_name, COLOR_SEP_CHAR);
378 printf ("\tColors (foreground) (background)\n");
379 for (i = 0; i < tables[FOREGROUND].count; i++)
380 {
381 const struct color *entry = &tables[FOREGROUND].entries[i];
382 const char *name = entry->name;
383 const char *code = entry->code;
384 if (code)
385 printf ("\t\t{\033[%s#\033[0m} [%c%c]%s%*s%s\n",
386 code, toupper (*name), *name, name + 1, 10 - (int)strlen (name), " ", name);
387 else
388 printf ("\t\t{-} %s%*s%s\n", name, 13 - (int)strlen (name), " ", name);
389 }
390 printf ("\t\t{*} [Rr]%s%*s%s [--exclude-random=<foreground color>]\n", "andom", 10 - (int)strlen ("random"), " ", "random");
391
392 printf ("\n\tFirst character of color name in upper case denotes increased intensity,\n");
393 printf ("\twhereas for lower case colors will be of normal intensity.\n");
394
395 printf ("\n\tOptions\n");
396 printf ("\t\t --clean\n");
397 printf ("\t\t --clean-all\n");
398 printf ("\t\t --exclude-random\n");
399 printf ("\t\t-h, --help\n");
400 printf ("\t\t-v, --version\n\n");
401 }
402
403 static void
404 print_version (void)
405 {
406 #ifdef HAVE_VERSION
407 # include "version.h"
408 #else
409 const char *version = NULL;
410 #endif
411 const char *c_flags;
412 struct bytes_size bytes_size;
413 bool debug;
414 #ifdef CFLAGS
415 c_flags = to_str (CFLAGS);
416 #else
417 c_flags = "unknown";
418 #endif
419 #if DEBUG
420 debug = true;
421 #else
422 debug = false;
423 #endif
424 if (version)
425 printf ("colorize %s (compiled at %s, %s)\n", version, __DATE__, __TIME__);
426 else
427 printf ("colorize v%s (compiled at %s, %s)\n", VERSION, __DATE__, __TIME__);
428 printf ("Compiler flags: %s\n", c_flags);
429 if (get_bytes_size (BUF_SIZE, &bytes_size))
430 {
431 if (BUF_SIZE % 1024 == 0)
432 printf ("Buffer size: %u%c\n", bytes_size.size, bytes_size.unit);
433 else
434 printf ("Buffer size: %u%c, %u byte%s\n", bytes_size.size, bytes_size.unit,
435 BUF_SIZE % 1024, BUF_SIZE % 1024 > 1 ? "s" : "");
436 }
437 else
438 printf ("Buffer size: %lu byte%s\n", (unsigned long)BUF_SIZE, BUF_SIZE > 1 ? "s" : "");
439 printf ("Debugging: %s\n", debug ? "yes" : "no");
440 }
441
442 static void
443 cleanup (void)
444 {
445 free_color_names (color_names);
446
447 if (stream && fileno (stream) != STDIN_FILENO)
448 fclose (stream);
449
450 if (vars_list)
451 {
452 unsigned int i;
453 for (i = 0; i < stacked_vars; i++)
454 if (vars_list[i])
455 free_null (vars_list[i]);
456
457 free_null (vars_list);
458 }
459 }
460
461 static void
462 free_color_names (struct color_name **color_names)
463 {
464 unsigned int i;
465 for (i = 0; color_names[i]; i++)
466 {
467 free_null (color_names[i]->name);
468 free_null (color_names[i]->orig);
469 free_null (color_names[i]);
470 }
471 }
472
473 static void
474 process_args (unsigned int arg_cnt, char **arg_strings, bool *bold, const struct color **colors, const char **file, FILE **stream)
475 {
476 int ret;
477 unsigned int index;
478 char *color, *p, *str;
479 struct stat sb;
480
481 const char *color_string = arg_cnt >= 1 ? arg_strings[0] : NULL;
482 const char *file_string = arg_cnt == 2 ? arg_strings[1] : NULL;
483
484 assert (color_string);
485
486 if (streq (color_string, "-"))
487 {
488 if (file_string)
489 vfprintf_fail (formats[FMT_GENERIC], "hyphen cannot be used as color string");
490 else
491 vfprintf_fail (formats[FMT_GENERIC], "hyphen must be preceeded by color string");
492 }
493
494 ret = lstat (color_string, &sb);
495
496 /* Ensure that we don't fail if there's a file with one or more
497 color names in its path. */
498 if (ret != -1)
499 {
500 bool have_file;
501 unsigned int c;
502 const char *color = color_string;
503 const mode_t mode = sb.st_mode;
504
505 for (c = 1; c <= 2 && *color; c++)
506 {
507 bool matched = false;
508 unsigned int i;
509 for (i = 0; i < tables[FOREGROUND].count; i++)
510 {
511 const struct color *entry = &tables[FOREGROUND].entries[i];
512 if (has_color_name (color, entry->name))
513 {
514 color += strlen (entry->name);
515 matched = true;
516 break;
517 }
518 }
519 if (!matched && has_color_name (color, "random"))
520 {
521 color += strlen ("random");
522 matched = true;
523 }
524 if (matched && *color == COLOR_SEP_CHAR && *(color + 1))
525 color++;
526 else
527 break;
528 }
529
530 have_file = (*color != '\0');
531
532 if (have_file)
533 {
534 const char *file_exists = color_string;
535 if (file_string)
536 vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "cannot be used as color string");
537 else
538 {
539 if (VALID_FILE_TYPE (mode))
540 vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "must be preceeded by color string");
541 else
542 vfprintf_fail (formats[FMT_QUOTE], get_file_type (mode), file_exists, "is not a valid file type");
543 }
544 }
545 }
546
547 if ((p = strchr (color_string, COLOR_SEP_CHAR)))
548 {
549 if (p == color_string)
550 vfprintf_fail (formats[FMT_STRING], "foreground color missing in string", color_string);
551 else if (p == color_string + strlen (color_string) - 1)
552 vfprintf_fail (formats[FMT_STRING], "background color missing in string", color_string);
553 else if (strchr (++p, COLOR_SEP_CHAR))
554 vfprintf_fail (formats[FMT_STRING], "one color pair allowed only for string", color_string);
555 }
556
557 str = xstrdup (color_string);
558 STACK_VAR (str);
559
560 for (index = 0, color = str; *color; index++, color = p)
561 {
562 char *ch, *sep;
563
564 p = NULL;
565 if ((sep = strchr (color, COLOR_SEP_CHAR)))
566 {
567 *sep = '\0';
568 p = sep + 1;
569 }
570 else
571 p = color + strlen (color);
572 assert (p);
573
574 for (ch = color; *ch; ch++)
575 if (!isalpha (*ch))
576 vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be made of non-alphabetic characters");
577
578 for (ch = color + 1; *ch; ch++)
579 if (!islower (*ch))
580 vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be in mixed lower/upper case");
581
582 if (streq (color, "None"))
583 vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color, "cannot be bold");
584
585 if (isupper (*color))
586 {
587 switch (index)
588 {
589 case FOREGROUND:
590 *bold = true;
591 break;
592 case BACKGROUND:
593 vfprintf_fail (formats[FMT_COLOR], tables[BACKGROUND].desc, color, "cannot be bold");
594 break;
595 default: /* never reached */
596 ABORT_TRACE ();
597 }
598 }
599
600 color_names[index] = xcalloc (1, sizeof (struct color_name));
601
602 color_names[index]->orig = xstrdup (color);
603
604 for (ch = color; *ch; ch++)
605 *ch = tolower (*ch);
606
607 color_names[index]->name = xstrdup (color);
608 }
609
610 RELEASE_VAR (str);
611
612 assert (color_names[FOREGROUND]);
613
614 if (color_names[BACKGROUND])
615 {
616 unsigned int i;
617 unsigned int color_sets[2][2] = { { FOREGROUND, BACKGROUND }, { BACKGROUND, FOREGROUND } };
618 for (i = 0; i < 2; i++)
619 {
620 unsigned int color1 = color_sets[i][0];
621 unsigned int color2 = color_sets[i][1];
622 if (CHECK_COLORS_RANDOM (color1, color2))
623 vfprintf_fail (formats[FMT_RANDOM], tables[color1].desc, color_names[color1]->orig, "cannot be combined with", color_names[color2]->orig);
624 }
625 }
626
627 find_color_entries (color_names, colors);
628 free_color_names (color_names);
629
630 if (!colors[FOREGROUND]->code && colors[BACKGROUND] && colors[BACKGROUND]->code)
631 {
632 struct color_name color_name;
633 color_name.name = color_name.orig = "default";
634
635 find_color_entry (&color_name, FOREGROUND, colors);
636 }
637
638 process_file_arg (file_string, file, stream);
639 }
640
641 static void
642 process_file_arg (const char *file_string, const char **file, FILE **stream)
643 {
644 if (file_string)
645 {
646 if (streq (file_string, "-"))
647 *stream = stdin;
648 else
649 {
650 FILE *s;
651 const char *file = file_string;
652 struct stat sb;
653 int ret;
654
655 errno = 0;
656 ret = lstat (file, &sb);
657
658 if (ret == -1)
659 vfprintf_fail (formats[FMT_FILE], file, strerror (errno));
660
661 if (!VALID_FILE_TYPE (sb.st_mode))
662 vfprintf_fail (formats[FMT_TYPE], file, "unrecognized type", get_file_type (sb.st_mode));
663
664 errno = 0;
665
666 s = fopen (file, "r");
667 if (!s)
668 vfprintf_fail (formats[FMT_FILE], file, strerror (errno));
669 *stream = s;
670 }
671 *file = file_string;
672 }
673 else
674 {
675 *stream = stdin;
676 *file = "stdin";
677 }
678
679 assert (*stream);
680 assert (*file);
681 }
682
683 #define MERGE_PRINT_LINE(part_line, line, flags, check_eof) do { \
684 char *current_line, *merged_line = NULL; \
685 if (part_line) \
686 { \
687 merged_line = str_concat (part_line, line); \
688 free_null (part_line); \
689 } \
690 current_line = merged_line ? merged_line : (char *)line; \
691 if (!check_eof || *current_line != '\0') \
692 print_line (bold, colors, current_line, flags); \
693 free (merged_line); \
694 } while (false)
695
696 static void
697 read_print_stream (bool bold, const struct color **colors, const char *file, FILE *stream)
698 {
699 char buf[BUF_SIZE + 1], *part_line = NULL;
700 unsigned int flags = 0;
701
702 while (!feof (stream))
703 {
704 size_t bytes_read;
705 char *eol;
706 const char *line;
707 memset (buf, '\0', BUF_SIZE + 1);
708 bytes_read = fread (buf, 1, BUF_SIZE, stream);
709 if (bytes_read != BUF_SIZE && ferror (stream))
710 vfprintf_fail (formats[FMT_ERROR], BUF_SIZE, "read");
711 line = buf;
712 while ((eol = strpbrk (line, "\n\r")))
713 {
714 char *p;
715 flags &= ~(CR|LF);
716 if (*eol == '\r')
717 {
718 flags |= CR;
719 if (*(eol + 1) == '\n')
720 flags |= LF;
721 }
722 else if (*eol == '\n')
723 flags |= LF;
724 else
725 vfprintf_fail (formats[FMT_FILE], file, "unrecognized line ending");
726 p = eol + SKIP_LINE_ENDINGS (flags);
727 *eol = '\0';
728 MERGE_PRINT_LINE (part_line, line, flags, false);
729 line = p;
730 }
731 if (feof (stream)) {
732 MERGE_PRINT_LINE (part_line, line, 0, true);
733 }
734 else if (*line != '\0')
735 {
736 if (!clean && !clean_all) /* efficiency */
737 print_line (bold, colors, line, 0);
738 else if (!part_line)
739 part_line = xstrdup (line);
740 else
741 {
742 char *merged_line = str_concat (part_line, line);
743 free (part_line);
744 part_line = merged_line;
745 }
746 }
747 }
748 }
749
750 static void
751 find_color_entries (struct color_name **color_names, const struct color **colors)
752 {
753 struct timeval tv;
754 unsigned int index;
755
756 /* randomness */
757 gettimeofday (&tv, NULL);
758 srand (tv.tv_usec * tv.tv_sec);
759
760 for (index = 0; color_names[index]; index++)
761 {
762 const char *color_name = color_names[index]->name;
763
764 const unsigned int count = tables[index].count;
765 const struct color *const color_entries = tables[index].entries;
766
767 if (streq (color_name, "random"))
768 {
769 bool excludable;
770 unsigned int i;
771 do {
772 excludable = false;
773 i = rand() % (count - 2) + 1; /* omit color none and default */
774 switch (index)
775 {
776 case FOREGROUND:
777 /* --exclude-random */
778 if (exclude && streq (exclude, color_entries[i].name))
779 excludable = true;
780 else if (color_names[BACKGROUND] && streq (color_names[BACKGROUND]->name, color_entries[i].name))
781 excludable = true;
782 break;
783 case BACKGROUND:
784 if (streq (colors[FOREGROUND]->name, color_entries[i].name))
785 excludable = true;
786 break;
787 default: /* never reached */
788 ABORT_TRACE ();
789 }
790 } while (excludable);
791 colors[index] = (struct color *)&color_entries[i];
792 }
793 else
794 find_color_entry (color_names[index], index, colors);
795 }
796 }
797
798 static void
799 find_color_entry (const struct color_name *color_name, unsigned int index, const struct color **colors)
800 {
801 bool found = false;
802 unsigned int i;
803
804 const unsigned int count = tables[index].count;
805 const struct color *const color_entries = tables[index].entries;
806
807 for (i = 0; i < count; i++)
808 if (streq (color_name->name, color_entries[i].name))
809 {
810 colors[index] = (struct color *)&color_entries[i];
811 found = true;
812 break;
813 }
814 if (!found)
815 vfprintf_fail (formats[FMT_COLOR], tables[index].desc, color_name->orig, "not recognized");
816 }
817
818 static void
819 print_line (bool bold, const struct color **colors, const char *const line, unsigned int flags)
820 {
821 /* --clean[-all] */
822 if (clean || clean_all)
823 print_clean (line);
824 else
825 {
826 /* Foreground color code is guaranteed to be set when background color code is present. */
827 if (colors[BACKGROUND] && colors[BACKGROUND]->code)
828 printf ("\033[%s", colors[BACKGROUND]->code);
829 if (colors[FOREGROUND]->code)
830 printf ("\033[%s%s%s\033[0m", bold ? "1;" : "", colors[FOREGROUND]->code, line);
831 else
832 printf (formats[FMT_GENERIC], line);
833 }
834 if (flags & CR)
835 putchar ('\r');
836 if (flags & LF)
837 putchar ('\n');
838 }
839
840 static void
841 print_clean (const char *line)
842 {
843 const char *p;
844 char ***offsets = NULL;
845 unsigned int count = 0, i = 0;
846
847 for (p = line; *p;)
848 {
849 /* ESC[ */
850 if (*p == 27 && *(p + 1) == '[')
851 {
852 const char *begin = p;
853 p += 2;
854 if (clean_all)
855 {
856 while (isdigit (*p) || *p == ';')
857 p++;
858 }
859 else if (clean)
860 {
861 bool check_values;
862 unsigned int iter = 0;
863 const char *digit;
864 do {
865 check_values = false;
866 iter++;
867 if (!isdigit (*p))
868 goto DISCARD;
869 digit = p;
870 while (isdigit (*p))
871 p++;
872 if (p - digit > 2)
873 goto DISCARD;
874 else /* check range */
875 {
876 char val[3];
877 int value;
878 unsigned int i;
879 const unsigned int digits = p - digit;
880 for (i = 0; i < digits; i++)
881 val[i] = *digit++;
882 val[i] = '\0';
883 value = atoi (val);
884 if (value == 0) /* reset */
885 {
886 if (iter > 1)
887 goto DISCARD;
888 goto END;
889 }
890 else if (value == 1) /* bold */
891 {
892 bool discard = false;
893 if (iter > 1)
894 discard = true;
895 else if (*p != ';')
896 discard = true;
897 if (discard)
898 goto DISCARD;
899 p++;
900 check_values = true;
901 }
902 else if ((value >= 30 && value <= 37) || value == 39) /* foreground colors */
903 goto END;
904 else if ((value >= 40 && value <= 47) || value == 49) /* background colors */
905 {
906 if (iter > 1)
907 goto DISCARD;
908 goto END;
909 }
910 else
911 goto DISCARD;
912 }
913 } while (iter == 1 && check_values);
914 }
915 END: if (*p == 'm')
916 {
917 const char *end = p++;
918 if (!offsets)
919 offsets = xmalloc (++count * sizeof (char **));
920 else
921 offsets = xrealloc (offsets, ++count * sizeof (char **));
922 offsets[i] = xmalloc (2 * sizeof (char *));
923 offsets[i][0] = (char *)begin; /* ESC */
924 offsets[i][1] = (char *)end; /* m */
925 i++;
926 continue;
927 }
928 DISCARD:
929 continue;
930 }
931 p++;
932 }
933
934 if (offsets)
935 print_free_offsets (line, offsets, count);
936 else
937 printf (formats[FMT_GENERIC], line);
938 }
939
940 #define SET_CHAR(offset, new, old) \
941 *old = *offset; \
942 *offset = new; \
943
944 #define RESTORE_CHAR(offset, old) \
945 *offset = old; \
946
947 static void
948 print_free_offsets (const char *line, char ***offsets, unsigned int count)
949 {
950 char ch;
951 unsigned int i;
952
953 SET_CHAR (offsets[0][0], '\0', &ch);
954 printf (formats[FMT_GENERIC], line);
955 RESTORE_CHAR (offsets[0][0], ch);
956
957 for (i = 0; i < count; i++)
958 {
959 char ch;
960 bool next_offset = false;
961 if (i + 1 < count)
962 {
963 SET_CHAR (offsets[i + 1][0], '\0', &ch);
964 next_offset = true;
965 }
966 printf (formats[FMT_GENERIC], offsets[i][1] + 1);
967 if (next_offset)
968 RESTORE_CHAR (offsets[i + 1][0], ch);
969 }
970 for (i = 0; i < count; i++)
971 free_null (offsets[i]);
972 free_null (offsets);
973 }
974
975 #if !DEBUG
976 static void *
977 malloc_wrap (size_t size)
978 {
979 void *p = malloc (size);
980 if (!p)
981 MEM_ALLOC_FAIL ();
982 return p;
983 }
984
985 static void *
986 calloc_wrap (size_t nmemb, size_t size)
987 {
988 void *p = calloc (nmemb, size);
989 if (!p)
990 MEM_ALLOC_FAIL ();
991 return p;
992 }
993
994 static void *
995 realloc_wrap (void *ptr, size_t size)
996 {
997 void *p = realloc (ptr, size);
998 if (!p)
999 MEM_ALLOC_FAIL ();
1000 return p;
1001 }
1002 #else
1003 static void *
1004 malloc_wrap_debug (size_t size, const char *file, unsigned int line)
1005 {
1006 void *p = malloc (size);
1007 if (!p)
1008 MEM_ALLOC_FAIL_DEBUG (file, line);
1009 return p;
1010 }
1011
1012 static void *
1013 calloc_wrap_debug (size_t nmemb, size_t size, const char *file, unsigned int line)
1014 {
1015 void *p = calloc (nmemb, size);
1016 if (!p)
1017 MEM_ALLOC_FAIL_DEBUG (file, line);
1018 return p;
1019 }
1020
1021 static void *
1022 realloc_wrap_debug (void *ptr, size_t size, const char *file, unsigned int line)
1023 {
1024 void *p = realloc (ptr, size);
1025 if (!p)
1026 MEM_ALLOC_FAIL_DEBUG (file, line);
1027 return p;
1028 }
1029 #endif
1030
1031 static void
1032 free_wrap (void **ptr)
1033 {
1034 free (*ptr);
1035 *ptr = NULL;
1036 }
1037
1038 static char *
1039 strdup_wrap (const char *str)
1040 {
1041 const size_t len = strlen (str) + 1;
1042 char *p = xmalloc (len);
1043 strncpy (p, str, len);
1044 return p;
1045 }
1046
1047 static char *
1048 str_concat (const char *str1, const char *str2)
1049 {
1050 const size_t len = strlen (str1) + strlen (str2) + 1;
1051 char *p, *str;
1052
1053 p = str = xmalloc (len);
1054 strncpy (p, str1, strlen (str1));
1055 p += strlen (str1);
1056 strncpy (p, str2, strlen (str2));
1057 p += strlen (str2);
1058 *p = '\0';
1059
1060 return str;
1061 }
1062
1063 static bool
1064 get_bytes_size (unsigned long bytes, struct bytes_size *bytes_size)
1065 {
1066 const char *unit, units[] = { '0', 'K', 'M', 'G', '\0' };
1067 unsigned long size = bytes;
1068 if (bytes < 1024)
1069 return false;
1070 unit = units;
1071 while (size >= 1024 && *(unit + 1))
1072 {
1073 size /= 1024;
1074 unit++;
1075 }
1076 bytes_size->size = (unsigned int)size;
1077 bytes_size->unit = *unit;
1078 return true;
1079 }
1080
1081 static char *
1082 get_file_type (mode_t mode)
1083 {
1084 if (S_ISREG (mode))
1085 return "file";
1086 else if (S_ISDIR (mode))
1087 return "directory";
1088 else if (S_ISCHR (mode))
1089 return "character device";
1090 else if (S_ISBLK (mode))
1091 return "block device";
1092 else if (S_ISFIFO (mode))
1093 return "named pipe";
1094 else if (S_ISLNK (mode))
1095 return "symbolic link";
1096 else if (S_ISSOCK (mode))
1097 return "socket";
1098 else
1099 return "file";
1100 }
1101
1102 static bool
1103 has_color_name (const char *str, const char *name)
1104 {
1105 char *p;
1106
1107 assert (strlen (str));
1108 assert (strlen (name));
1109
1110 if (!(*str == *name || *str == toupper (*name)))
1111 return false;
1112 else if (*(name + 1) != '\0'
1113 && !((p = strstr (str + 1, name + 1)) && p == str + 1))
1114 return false;
1115
1116 return true;
1117 }
1118
1119 #define DO_VFPRINTF(fmt) \
1120 va_list ap; \
1121 fprintf (stderr, "%s: ", program_name); \
1122 va_start (ap, fmt); \
1123 vfprintf (stderr, fmt, ap); \
1124 va_end (ap); \
1125 fprintf (stderr, "\n"); \
1126
1127 static void
1128 vfprintf_diag (const char *fmt, ...)
1129 {
1130 DO_VFPRINTF (fmt);
1131 }
1132
1133 static void
1134 vfprintf_fail (const char *fmt, ...)
1135 {
1136 DO_VFPRINTF (fmt);
1137 exit (EXIT_FAILURE);
1138 }
1139
1140 static void
1141 stack_var (void ***list, unsigned int *stacked, unsigned int index, void *ptr)
1142 {
1143 /* nothing to stack */
1144 if (ptr == NULL)
1145 return;
1146 if (!*list)
1147 *list = xmalloc (sizeof (void *));
1148 else
1149 {
1150 unsigned int i;
1151 for (i = 0; i < *stacked; i++)
1152 if (!(*list)[i])
1153 {
1154 (*list)[i] = ptr;
1155 return; /* reused */
1156 }
1157 *list = xrealloc (*list, (*stacked + 1) * sizeof (void *));
1158 }
1159 (*list)[index] = ptr;
1160 (*stacked)++;
1161 }
1162
1163 static void
1164 release_var (void **list, unsigned int stacked, void **ptr)
1165 {
1166 unsigned int i;
1167 /* nothing to release */
1168 if (*ptr == NULL)
1169 return;
1170 for (i = 0; i < stacked; i++)
1171 if (list[i] == *ptr)
1172 {
1173 free (*ptr);
1174 *ptr = NULL;
1175 list[i] = NULL;
1176 return;
1177 }
1178 }