neovim

Neovim text editor
git clone https://git.dasho.dev/neovim.git
Log | Files | Refs | README

xxd.c (35326B)


      1 /* xxd: my hexdump facility. jw
      2 *
      3 *  2.10.90 changed to word output
      4 *  3.03.93 new indent style, dumb bug inserted and fixed.
      5 *          -c option, mls
      6 * 26.04.94 better option parser, -ps, -l, -s added.
      7 *  1.07.94 -r badly needs - as input file.  Per default autoskip over
      8 *             consecutive lines of zeroes, as unix od does.
      9 *          -a shows them too.
     10 *          -i dump as c-style #include "file.h"
     11 *  1.11.95 if "xxd -i" knows the filename, an 'unsigned char filename_bits[]'
     12 *          array is written in correct c-syntax.
     13 *          -s improved, now defaults to absolute seek, relative requires a '+'.
     14 *          -r improved, now -r -s -0x... is supported.
     15 *             change/suppress leading '\0' bytes.
     16 *          -l n improved: stops exactly after n bytes.
     17 *          -r improved, better handling of partial lines with trailing garbage.
     18 *          -r improved, now -r -p works again!
     19 *          -r improved, less flushing, much faster now! (that was silly)
     20 *  3.04.96 Per repeated request of a single person: autoskip defaults to off.
     21 * 15.05.96 -v added. They want to know the version.
     22 *          -a fixed, to show last line inf file ends in all zeros.
     23 *          -u added: Print upper case hex-letters, as preferred by unix bc.
     24 *          -h added to usage message. Usage message extended.
     25 *          Now using outfile if specified even in normal mode, aehem.
     26 *          No longer mixing of ints and longs. May help doze people.
     27 *          Added binify ioctl for same reason. (Enough Doze stress for 1996!)
     28 * 16.05.96 -p improved, removed occasional superfluous linefeed.
     29 * 20.05.96 -l 0 fixed. tried to read anyway.
     30 * 21.05.96 -i fixed. now honours -u, and prepends __ to numeric filenames.
     31 *          compile -DWIN32 for NT or W95. George V. Reilly, * -v improved :-)
     32 *          support --gnuish-longhorn-options
     33 * 25.05.96 MAC support added: CodeWarrior already uses ``outline'' in Types.h
     34 *          which is included by MacHeaders (Axel Kielhorn). Renamed to
     35 *          xxdline().
     36 *  7.06.96 -i printed 'int' instead of 'char'. *blush*
     37 *          added Bram's OS2 ifdefs...
     38 * 18.07.96 gcc -Wall @ SunOS4 is now silent.
     39 *          Added osver for MSDOS/DJGPP/WIN32.
     40 * 29.08.96 Added size_t to strncmp() for Amiga.
     41 * 24.03.97 Windows NT support (Phil Hanna). Clean exit for Amiga WB (Bram)
     42 * 02.04.97 Added -E option, to have EBCDIC translation instead of ASCII
     43 *          (azc10@yahoo.com)
     44 * 22.05.97 added -g (group octets) option (jcook@namerica.kla.com).
     45 * 23.09.98 nasty -p -r misfeature fixed: slightly wrong output, when -c was
     46 *          missing or wrong.
     47 * 26.09.98 Fixed: 'xxd -i infile outfile' did not truncate outfile.
     48 * 27.10.98 Fixed: -g option parser required blank.
     49 *          option -b added: 01000101 binary output in normal format.
     50 * 16.05.00 Added VAXC changes by Stephen P. Wall
     51 * 16.05.00 Improved MMS file and merge for VMS by Zoltan Arpadffy
     52 * 2011 March  Better error handling by Florian Zumbiehl.
     53 * 2011 April  Formatting by Bram Moolenaar
     54 * 08.06.2013  Little-endian hexdump (-e) and offset (-o) by Vadim Vygonets.
     55 * 11.01.2019  Add full 64/32 bit range to -o and output by Christer Jensen.
     56 * 04.02.2020  Add -d for decimal offsets by Aapo Rantalainen
     57 * 14.01.2022  Disable extra newlines with -c0 -p by Erik Auerswald.
     58 * 20.06.2022  Permit setting the variable names used by -i by David Gow
     59 * 31.08.2023  -R never/auto/always prints colored output
     60 * 06.10.2023  enable -r -b to reverse bit dumps
     61 * 12.01.2024  disable auto-conversion for z/OS (MVS)
     62 * 17.01.2024  use size_t instead of usigned int for code-generation (-i), #13876
     63 * 25.01.2024  revert the previous patch (size_t instead of unsigned int)
     64 * 10.02.2024  fix buffer-overflow when writing color output to buffer, #14003
     65 * 10.05.2024  fix another buffer-overflow when writing colored output to buffer, #14738
     66 * 10.09.2024  Support -b and -i together, #15661
     67 * 19.10.2024  -e did add an extra space #15899
     68 * 11.11.2024  improve end-of-options argument parser #9285
     69 * 07.12.2024  fix overflow with xxd --autoskip and large sparse files #16175
     70 * 15.06.2025  improve color code logic
     71 * 08.08.2025  fix overflow with bitwise output
     72 * 20.08.2025  remove external library call for autoconversion on z/OS (MVS)
     73 * 24.08.2025  avoid NULL dereference with autoskip colorless
     74 * 26.11.2025  update indent in exit_with_usage()
     75 *
     76 * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com)
     77 *
     78 * I hereby grant permission to distribute and use xxd
     79 * under X11-MIT or GPL-2.0 (at the user's choice).
     80 *
     81 * Contributions by Bram Moolenaar et al.
     82 */
     83 
     84 // Visual Studio 2005 has 'deprecated' many of the standard CRT functions
     85 #if _MSC_VER >= 1400
     86 # define _CRT_SECURE_NO_DEPRECATE
     87 # define _CRT_NONSTDC_NO_DEPRECATE
     88 #endif
     89 #if !defined(CYGWIN) && defined(__CYGWIN__)
     90 # define CYGWIN
     91 #endif
     92 
     93 #if (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__)
     94 # define _XOPEN_SOURCE 700   /* for fdopen() */
     95 #endif
     96 
     97 #include <stdio.h>
     98 #ifdef VAXC
     99 # include <file.h>
    100 #else
    101 # include <fcntl.h>
    102 #endif
    103 #if defined(WIN32) || defined(CYGWIN)
    104 # include <io.h>        // for setmode()
    105 #endif
    106 #ifdef WIN32
    107 # include <windows.h>
    108 #endif
    109 #ifdef UNIX
    110 # include <unistd.h>
    111 #endif
    112 #include <ctype.h>
    113 #include <limits.h>
    114 #include <stdlib.h>
    115 #include <string.h>
    116 #if __MWERKS__ && !defined(BEBOX)
    117 # include <unix.h>      // for fdopen() on MAC
    118 #endif
    119 
    120 /*  This corrects the problem of missing prototypes for certain functions
    121 *  in some GNU installations (e.g. SunOS 4.1.x).
    122 *  Darren Hiebert <darren@hmi.com> (sparc-sun-sunos4.1.3_U1/2.7.2.2)
    123 */
    124 #if defined(__GNUC__) && defined(__STDC__)
    125 # ifndef __USE_FIXED_PROTOTYPES__
    126 #  define __USE_FIXED_PROTOTYPES__
    127 # endif
    128 #endif
    129 
    130 #ifndef __USE_FIXED_PROTOTYPES__
    131 /*
    132 * This is historic and works only if the compiler really has no prototypes:
    133 *
    134 * Include prototypes for Sun OS 4.x, when using an ANSI compiler.
    135 * FILE is defined on OS 4.x, not on 5.x (Solaris).
    136 * if __SVR4 is defined (some Solaris versions), don't include this.
    137 */
    138 # if defined(sun) && defined(FILE) && !defined(__SVR4) && defined(__STDC__)
    139 #  define __P(a) a
    140 // excerpt from my sun_stdlib.h
    141 extern int fprintf __P((FILE *, char *, ...));
    142 extern int fputs   __P((char *, FILE *));
    143 extern int _flsbuf __P((unsigned char, FILE *));
    144 extern int _filbuf __P((FILE *));
    145 extern int fflush  __P((FILE *));
    146 extern int fclose  __P((FILE *));
    147 extern int fseek   __P((FILE *, long, int));
    148 extern int rewind  __P((FILE *));
    149 
    150 extern void perror __P((char *));
    151 # endif
    152 #endif
    153 
    154 char version[] = "xxd 2025-11-26 by Juergen Weigert et al.";
    155 #ifdef WIN32
    156 char osver[] = " (Win32)";
    157 #else
    158 char osver[] = "";
    159 #endif
    160 
    161 #if defined(WIN32)
    162 # define BIN_READ(yes)  ((yes) ? "rb" : "rt")
    163 # define BIN_WRITE(yes) ((yes) ? "wb" : "wt")
    164 # define BIN_CREAT(yes) ((yes) ? (O_CREAT|O_BINARY) : O_CREAT)
    165 # define BIN_ASSIGN(fp, yes) setmode(fileno(fp), (yes) ? O_BINARY : O_TEXT)
    166 # define PATH_SEP '\\'
    167 #elif defined(CYGWIN)
    168 # define BIN_READ(yes)  ((yes) ? "rb" : "rt")
    169 # define BIN_WRITE(yes) ((yes) ? "wb" : "w")
    170 # define BIN_CREAT(yes) ((yes) ? (O_CREAT|O_BINARY) : O_CREAT)
    171 # define BIN_ASSIGN(fp, yes) ((yes) ? (void)setmode(fileno(fp), O_BINARY) : (void)(fp))
    172 # define PATH_SEP '/'
    173 #else
    174 # ifdef VMS
    175 #  define BIN_READ(dummy)  "r"
    176 #  define BIN_WRITE(dummy) "w"
    177 #  define BIN_CREAT(dummy) O_CREAT
    178 #  define BIN_ASSIGN(fp, dummy) fp
    179 #  define PATH_SEP ']'
    180 #  define FILE_SEP '.'
    181 # else
    182 #  define BIN_READ(dummy)  "r"
    183 #  define BIN_WRITE(dummy) "w"
    184 #  define BIN_CREAT(dummy) O_CREAT
    185 #  define BIN_ASSIGN(fp, dummy) fp
    186 #  define PATH_SEP '/'
    187 # endif
    188 #endif
    189 
    190 // open has only to arguments on the Mac
    191 #if __MWERKS__
    192 # define OPEN(name, mode, umask) open(name, mode)
    193 #else
    194 # define OPEN(name, mode, umask) open(name, mode, umask)
    195 #endif
    196 
    197 #ifdef AMIGA
    198 # define STRNCMP(s1, s2, l) strncmp(s1, s2, (size_t)l)
    199 #else
    200 # define STRNCMP(s1, s2, l) strncmp(s1, s2, l)
    201 #endif
    202 
    203 #ifndef __P
    204 # if defined(__STDC__) || defined(WIN32)
    205 #  define __P(a) a
    206 # else
    207 #  define __P(a) ()
    208 # endif
    209 #endif
    210 
    211 #define TRY_SEEK        /* attempt to use lseek, or skip forward by reading */
    212 #define COLS 256        /* change here, if you ever need more columns */
    213 
    214 /*
    215 * LLEN is the maximum length of a line; other than the visible characters
    216 * we need to consider also the escape color sequence prologue/epilogue ,
    217 * (11 bytes for each character).
    218 */
    219 #define LLEN \
    220  (39            /* addr: ⌈log10(ULONG_MAX)⌉ if "-d" flag given. We assume ULONG_MAX = 2**128 */ \
    221   + 2            /* ": " */ \
    222   + 13 * COLS    /* hex dump with colors */ \
    223   + (COLS - 1)   /* whitespace between groups if "-g1" option given and "-c" maxed out */ \
    224   + 2            /* whitespace */ \
    225   + 12 * COLS    /* ASCII dump with colors */ \
    226   + 2)           /* "\n\0" */
    227 
    228 /*
    229 * LLEN_NO_COLOR is the maximum length of a line excluding the colors.
    230 */
    231 #define LLEN_NO_COLOR \
    232  (39            /* addr: ⌈log10(ULONG_MAX)⌉ if "-d" flag given. We assume ULONG_MAX = 2**128 */ \
    233   + 2            /* ": " */ \
    234   + 9 * COLS     /* hex dump, worst case: bitwise output using -b */ \
    235   + 2            /* whitespace */ \
    236   + COLS         /* ASCII dump */ \
    237   + 2)           /* "\n\0" */
    238 
    239 char hexxa[] = "0123456789abcdef0123456789ABCDEF", *hexx = hexxa;
    240 
    241 // the different hextypes known by this program:
    242 #define HEX_NORMAL         0x00  /* no flags set */
    243 #define HEX_POSTSCRIPT     0x01
    244 #define HEX_CINCLUDE       0x02
    245 #define HEX_BITS           0x04  /* not hex a dump, but bits: 01111001 */
    246 #define HEX_LITTLEENDIAN   0x08
    247 
    248 #define CONDITIONAL_CAPITALIZE(c) (capitalize ? toupper((unsigned char)(c)) : (c))
    249 
    250 #define COLOR_PROLOGUE(color) \
    251  l_colored[c++] = '\033'; \
    252  l_colored[c++] = '['; \
    253  l_colored[c++] = '1'; \
    254  l_colored[c++] = ';'; \
    255  l_colored[c++] = '3'; \
    256  l_colored[c++] = (color); \
    257  l_colored[c++] = 'm';
    258 
    259 #define COLOR_EPILOGUE \
    260  l_colored[c++] = '\033'; \
    261  l_colored[c++] = '['; \
    262  l_colored[c++] = '0'; \
    263  l_colored[c++] = 'm';
    264 #define COLOR_RED '1'
    265 #define COLOR_GREEN '2'
    266 #define COLOR_YELLOW '3'
    267 #define COLOR_BLUE '4'
    268 #define COLOR_WHITE '7'
    269 
    270 static char *pname;
    271 
    272 static void exit_with_usage(void)
    273 {
    274  fprintf(stderr, "Usage:\n       %s [options] [infile [outfile]]\n", pname);
    275  fprintf(stderr, "    or\n       %s -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]\n",
    276          pname);
    277  fprintf(stderr, "Options:\n"
    278          "    -a          toggle autoskip: A single '*' replaces nul-lines. Default off.\n"
    279          "    -b          binary digit dump (incompatible with -ps). Default hex.\n"
    280          "    -C          capitalize variable names in C include file style (-i).\n"
    281          "    -c cols     format <cols> octets per line. Default 16 (-i: 12, -ps: 30).\n"
    282          "    -E          show characters in EBCDIC. Default ASCII.\n"
    283          "    -e          little-endian dump (incompatible with -ps,-i,-r).\n"
    284          "    -g bytes    number of octets per group in normal output. Default 2 (-e: 4).\n"
    285          "    -h          print this summary.\n"
    286          "    -i          output in C include file style.\n"
    287          "    -l len      stop after <len> octets.\n"
    288          "    -n name     set the variable name used in C include output (-i).\n"
    289          "    -o off      add <off> to the displayed file position.\n"
    290          "    -ps         output in postscript plain hexdump style.\n"
    291          "    -r          reverse operation: convert (or patch) hexdump into binary.\n"
    292          "    -r -s off   revert with <off> added to file positions found in hexdump.\n"
    293          "    -d          show offset in decimal instead of hex.\n");
    294  fprintf(stderr, "    -s %sseek  start at <seek> bytes abs. %sinfile offset.\n",
    295 #ifdef TRY_SEEK
    296          "[+][-]", "(or +: rel.) ");
    297 #else
    298          "", "");
    299 #endif
    300  fprintf(stderr, "    -u          use upper case hex letters.\n"
    301          "    -R when     colorize the output; <when> can be 'always', 'auto' or 'never'. Default: 'auto'.\n"
    302          "    -v          show version: \"%s%s\".\n", version, osver);
    303  exit(1);
    304 }
    305 
    306 static void perror_exit(int ret)
    307 {
    308  fprintf(stderr, "%s: ", pname);
    309  perror(NULL);
    310  exit(ret);
    311 }
    312 
    313 static void error_exit(int ret, char *msg)
    314 {
    315  fprintf(stderr, "%s: %s\n", pname, msg);
    316  exit(ret);
    317 }
    318 
    319 static int getc_or_die(FILE *fpi)
    320 {
    321  int c = getc(fpi);
    322  if (c == EOF && ferror(fpi)) {
    323    perror_exit(2);
    324  }
    325  return c;
    326 }
    327 
    328 static void putc_or_die(int c, FILE *fpo)
    329 {
    330  if (putc(c, fpo) == EOF) {
    331    perror_exit(3);
    332  }
    333 }
    334 
    335 static void fputs_or_die(char *s, FILE *fpo)
    336 {
    337  if (fputs(s, fpo) == EOF) {
    338    perror_exit(3);
    339  }
    340 }
    341 
    342 // Use a macro to allow for different arguments.
    343 #define FPRINTF_OR_DIE(args) if (fprintf args < 0) perror_exit(3)
    344 
    345 static void fclose_or_die(FILE *fpi, FILE *fpo)
    346 {
    347  if (fclose(fpo) != 0) {
    348    perror_exit(3);
    349  }
    350  if (fclose(fpi) != 0) {
    351    perror_exit(2);
    352  }
    353 }
    354 
    355 /*
    356 * If "c" is a hex digit, return the value.
    357 * Otherwise return -1.
    358 */
    359 static int parse_hex_digit(int c)
    360 {
    361  return (c >= '0' && c <= '9') ? c - '0'
    362                                : (c >= 'a' && c <= 'f') ? c - 'a' + 10
    363                                                         : (c >= 'A' && c <= 'F') ? c - 'A' + 10
    364                                                                                  : -1;
    365 }
    366 
    367 /*
    368 * If "c" is a bin digit, return the value.
    369 * Otherwise return -1.
    370 */
    371 static int parse_bin_digit(int c)
    372 {
    373  return (c >= '0' && c <= '1') ? c - '0'
    374                                : -1;
    375 }
    376 
    377 /*
    378 * Ignore text on "fpi" until end-of-line or end-of-file.
    379 * Return the '\n' or EOF character.
    380 * When an error is encountered exit with an error message.
    381 */
    382 static int skip_to_eol(FILE *fpi, int c)
    383 {
    384  while (c != '\n' && c != EOF) {
    385    c = getc_or_die(fpi);
    386  }
    387  return c;
    388 }
    389 
    390 /*
    391 * Max. cols binary characters are decoded from the input stream per line.
    392 * Two adjacent garbage characters after evaluated data delimit valid data.
    393 * Everything up to the next newline is discarded.
    394 *
    395 * The name is historic and came from 'undo type opt h'.
    396 */
    397 static int huntype(FILE *fpi, FILE *fpo, int cols, int hextype, long base_off)
    398 {
    399  int c, ign_garb = 1, n1 = -1, n2 = 0, n3 = 0, p = cols, bt = 0, b = 0, bcnt = 0;
    400  long have_off = 0, want_off = 0;
    401 
    402  rewind(fpi);
    403 
    404  while ((c = getc(fpi)) != EOF) {
    405    if (c == '\r') {  // Doze style input file?
    406      continue;
    407    }
    408 
    409    /* Allow multiple spaces.  This doesn't work when there is normal text
    410     * after the hex codes in the last line that looks like hex, thus only
    411     * use it for PostScript format. */
    412    if (hextype == HEX_POSTSCRIPT && (c == ' ' || c == '\n' || c == '\t')) {
    413      continue;
    414    }
    415 
    416    if (hextype == HEX_NORMAL || hextype == HEX_POSTSCRIPT) {
    417      n3 = n2;
    418      n2 = n1;
    419 
    420      n1 = parse_hex_digit(c);
    421      if (n1 == -1 && ign_garb) {
    422        continue;
    423      }
    424    } else {  // HEX_BITS
    425      n1 = parse_hex_digit(c);
    426      if (n1 == -1 && ign_garb) {
    427        continue;
    428      }
    429 
    430      bt = parse_bin_digit(c);
    431      if (bt != -1) {
    432        b = ((b << 1) | bt);
    433        ++bcnt;
    434      }
    435    }
    436 
    437    ign_garb = 0;
    438 
    439    if ((hextype != HEX_POSTSCRIPT) && (p >= cols)) {
    440      if (hextype == HEX_NORMAL) {
    441        if (n1 < 0) {
    442          p = 0;
    443          continue;
    444        }
    445        want_off = (want_off << 4) | n1;
    446      } else {  // HEX_BITS
    447        if (n1 < 0) {
    448          p = 0;
    449          bcnt = 0;
    450          continue;
    451        }
    452        want_off = (want_off << 4) | n1;
    453      }
    454      continue;
    455    }
    456 
    457    if (base_off + want_off != have_off) {
    458      if (fflush(fpo) != 0) {
    459        perror_exit(3);
    460      }
    461 #ifdef TRY_SEEK
    462      if (fseek(fpo, base_off + want_off - have_off, SEEK_CUR) >= 0) {
    463        have_off = base_off + want_off;
    464      }
    465 #endif
    466      if (base_off + want_off < have_off) {
    467        error_exit(5, "Sorry, cannot seek backwards.");
    468      }
    469      for (; have_off < base_off + want_off; have_off++) {
    470        putc_or_die(0, fpo);
    471      }
    472    }
    473 
    474    if (hextype == HEX_NORMAL || hextype == HEX_POSTSCRIPT) {
    475      if (n2 >= 0 && n1 >= 0) {
    476        putc_or_die((n2 << 4) | n1, fpo);
    477        have_off++;
    478        want_off++;
    479        n1 = -1;
    480        if (!hextype && (++p >= cols)) {
    481          // skip the rest of the line as garbage
    482          c = skip_to_eol(fpi, c);
    483        }
    484      } else if (n1 < 0 && n2 < 0 && n3 < 0) {
    485        // already stumbled into garbage, skip line, wait and see
    486        c = skip_to_eol(fpi, c);
    487      }
    488    } else {  // HEX_BITS
    489      if (bcnt == 8) {
    490        putc_or_die(b, fpo);
    491        have_off++;
    492        want_off++;
    493        b = 0;
    494        bcnt = 0;
    495        if (++p >= cols) {
    496          // skip the rest of the line as garbage
    497          c = skip_to_eol(fpi, c);
    498        }
    499      }
    500    }
    501 
    502    if (c == '\n') {
    503      if (hextype == HEX_NORMAL || hextype == HEX_BITS) {
    504        want_off = 0;
    505      }
    506      p = cols;
    507      ign_garb = 1;
    508    }
    509  }
    510  if (fflush(fpo) != 0) {
    511    perror_exit(3);
    512  }
    513 #ifdef TRY_SEEK
    514  fseek(fpo, 0L, SEEK_END);
    515 #endif
    516  fclose_or_die(fpi, fpo);
    517  return 0;
    518 }
    519 
    520 /*
    521 * Print line l with given colors.
    522 */
    523 static void print_colored_line(FILE *fp, char *l, char *colors)
    524 {
    525  static char l_colored[LLEN + 1];
    526 
    527  if (colors) {
    528    int c = 0;
    529    if (colors[0]) {
    530      COLOR_PROLOGUE(colors[0])
    531    }
    532    l_colored[c++] = l[0];
    533    int i;
    534    for (i = 1; l[i]; i++) {
    535      if (colors[i] != colors[i - 1]) {
    536        if (colors[i - 1]) {
    537          COLOR_EPILOGUE
    538        }
    539        if (colors[i]) {
    540          COLOR_PROLOGUE(colors[i])
    541        }
    542      }
    543      l_colored[c++] = l[i];
    544    }
    545 
    546    if (colors[i]) {
    547      COLOR_EPILOGUE
    548    }
    549    l_colored[c++] = '\0';
    550 
    551    fputs_or_die(l_colored, fp);
    552  } else {
    553    fputs_or_die(l, fp);
    554  }
    555 }
    556 
    557 /*
    558 * Print line l with given colors. If nz is false, xxdline regards the line as a line of
    559 * zeroes. If there are three or more consecutive lines of zeroes,
    560 * they are replaced by a single '*' character.
    561 *
    562 * If the output ends with more than two lines of zeroes, you
    563 * should call xxdline again with l being the last line and nz
    564 * negative. This ensures that the last line is shown even when
    565 * it is all zeroes.
    566 *
    567 * If nz is always positive, lines are never suppressed.
    568 */
    569 static void xxdline(FILE *fp, char *l, char *colors, int nz)
    570 {
    571  static char z[LLEN_NO_COLOR + 1];
    572  static char z_colors[LLEN_NO_COLOR + 1];
    573  static signed char zero_seen = 0;
    574 
    575  if (!nz && zero_seen == 1) {
    576    strcpy(z, l);
    577    if (colors) {
    578      memcpy(z_colors, colors, strlen(z));
    579    }
    580  }
    581 
    582  if (nz || !zero_seen++) {
    583    if (nz) {
    584      if (nz < 0) {
    585        zero_seen--;
    586      }
    587      if (zero_seen == 2) {
    588        print_colored_line(fp, z, z_colors);
    589      }
    590      if (zero_seen > 2) {
    591        fputs_or_die("*\n", fp);
    592      }
    593    }
    594    if (nz >= 0 || zero_seen > 0) {
    595      print_colored_line(fp, l, colors);
    596    }
    597 
    598    if (nz) {
    599      zero_seen = 0;
    600    }
    601  }
    602 
    603  /* If zero_seen > 3, then its exact value doesn't matter, so long as it
    604   * remains >3 and incrementing it will not cause overflow. */
    605  if (zero_seen >= 0x7F) {
    606    zero_seen = 4;
    607  }
    608 }
    609 
    610 // This is an EBCDIC to ASCII conversion table
    611 // from a proposed BTL standard April 16, 1979
    612 static unsigned char etoa64[] = {
    613  0040, 0240, 0241, 0242, 0243, 0244, 0245, 0246,
    614  0247, 0250, 0325, 0056, 0074, 0050, 0053, 0174,
    615  0046, 0251, 0252, 0253, 0254, 0255, 0256, 0257,
    616  0260, 0261, 0041, 0044, 0052, 0051, 0073, 0176,
    617  0055, 0057, 0262, 0263, 0264, 0265, 0266, 0267,
    618  0270, 0271, 0313, 0054, 0045, 0137, 0076, 0077,
    619  0272, 0273, 0274, 0275, 0276, 0277, 0300, 0301,
    620  0302, 0140, 0072, 0043, 0100, 0047, 0075, 0042,
    621  0303, 0141, 0142, 0143, 0144, 0145, 0146, 0147,
    622  0150, 0151, 0304, 0305, 0306, 0307, 0310, 0311,
    623  0312, 0152, 0153, 0154, 0155, 0156, 0157, 0160,
    624  0161, 0162, 0136, 0314, 0315, 0316, 0317, 0320,
    625  0321, 0345, 0163, 0164, 0165, 0166, 0167, 0170,
    626  0171, 0172, 0322, 0323, 0324, 0133, 0326, 0327,
    627  0330, 0331, 0332, 0333, 0334, 0335, 0336, 0337,
    628  0340, 0341, 0342, 0343, 0344, 0135, 0346, 0347,
    629  0173, 0101, 0102, 0103, 0104, 0105, 0106, 0107,
    630  0110, 0111, 0350, 0351, 0352, 0353, 0354, 0355,
    631  0175, 0112, 0113, 0114, 0115, 0116, 0117, 0120,
    632  0121, 0122, 0356, 0357, 0360, 0361, 0362, 0363,
    633  0134, 0237, 0123, 0124, 0125, 0126, 0127, 0130,
    634  0131, 0132, 0364, 0365, 0366, 0367, 0370, 0371,
    635  0060, 0061, 0062, 0063, 0064, 0065, 0066, 0067,
    636  0070, 0071, 0372, 0373, 0374, 0375, 0376, 0377
    637 };
    638 
    639 static char get_color_char(int e, int ebcdic)
    640 {
    641  if (ebcdic) {
    642    if ((e >= 75 && e <= 80) || (e >= 90 && e <= 97)
    643        || (e >= 107 && e <= 111) || (e >= 121 && e <= 127)
    644        || (e >= 129 && e <= 137) || (e >= 145 && e <= 154)
    645        || (e >= 162 && e <= 169) || (e >= 192 && e <= 201)
    646        || (e >= 208 && e <= 217) || (e >= 226 && e <= 233)
    647        || (e >= 240 && e <= 249) || (e == 189) || (e == 64)
    648        || (e == 173) || (e == 224)) {
    649      return COLOR_GREEN;
    650    } else if (e == 37 || e == 13 || e == 5) {
    651      return COLOR_YELLOW;
    652    } else if (e == 0) {
    653      return COLOR_WHITE;
    654    } else if (e == 255) {
    655      return COLOR_BLUE;
    656    } else {
    657      return COLOR_RED;
    658    }
    659  } else {  // ASCII
    660 #if defined(__MVS__) && __CHARSET_LIB == 0
    661    if (e >= 64) {
    662      return COLOR_GREEN;
    663    }
    664 #else
    665    if (e > 31 && e < 127) {
    666      return COLOR_GREEN;
    667    }
    668 #endif
    669 
    670    else if (e == 9 || e == 10 || e == 13) {
    671      return COLOR_YELLOW;
    672    } else if (e == 0) {
    673      return COLOR_WHITE;
    674    } else if (e == 255) {
    675      return COLOR_BLUE;
    676    } else {
    677      return COLOR_RED;
    678    }
    679  }
    680  return 0;
    681 }
    682 
    683 static int enable_color(void)
    684 {
    685 #ifdef WIN32
    686  DWORD mode;
    687  HANDLE out;
    688 
    689  if (!isatty(1)) {
    690    return 0;
    691  }
    692 
    693  out = GetStdHandle(STD_OUTPUT_HANDLE);
    694  GetConsoleMode(out, &mode);
    695  mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
    696  return (int)SetConsoleMode(out, mode);
    697 #elif defined(UNIX)
    698  return isatty(STDOUT_FILENO);
    699 #else
    700  return 0;
    701 #endif
    702 }
    703 
    704 int main(int argc, char *argv[])
    705 {
    706  FILE *fp, *fpo;
    707  int c, e, p = 0, relseek = 1, negseek = 0, revert = 0, i, x;
    708  int cols = 0, colsgiven = 0, nonzero = 0, autoskip = 0, hextype = HEX_NORMAL;
    709  int capitalize = 0, decimal_offset = 0;
    710  int ebcdic = 0;
    711  int octspergrp = -1;  // number of octets grouped in output
    712  int grplen;           // total chars per octet group excluding colors
    713  long length = -1, n = 0, seekoff = 0;
    714  unsigned long displayoff = 0;
    715  static char l[LLEN_NO_COLOR + 1];  // static because it may be too big for stack
    716  static char colors[LLEN_NO_COLOR + 1];  // color array
    717  char *pp;
    718  char *varname = NULL;
    719  int addrlen = 9;
    720  int color = 0;
    721  char *no_color;
    722  char cur_color = 0;
    723 
    724  no_color = getenv("NO_COLOR");
    725  if (no_color == NULL || no_color[0] == '\0') {
    726    color = enable_color();
    727  }
    728 
    729 #ifdef AMIGA
    730  // This program doesn't work when started from the Workbench
    731  if (argc == 0) {
    732    exit(1);
    733  }
    734 #endif
    735 
    736  pname = argv[0];
    737  for (pp = pname; *pp;) {
    738    if (*pp++ == PATH_SEP) {
    739      pname = pp;
    740    }
    741  }
    742 #ifdef FILE_SEP
    743  for (pp = pname; *pp; pp++) {
    744    if (*pp == FILE_SEP) {
    745      *pp = '\0';
    746      break;
    747    }
    748  }
    749 #endif
    750 
    751  while (argc >= 2) {
    752    pp = argv[1] + (!STRNCMP(argv[1], "--", 2) && argv[1][2]);
    753    if (!STRNCMP(pp, "-a", 2)) {
    754      autoskip = 1 - autoskip;
    755    } else if (!STRNCMP(pp, "-b", 2)) {
    756      hextype |= HEX_BITS;
    757    } else if (!STRNCMP(pp, "-e", 2)) {
    758      hextype |= HEX_LITTLEENDIAN;
    759    } else if (!STRNCMP(pp, "-u", 2)) {
    760      hexx = hexxa + 16;
    761    } else if (!STRNCMP(pp, "-p", 2)) {
    762      hextype |= HEX_POSTSCRIPT;
    763    } else if (!STRNCMP(pp, "-i", 2)) {
    764      hextype |= HEX_CINCLUDE;
    765    } else if (!STRNCMP(pp, "-C", 2)) {
    766      capitalize = 1;
    767    } else if (!STRNCMP(pp, "-d", 2)) {
    768      decimal_offset = 1;
    769    } else if (!STRNCMP(pp, "-r", 2)) {
    770      revert++;
    771    } else if (!STRNCMP(pp, "-E", 2)) {
    772      ebcdic++;
    773    } else if (!STRNCMP(pp, "-v", 2)) {
    774      fprintf(stderr, "%s%s\n", version, osver);
    775      exit(0);
    776    } else if (!STRNCMP(pp, "-c", 2)) {
    777      if (pp[2] && !STRNCMP("apitalize", pp + 2, 9)) {
    778        capitalize = 1;
    779      } else if (pp[2] && STRNCMP("ols", pp + 2, 3)) {
    780        colsgiven = 1;
    781        cols = (int)strtol(pp + 2, NULL, 0);
    782      } else {
    783        if (!argv[2]) {
    784          exit_with_usage();
    785        }
    786        colsgiven = 1;
    787        cols = (int)strtol(argv[2], NULL, 0);
    788        argv++;
    789        argc--;
    790      }
    791    } else if (!STRNCMP(pp, "-g", 2)) {
    792      if (pp[2] && STRNCMP("roup", pp + 2, 4)) {
    793        octspergrp = (int)strtol(pp + 2, NULL, 0);
    794      } else {
    795        if (!argv[2]) {
    796          exit_with_usage();
    797        }
    798        octspergrp = (int)strtol(argv[2], NULL, 0);
    799        argv++;
    800        argc--;
    801      }
    802    } else if (!STRNCMP(pp, "-o", 2)) {
    803      int reloffset = 0;
    804      int negoffset = 0;
    805      if (pp[2] && STRNCMP("ffset", pp + 2, 5)) {
    806        displayoff = strtoul(pp + 2, NULL, 0);
    807      } else {
    808        if (!argv[2]) {
    809          exit_with_usage();
    810        }
    811 
    812        if (argv[2][0] == '+') {
    813          reloffset++;
    814        }
    815        if (argv[2][reloffset] == '-') {
    816          negoffset++;
    817        }
    818 
    819        if (negoffset) {
    820          displayoff = ULONG_MAX - strtoul(argv[2] + reloffset + negoffset, NULL, 0) + 1;
    821        } else {
    822          displayoff = strtoul(argv[2] + reloffset + negoffset, NULL, 0);
    823        }
    824 
    825        argv++;
    826        argc--;
    827      }
    828    } else if (!STRNCMP(pp, "-s", 2)) {
    829      relseek = 0;
    830      negseek = 0;
    831      if (pp[2] && STRNCMP("kip", pp + 2, 3) && STRNCMP("eek", pp + 2, 3)) {
    832 #ifdef TRY_SEEK
    833        if (pp[2] == '+') {
    834          relseek++;
    835        }
    836        if (pp[2 + relseek] == '-') {
    837          negseek++;
    838        }
    839 #endif
    840        seekoff = strtol(pp + 2 + relseek + negseek, (char **)NULL, 0);
    841      } else {
    842        if (!argv[2]) {
    843          exit_with_usage();
    844        }
    845 #ifdef TRY_SEEK
    846        if (argv[2][0] == '+') {
    847          relseek++;
    848        }
    849        if (argv[2][relseek] == '-') {
    850          negseek++;
    851        }
    852 #endif
    853        seekoff = strtol(argv[2] + relseek + negseek, (char **)NULL, 0);
    854        argv++;
    855        argc--;
    856      }
    857    } else if (!STRNCMP(pp, "-l", 2)) {
    858      if (pp[2] && STRNCMP("en", pp + 2, 2)) {
    859        length = strtol(pp + 2, (char **)NULL, 0);
    860      } else {
    861        if (!argv[2]) {
    862          exit_with_usage();
    863        }
    864        length = strtol(argv[2], (char **)NULL, 0);
    865        argv++;
    866        argc--;
    867      }
    868    } else if (!STRNCMP(pp, "-n", 2)) {
    869      if (pp[2] && STRNCMP("ame", pp + 2, 3)) {
    870        varname = pp + 2;
    871      } else {
    872        if (!argv[2]) {
    873          exit_with_usage();
    874        }
    875        varname = argv[2];
    876        argv++;
    877        argc--;
    878      }
    879    } else if (!STRNCMP(pp, "-R", 2)) {
    880      char *pw = pp + 2;
    881      if (!pw[0]) {
    882        pw = argv[2];
    883        argv++;
    884        argc--;
    885      }
    886      if (!pw) {
    887        exit_with_usage();
    888      }
    889      if (!STRNCMP(pw, "always", 6)) {
    890        (void)enable_color();
    891        color = 1;
    892      } else if (!STRNCMP(pw, "never", 5)) {
    893        color = 0;
    894      } else if (!STRNCMP(pw, "auto", 4)) {
    895        color = enable_color();
    896      } else {
    897        exit_with_usage();
    898      }
    899    } else if (!strcmp(argv[1], "--")) {  // end of options
    900      argv++;
    901      argc--;
    902      break;
    903    } else if (pp[0] == '-' && pp[1]) {  // unknown option
    904      exit_with_usage();
    905    } else {
    906      break;                          // not an option
    907    }
    908    argv++;                           // advance to next argument
    909    argc--;
    910  }
    911 
    912  if (hextype != (HEX_CINCLUDE | HEX_BITS)) {
    913    // Allow at most one bit to be set in hextype
    914    if (hextype & (hextype - 1)) {
    915      error_exit(1, "only one of -b, -e, -u, -p, -i can be used");
    916    }
    917  }
    918 
    919  if (!colsgiven || (!cols && hextype != HEX_POSTSCRIPT)) {
    920    switch (hextype) {
    921    case HEX_POSTSCRIPT:
    922      cols = 30; break;
    923    case HEX_CINCLUDE:
    924      cols = 12; break;
    925    case HEX_CINCLUDE | HEX_BITS:
    926    case HEX_BITS:
    927      cols = 6; break;
    928    case HEX_NORMAL:
    929    case HEX_LITTLEENDIAN:
    930    default:
    931      cols = 16; break;
    932    }
    933  }
    934 
    935  if (octspergrp < 0) {
    936    switch (hextype) {
    937    case HEX_CINCLUDE | HEX_BITS:
    938    case HEX_BITS:
    939      octspergrp = 1; break;
    940    case HEX_NORMAL:
    941      octspergrp = 2; break;
    942    case HEX_LITTLEENDIAN:
    943      octspergrp = 4; break;
    944    case HEX_POSTSCRIPT:
    945    case HEX_CINCLUDE:
    946    default:
    947      octspergrp = 0; break;
    948    }
    949  }
    950 
    951  if ((hextype == HEX_POSTSCRIPT && cols < 0)
    952      || (hextype != HEX_POSTSCRIPT && cols < 1)
    953      || ((hextype == HEX_NORMAL || hextype == HEX_BITS || hextype == HEX_LITTLEENDIAN)
    954          && (cols > COLS))) {
    955    fprintf(stderr, "%s: invalid number of columns (max. %d).\n", pname, COLS);
    956    exit(1);
    957  }
    958 
    959  if (octspergrp < 1 || octspergrp > cols) {
    960    octspergrp = cols;
    961  } else if (hextype == HEX_LITTLEENDIAN && (octspergrp & (octspergrp - 1))) {
    962    error_exit(1, "number of octets per group must be a power of 2 with -e.");
    963  }
    964 
    965  if (argc > 3) {
    966    exit_with_usage();
    967  }
    968 
    969  if (argc == 1 || (argv[1][0] == '-' && !argv[1][1])) {
    970    BIN_ASSIGN(fp = stdin, !revert);
    971  } else {
    972    if ((fp = fopen(argv[1], BIN_READ(!revert))) == NULL) {
    973      fprintf(stderr, "%s: ", pname);
    974      perror(argv[1]);
    975      return 2;
    976    }
    977  }
    978 
    979  if (argc < 3 || (argv[2][0] == '-' && !argv[2][1])) {
    980    BIN_ASSIGN(fpo = stdout, revert);
    981  } else {
    982    int fd;
    983    int mode = revert ? O_WRONLY : (O_TRUNC|O_WRONLY);
    984 
    985    if (((fd = OPEN(argv[2], mode | BIN_CREAT(revert), 0666)) < 0)
    986        || (fpo = fdopen(fd, BIN_WRITE(revert))) == NULL) {
    987      fprintf(stderr, "%s: ", pname);
    988      perror(argv[2]);
    989      return 3;
    990    }
    991    rewind(fpo);
    992  }
    993 
    994  if (revert) {
    995    switch (hextype) {
    996    case HEX_NORMAL:
    997    case HEX_POSTSCRIPT:
    998    case HEX_BITS:
    999      return huntype(fp, fpo, cols, hextype,
   1000                     negseek ? -seekoff : seekoff);
   1001      break;
   1002    default:
   1003      error_exit(-1, "Sorry, cannot revert this type of hexdump");
   1004    }
   1005  }
   1006 
   1007  if (seekoff || negseek || !relseek) {
   1008 #ifdef TRY_SEEK
   1009    if (relseek) {
   1010      e = fseek(fp, negseek ? -seekoff : seekoff, SEEK_CUR);
   1011    } else {
   1012      e = fseek(fp, negseek ? -seekoff : seekoff,
   1013                negseek ? SEEK_END : SEEK_SET);
   1014    }
   1015    if (e < 0 && negseek) {
   1016      error_exit(4, "Sorry, cannot seek.");
   1017    }
   1018    if (e >= 0) {
   1019      seekoff = ftell(fp);
   1020    } else
   1021 #endif
   1022    {
   1023      long s = seekoff;
   1024 
   1025      while (s--) {
   1026        if (getc_or_die(fp) == EOF) {
   1027          error_exit(4, "Sorry, cannot seek.");
   1028        }
   1029      }
   1030    }
   1031  }
   1032 
   1033  if (hextype & HEX_CINCLUDE) {
   1034    // A user-set variable name overrides fp == stdin
   1035    if (varname == NULL && fp != stdin) {
   1036      varname = argv[1];
   1037    }
   1038 
   1039    if (varname != NULL) {
   1040      FPRINTF_OR_DIE((fpo, "unsigned char %s", isdigit((unsigned char)varname[0]) ? "__" : ""));
   1041      for (e = 0; (c = varname[e]) != 0; e++) {
   1042        putc_or_die(isalnum((unsigned char)c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo);
   1043      }
   1044      fputs_or_die("[] = {\n", fpo);
   1045    }
   1046 
   1047    p = 0;
   1048    while ((length < 0 || p < length) && (c = getc_or_die(fp)) != EOF) {
   1049      if (hextype & HEX_BITS) {
   1050        if (p == 0) {
   1051          fputs_or_die("  ", fpo);
   1052        } else if (p % cols == 0) {
   1053          fputs_or_die(",\n  ", fpo);
   1054        } else {
   1055          fputs_or_die(", ", fpo);
   1056        }
   1057 
   1058        FPRINTF_OR_DIE((fpo, "0b"));
   1059        for (int j = 7; j >= 0; j--) {
   1060          putc_or_die((c & (1 << j)) ? '1' : '0', fpo);
   1061        }
   1062        p++;
   1063      } else {
   1064        FPRINTF_OR_DIE((fpo, (hexx == hexxa) ? "%s0x%02x" : "%s0X%02X",
   1065                        (p % cols) ? ", " : (!p ? "  " : ",\n  "), c));
   1066        p++;
   1067      }
   1068    }
   1069 
   1070    if (p) {
   1071      fputs_or_die("\n", fpo);
   1072    }
   1073 
   1074    if (varname != NULL) {
   1075      fputs_or_die("};\n", fpo);
   1076      FPRINTF_OR_DIE((fpo, "unsigned int %s", isdigit((unsigned char)varname[0]) ? "__" : ""));
   1077      for (e = 0; (c = varname[e]) != 0; e++) {
   1078        putc_or_die(isalnum((unsigned char)c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo);
   1079      }
   1080      FPRINTF_OR_DIE((fpo, "_%s = %d;\n", capitalize ? "LEN" : "len", p));
   1081    }
   1082 
   1083    fclose_or_die(fp, fpo);
   1084    return 0;
   1085  }
   1086 
   1087  if (hextype == HEX_POSTSCRIPT) {
   1088    p = cols;
   1089    while ((length < 0 || n < length) && (e = getc_or_die(fp)) != EOF) {
   1090      putc_or_die(hexx[(e >> 4) & 0xf], fpo);
   1091      putc_or_die(hexx[e & 0xf], fpo);
   1092      n++;
   1093      if (cols > 0 && !--p) {
   1094        putc_or_die('\n', fpo);
   1095        p = cols;
   1096      }
   1097    }
   1098    if (cols == 0 || p < cols) {
   1099      putc_or_die('\n', fpo);
   1100    }
   1101    fclose_or_die(fp, fpo);
   1102    return 0;
   1103  }
   1104 
   1105  // hextype: HEX_NORMAL or HEX_BITS or HEX_LITTLEENDIAN
   1106 
   1107  if (hextype != HEX_BITS) {
   1108    grplen = octspergrp + octspergrp + 1;     // chars per octet group
   1109  } else {  // hextype == HEX_BITS
   1110    grplen = 8 * octspergrp + 1;
   1111  }
   1112 
   1113  while ((length < 0 || n < length) && (e = getc_or_die(fp)) != EOF) {
   1114    if (p == 0) {
   1115      addrlen = sprintf(l, decimal_offset ? "%08ld:" : "%08lx:",
   1116                        ((unsigned long)(n + seekoff + displayoff)));
   1117      for (c = addrlen; c < LLEN_NO_COLOR; l[c++] = ' ') {}
   1118    }
   1119    x = hextype == HEX_LITTLEENDIAN ? p ^ (octspergrp - 1) : p;
   1120    c = addrlen + 1 + (grplen * x) / octspergrp;
   1121    if (hextype == HEX_NORMAL || hextype == HEX_LITTLEENDIAN) {
   1122      if (color) {
   1123        cur_color = get_color_char(e, ebcdic);
   1124        colors[c] = cur_color;
   1125        colors[c + 1] = cur_color;
   1126      }
   1127 
   1128      l[c] = hexx[(e >> 4) & 0xf];
   1129      l[++c] = hexx[e & 0xf];
   1130    } else {  // hextype == HEX_BITS
   1131      for (i = 7; i >= 0; i--) {
   1132        l[c++] = (e & (1 << i)) ? '1' : '0';
   1133      }
   1134    }
   1135    if (e) {
   1136      nonzero++;
   1137    }
   1138    // When changing this update definition of LLEN and LLEN_NO_COLOR above.
   1139    if (hextype == HEX_LITTLEENDIAN) {
   1140      // last group will be fully used, round up
   1141      c = grplen * ((cols + octspergrp - 1) / octspergrp);
   1142    } else {
   1143      c = (grplen * cols - 1) / octspergrp;
   1144    }
   1145 
   1146    if (ebcdic) {
   1147      e = (e < 64) ? '.' : etoa64[e - 64];
   1148    }
   1149 
   1150    if (hextype == HEX_LITTLEENDIAN) {
   1151      c -= 1;
   1152    }
   1153 
   1154    c += addrlen + 3 + p;
   1155    if (color) {
   1156      colors[c] = cur_color;
   1157    }
   1158    l[c++] =
   1159 #if defined(__MVS__) && __CHARSET_LIB == 0
   1160      (e >= 64)
   1161 #else
   1162      (e > 31 && e < 127)
   1163 #endif
   1164      ? e : '.';
   1165    n++;
   1166    if (++p == cols) {
   1167      l[c++] = '\n';
   1168      l[c] = '\0';
   1169 
   1170      xxdline(fpo, l, color ? colors : NULL, autoskip ? nonzero : 1);
   1171      memset(colors, 0, c);
   1172      nonzero = 0;
   1173      p = 0;
   1174    }
   1175  }
   1176  if (p) {
   1177    l[c++] = '\n';
   1178    l[c] = '\0';
   1179    if (color) {
   1180      x = p;
   1181      if (hextype == HEX_LITTLEENDIAN) {
   1182        int fill = octspergrp - (p % octspergrp);
   1183        if (fill == octspergrp) {
   1184          fill = 0;
   1185        }
   1186 
   1187        c = addrlen + 1 + (grplen * (x - (octspergrp - fill))) / octspergrp;
   1188 
   1189        for (i = 0; i < fill; i++) {
   1190          colors[c] = COLOR_RED;
   1191          l[c++] = ' ';  // empty space
   1192          x++;
   1193          p++;
   1194        }
   1195      }
   1196 
   1197      if (hextype != HEX_BITS) {
   1198        c = addrlen + 1 + (grplen * x) / octspergrp;
   1199        c += cols - p;
   1200        c += (cols - p) / octspergrp;
   1201 
   1202        for (i = cols - p; i > 0; i--) {
   1203          colors[c] = COLOR_RED;
   1204          l[c++] = ' ';  // empty space
   1205        }
   1206      }
   1207      xxdline(fpo, l, colors, 1);
   1208    } else {
   1209      xxdline(fpo, l, NULL, 1);
   1210    }
   1211  } else if (autoskip) {
   1212    xxdline(fpo, l, color ? colors : NULL, -1);  // last chance to flush out suppressed lines
   1213  }
   1214  fclose_or_die(fp, fpo);
   1215  return 0;
   1216 }
   1217 
   1218 // vi:set ts=8 sw=4 sts=2 cino+={2 cino+=n-2 :