Add support for a new _cat(1)_ option, "--bold-escapes", that makes escape sequences generated by "-E", "-v" and "-T" bold making it easy to determine which strings are literal sequences in the input and which strings represent escaped characters. The "--bold-escapes" option is ignored if "-v" is not specified since it may conflict with escape sequences present in the input. --- src/cat.c 2019-12-22 10:15:43.126764549 -0800 +++ src/cat.c 2019-12-22 21:39:36.413294479 -0800 @@ -40,6 +40,7 @@ #include "full-write.h" #include "safe-read.h" #include "xbinary-io.h" +#include "argmatch.h" /* The official name of this program (e.g., no 'g' prefix). */ #define PROGRAM_NAME "cat" @@ -48,6 +49,9 @@ proper_name ("Torbjorn Granlund"), \ proper_name ("Richard M. Stallman") +#define TTY_BOLD "\033[1m" +#define TTY_SGR0 "\033[0m" + /* Name of input file. May be "-". */ static char const *infile; @@ -78,6 +82,24 @@ /* Preserves the 'cat' function's local 'newlines' between invocations. */ static int newlines2 = 0; +enum bold_type + { + bold_never, + bold_always, + bold_auto, + }; + +static char const *const bold_args[] = + { + "always", "never", "auto", NULL + }; + +static enum bold_type const bold_types[] = + { + bold_always, bold_never, bold_auto + }; +ARGMATCH_VERIFY (bold_args, bold_types); + void usage (int status) { @@ -99,6 +121,11 @@ \n\ -A, --show-all equivalent to -vET\n\ -b, --number-nonblank number nonempty output lines, overrides -n\n\ + --bold-escapes[=WHEN] make escape sequences generated by -E, -v and -T\n\ + bold. WHEN can be 'always' (default if omitted),\n\ + 'auto' or 'never'. This option is ignored if -v\n\ + is not specified since it may conflict with\n\ + escape sequences present in the input.\n\ -e equivalent to -vE\n\ -E, --show-ends display $ at end of each line\n\ -n, --number number all output lines\n\ @@ -227,6 +254,7 @@ size_t outsize, /* Variables that have values according to the specified options. */ + bool bold_escapes, bool show_nonprinting, bool show_tabs, bool number, @@ -399,7 +427,15 @@ /* Output a currency symbol if requested (-e). */ if (show_ends) - *bpout++ = '$'; + { + if (bold_escapes) + bpout = stpcpy(bpout, TTY_BOLD); + + *bpout++ = '$'; + + if (bold_escapes) + bpout = stpcpy(bpout, TTY_SGR0); + } /* Output the newline. */ @@ -427,13 +463,19 @@ scan for chars that need conversion. */ if (show_nonprinting) { - while (true) + for (;; ch = *bpin++) { if (ch >= 32) { - if (ch < 127) + if (ch < 127) { *bpout++ = ch; - else if (ch == 127) + continue; + } + + if (bold_escapes) + bpout = stpcpy(bpout, TTY_BOLD); + + if (ch == 127) { *bpout++ = '^'; *bpout++ = '?'; @@ -458,6 +500,9 @@ *bpout++ = ch - 128 + 64; } } + + if (bold_escapes) + bpout = stpcpy(bpout, TTY_SGR0); } else if (ch == '\t' && !show_tabs) *bpout++ = '\t'; @@ -468,11 +513,15 @@ } else { + if (bold_escapes) + bpout = stpcpy(bpout, TTY_BOLD); + *bpout++ = '^'; *bpout++ = ch + 64; - } - ch = *bpin++; + if (bold_escapes) + bpout = stpcpy(bpout, TTY_SGR0); + } } } else @@ -482,8 +531,14 @@ { if (ch == '\t' && show_tabs) { + if (bold_escapes) + bpout = stpcpy(bpout, TTY_BOLD); + *bpout++ = '^'; *bpout++ = ch + 64; + + if (bold_escapes) + bpout = stpcpy(bpout, TTY_SGR0); } else if (ch != '\n') *bpout++ = ch; @@ -518,6 +573,8 @@ bool ok = true; int c; + int bold; + size_t inflation = 4; /* Index in argv to processed argument. */ int argind; @@ -543,6 +600,7 @@ bool show_ends = false; bool show_nonprinting = false; bool show_tabs = false; + bool bold_escapes = false; int file_open_mode = O_RDONLY; static struct option const long_options[] = @@ -554,6 +612,7 @@ {"show-ends", no_argument, NULL, 'E'}, {"show-tabs", no_argument, NULL, 'T'}, {"show-all", no_argument, NULL, 'A'}, + {"bold-escapes", optional_argument, NULL, 'B'}, {GETOPT_HELP_OPTION_DECL}, {GETOPT_VERSION_OPTION_DECL}, {NULL, 0, NULL, 0} @@ -573,7 +632,7 @@ /* Parse command line options. */ - while ((c = getopt_long (argc, argv, "benstuvAET", long_options, NULL)) + while ((c = getopt_long (argc, argv, "benstuvABET", long_options, NULL)) != -1) { switch (c) @@ -623,6 +682,19 @@ show_tabs = true; break; + case 'B': + if (optarg) + { + bold = XARGMATCH ("--bold-escapes", optarg, bold_args, + bold_types); + bold_escapes = bold == bold_always || + (bold == bold_auto && isatty(STDOUT_FILENO)); + } + else + bold_escapes = true; + + break; + case_GETOPT_HELP_CHAR; case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS); @@ -632,6 +704,9 @@ } } + if (bold_escapes && !show_nonprinting) + bold_escapes = false; + /* Get device, i-node number, and optimal blocksize of output. */ if (fstat (STDOUT_FILENO, &stat_buf) < 0) @@ -738,13 +813,16 @@ on some paging implementations, so add PAGE_SIZE - 1 bytes to the request to make room for the alignment. */ - outbuf = xmalloc (outsize - 1 + insize * 4 + LINE_COUNTER_BUF_LEN - + page_size - 1); + if (bold_escapes) + inflation += strlen(TTY_BOLD) + strlen(TTY_SGR0); + + outbuf = xmalloc (outsize - 1 + insize * inflation + + LINE_COUNTER_BUF_LEN + page_size - 1); ok &= cat (ptr_align (inbuf, page_size), insize, - ptr_align (outbuf, page_size), outsize, show_nonprinting, - show_tabs, number, number_nonblank, show_ends, - squeeze_blank); + ptr_align (outbuf, page_size), outsize, bold_escapes, + show_nonprinting, show_tabs, number, number_nonblank, + show_ends, squeeze_blank); free (outbuf); }