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