view src/xxd/xxd.c @ 34686:83875247fbc0 v9.1.0224

patch 9.1.0224: cursor may move too many lines over "right" & "below" virt text Commit: https://github.com/vim/vim/commit/515f734e687f28f7199b2a8042197624d9f3ec15 Author: Dylan Thacker-Smith <dylan.ah.smith@gmail.com> Date: Thu Mar 28 12:01:14 2024 +0100 patch 9.1.0224: cursor may move too many lines over "right" & "below" virt text Problem: If a line has "right" & "below" virtual text properties, where the "below" property may be stored first due to lack of ordering between them, then the line height is calculated to be 1 more and causes the cursor to far over the line. Solution: Remove some unnecessary setting of a `next_right_goes_below = TRUE` flag for "below" and "above" text properties. (Dylan Thacker-Smith) I modified a regression test I recently added to cover this case, leveraging the fact that "after", "right" & "below" text properties are being stored in the reverse of the order they are added in. The previous version of this regression test was crafted to workaround this issue so it can be addressed by this separate patch. closes: #14317 Signed-off-by: Dylan Thacker-Smith <dylan.ah.smith@gmail.com> Signed-off-by: Christian Brabandt <cb@256bit.org>
author Christian Brabandt <cb@256bit.org>
date Thu, 28 Mar 2024 12:15:03 +0100
parents 9d07f1036a69
children 42f061099b39
line wrap: on
line source

/* xxd: my hexdump facility. jw
 *
 *  2.10.90 changed to word output
 *  3.03.93 new indent style, dumb bug inserted and fixed.
 *	    -c option, mls
 * 26.04.94 better option parser, -ps, -l, -s added.
 *  1.07.94 -r badly needs - as input file.  Per default autoskip over
 *	       consecutive lines of zeroes, as unix od does.
 *	    -a shows them too.
 *	    -i dump as c-style #include "file.h"
 *  1.11.95 if "xxd -i" knows the filename, an 'unsigned char filename_bits[]'
 *	    array is written in correct c-syntax.
 *	    -s improved, now defaults to absolute seek, relative requires a '+'.
 *	    -r improved, now -r -s -0x... is supported.
 *	       change/suppress leading '\0' bytes.
 *	    -l n improved: stops exactly after n bytes.
 *	    -r improved, better handling of partial lines with trailing garbage.
 *	    -r improved, now -r -p works again!
 *	    -r improved, less flushing, much faster now! (that was silly)
 *  3.04.96 Per repeated request of a single person: autoskip defaults to off.
 * 15.05.96 -v added. They want to know the version.
 *	    -a fixed, to show last line inf file ends in all zeros.
 *	    -u added: Print upper case hex-letters, as preferred by unix bc.
 *	    -h added to usage message. Usage message extended.
 *	    Now using outfile if specified even in normal mode, aehem.
 *	    No longer mixing of ints and longs. May help doze people.
 *	    Added binify ioctl for same reason. (Enough Doze stress for 1996!)
 * 16.05.96 -p improved, removed occasional superfluous linefeed.
 * 20.05.96 -l 0 fixed. tried to read anyway.
 * 21.05.96 -i fixed. now honours -u, and prepends __ to numeric filenames.
 *	    compile -DWIN32 for NT or W95. George V. Reilly, * -v improved :-)
 *	    support --gnuish-longhorn-options
 * 25.05.96 MAC support added: CodeWarrior already uses ``outline'' in Types.h
 *	    which is included by MacHeaders (Axel Kielhorn). Renamed to
 *	    xxdline().
 *  7.06.96 -i printed 'int' instead of 'char'. *blush*
 *	    added Bram's OS2 ifdefs...
 * 18.07.96 gcc -Wall @ SunOS4 is now silent.
 *	    Added osver for MSDOS/DJGPP/WIN32.
 * 29.08.96 Added size_t to strncmp() for Amiga.
 * 24.03.97 Windows NT support (Phil Hanna). Clean exit for Amiga WB (Bram)
 * 02.04.97 Added -E option, to have EBCDIC translation instead of ASCII
 *	    (azc10@yahoo.com)
 * 22.05.97 added -g (group octets) option (jcook@namerica.kla.com).
 * 23.09.98 nasty -p -r misfeature fixed: slightly wrong output, when -c was
 *	    missing or wrong.
 * 26.09.98 Fixed: 'xxd -i infile outfile' did not truncate outfile.
 * 27.10.98 Fixed: -g option parser required blank.
 *	    option -b added: 01000101 binary output in normal format.
 * 16.05.00 Added VAXC changes by Stephen P. Wall
 * 16.05.00 Improved MMS file and merge for VMS by Zoltan Arpadffy
 * 2011 March  Better error handling by Florian Zumbiehl.
 * 2011 April  Formatting by Bram Moolenaar
 * 08.06.2013  Little-endian hexdump (-e) and offset (-o) by Vadim Vygonets.
 * 11.01.2019  Add full 64/32 bit range to -o and output by Christer Jensen.
 * 04.02.2020  Add -d for decimal offsets by Aapo Rantalainen
 * 14.01.2022  Disable extra newlines with -c0 -p by Erik Auerswald.
 * 20.06.2022  Permit setting the variable names used by -i by David Gow
 * 31.08.2023  -R never/auto/always prints colored output
 * 06.10.2023  enable -r -b to reverse bit dumps
 * 12.01.2024  disable auto-conversion for z/OS (MVS)
 * 17.01.2024  use size_t instead of usigned int for code-generation (-i), #13876
 * 25.01.2024  revert the previous patch (size_t instead of unsigned int)
 * 10.02.2024  fix buffer-overflow when writing color output to buffer, #14003
 *
 * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com)
 *
 * I hereby grant permission to distribute and use xxd
 * under X11-MIT or GPL-2.0 (at the user's choice).
 *
 * Contributions by Bram Moolenaar et al.
 */

/* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */
#if _MSC_VER >= 1400
# define _CRT_SECURE_NO_DEPRECATE
# define _CRT_NONSTDC_NO_DEPRECATE
#endif
#if !defined(CYGWIN) && defined(__CYGWIN__)
# define CYGWIN
#endif

#if (defined(__linux__) && !defined(__ANDROID__)) || defined(__CYGWIN__)
# define _XOPEN_SOURCE 700   /* for fdopen() */
#endif

#include <stdio.h>
#ifdef VAXC
# include <file.h>
#else
# include <fcntl.h>
#endif
#if defined(WIN32) || defined(CYGWIN)
# include <io.h>	/* for setmode() */
#endif
#ifdef WIN32
# include <windows.h>
#endif
#ifdef UNIX
# include <unistd.h>
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include <limits.h>
#if __MWERKS__ && !defined(BEBOX)
# include <unix.h>	/* for fdopen() on MAC */
#endif


/*  This corrects the problem of missing prototypes for certain functions
 *  in some GNU installations (e.g. SunOS 4.1.x).
 *  Darren Hiebert <darren@hmi.com> (sparc-sun-sunos4.1.3_U1/2.7.2.2)
 */
#if defined(__GNUC__) && defined(__STDC__)
# ifndef __USE_FIXED_PROTOTYPES__
#  define __USE_FIXED_PROTOTYPES__
# endif
#endif

#ifndef __USE_FIXED_PROTOTYPES__
/*
 * This is historic and works only if the compiler really has no prototypes:
 *
 * Include prototypes for Sun OS 4.x, when using an ANSI compiler.
 * FILE is defined on OS 4.x, not on 5.x (Solaris).
 * if __SVR4 is defined (some Solaris versions), don't include this.
 */
#if defined(sun) && defined(FILE) && !defined(__SVR4) && defined(__STDC__)
#  define __P(a) a
/* excerpt from my sun_stdlib.h */
extern int fprintf __P((FILE *, char *, ...));
extern int fputs   __P((char *, FILE *));
extern int _flsbuf __P((unsigned char, FILE *));
extern int _filbuf __P((FILE *));
extern int fflush  __P((FILE *));
extern int fclose  __P((FILE *));
extern int fseek   __P((FILE *, long, int));
extern int rewind  __P((FILE *));

extern void perror __P((char *));
# endif
#endif

char version[] = "xxd 2024-02-10 by Juergen Weigert et al.";
#ifdef WIN32
char osver[] = " (Win32)";
#else
char osver[] = "";
#endif

#if defined(WIN32)
# define BIN_READ(yes)  ((yes) ? "rb" : "rt")
# define BIN_WRITE(yes) ((yes) ? "wb" : "wt")
# define BIN_CREAT(yes) ((yes) ? (O_CREAT|O_BINARY) : O_CREAT)
# define BIN_ASSIGN(fp, yes) setmode(fileno(fp), (yes) ? O_BINARY : O_TEXT)
# define PATH_SEP '\\'
#elif defined(CYGWIN)
# define BIN_READ(yes)  ((yes) ? "rb" : "rt")
# define BIN_WRITE(yes) ((yes) ? "wb" : "w")
# define BIN_CREAT(yes) ((yes) ? (O_CREAT|O_BINARY) : O_CREAT)
# define BIN_ASSIGN(fp, yes) ((yes) ? (void) setmode(fileno(fp), O_BINARY) : (void) (fp))
# define PATH_SEP '/'
#else
# ifdef VMS
#  define BIN_READ(dummy)  "r"
#  define BIN_WRITE(dummy) "w"
#  define BIN_CREAT(dummy) O_CREAT
#  define BIN_ASSIGN(fp, dummy) fp
#  define PATH_SEP ']'
#  define FILE_SEP '.'
# else
#  define BIN_READ(dummy)  "r"
#  define BIN_WRITE(dummy) "w"
#  define BIN_CREAT(dummy) O_CREAT
#  define BIN_ASSIGN(fp, dummy) fp
#  define PATH_SEP '/'
# endif
#endif

/* open has only to arguments on the Mac */
#if __MWERKS__
# define OPEN(name, mode, umask) open(name, mode)
#else
# define OPEN(name, mode, umask) open(name, mode, umask)
#endif

#ifdef AMIGA
# define STRNCMP(s1, s2, l) strncmp(s1, s2, (size_t)l)
#else
# define STRNCMP(s1, s2, l) strncmp(s1, s2, l)
#endif

#ifndef __P
# if defined(__STDC__) || defined(WIN32)
#  define __P(a) a
# else
#  define __P(a) ()
# endif
#endif

#define TRY_SEEK	/* attempt to use lseek, or skip forward by reading */
#define COLS 256	/* change here, if you ever need more columns */

/*
 * LLEN is the maximum length of a line; other than the visible characters
 * we need to consider also the escape color sequence prologue/epilogue ,
 * (11 bytes for each character). The most larger format is the default one:
 * addr + 1 word for each col/2 + 1 char for each col
 *
 *   addr        1st group       2nd group
 * +-------+ +-----------------+ +------+
 * 01234567: 1234 5678 9abc def0 12345678
 *
 * - addr: typically 012345678:    -> from 10 up to 18 bytes (including trailing
 *                                    space)
 * - 1st group: 1234 5678 9abc ... -> each byte may be colored, so add 11
 *                                    for each byte
 * - space                         -> 1 byte
 * - 2nd group: 12345678           -> each char may be colore so add 11
 *                                    for each byte
 * - new line                      -> 1 byte
 * - zero (end line)               -> 1 byte
 */
#define LLEN (2*(int)sizeof(unsigned long) + 2 +     /* addr + ": " */ \
             (11 * 2 + 4 + 1) * (COLS / 2) +         /* 1st group */   \
	     1 +                                     /* space */       \
	     (1 + 11) * COLS +                       /* 2nd group */   \
	     1 +                                     /* new line */    \
	     1)                                      /* zero */

char hexxa[] = "0123456789abcdef0123456789ABCDEF", *hexx = hexxa;

/* the different hextypes known by this program: */
#define HEX_NORMAL 0
#define HEX_POSTSCRIPT 1
#define HEX_CINCLUDE 2
#define HEX_BITS 3		/* not hex a dump, but bits: 01111001 */
#define HEX_LITTLEENDIAN 4

#define CONDITIONAL_CAPITALIZE(c) (capitalize ? toupper((unsigned char)(c)) : (c))

#define COLOR_PROLOGUE \
l[c++] = '\033'; \
l[c++] = '['; \
l[c++] = '1'; \
l[c++] = ';'; \
l[c++] = '3';

#define COLOR_EPILOGUE \
l[c++] = '\033'; \
l[c++] = '['; \
l[c++] = '0'; \
l[c++] = 'm';
#define COLOR_RED '1'
#define COLOR_GREEN '2'
#define COLOR_YELLOW '3'
#define COLOR_BLUE '4'
#define COLOR_WHITE '7'

static char *pname;

  static void
exit_with_usage(void)
{
  fprintf(stderr, "Usage:\n       %s [options] [infile [outfile]]\n", pname);
  fprintf(stderr, "    or\n       %s -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]\n", pname);
  fprintf(stderr, "Options:\n");
  fprintf(stderr, "    -a          toggle autoskip: A single '*' replaces nul-lines. Default off.\n");
  fprintf(stderr, "    -b          binary digit dump (incompatible with -ps,-i). Default hex.\n");
  fprintf(stderr, "    -C          capitalize variable names in C include file style (-i).\n");
  fprintf(stderr, "    -c cols     format <cols> octets per line. Default 16 (-i: 12, -ps: 30).\n");
  fprintf(stderr, "    -E          show characters in EBCDIC. Default ASCII.\n");
  fprintf(stderr, "    -e          little-endian dump (incompatible with -ps,-i,-r).\n");
  fprintf(stderr, "    -g bytes    number of octets per group in normal output. Default 2 (-e: 4).\n");
  fprintf(stderr, "    -h          print this summary.\n");
  fprintf(stderr, "    -i          output in C include file style.\n");
  fprintf(stderr, "    -l len      stop after <len> octets.\n");
  fprintf(stderr, "    -n name     set the variable name used in C include output (-i).\n");
  fprintf(stderr, "    -o off      add <off> to the displayed file position.\n");
  fprintf(stderr, "    -ps         output in postscript plain hexdump style.\n");
  fprintf(stderr, "    -r          reverse operation: convert (or patch) hexdump into binary.\n");
  fprintf(stderr, "    -r -s off   revert with <off> added to file positions found in hexdump.\n");
  fprintf(stderr, "    -d          show offset in decimal instead of hex.\n");
  fprintf(stderr, "    -s %sseek  start at <seek> bytes abs. %sinfile offset.\n",
#ifdef TRY_SEEK
	  "[+][-]", "(or +: rel.) ");
#else
	  "", "");
#endif
  fprintf(stderr, "    -u          use upper case hex letters.\n");
  fprintf(stderr, "    -R when     colorize the output; <when> can be 'always', 'auto' or 'never'. Default: 'auto'.\n"),
  fprintf(stderr, "    -v          show version: \"%s%s\".\n", version, osver);
  exit(1);
}

  static void
perror_exit(int ret)
{
  fprintf(stderr, "%s: ", pname);
  perror(NULL);
  exit(ret);
}

  static void
error_exit(int ret, char *msg)
{
  fprintf(stderr, "%s: %s\n", pname, msg);
  exit(ret);
}

  static int
getc_or_die(FILE *fpi)
{
  int c = getc(fpi);
  if (c == EOF && ferror(fpi))
    perror_exit(2);
  return c;
}

  static void
putc_or_die(int c, FILE *fpo)
{
  if (putc(c, fpo) == EOF)
    perror_exit(3);
}

  static void
fputs_or_die(char *s, FILE *fpo)
{
  if (fputs(s, fpo) == EOF)
    perror_exit(3);
}

/* Use a macro to allow for different arguments. */
#define FPRINTF_OR_DIE(args) if (fprintf args < 0) perror_exit(3)

  static void
fclose_or_die(FILE *fpi, FILE *fpo)
{
  if (fclose(fpo) != 0)
    perror_exit(3);
  if (fclose(fpi) != 0)
    perror_exit(2);
}

/*
 * If "c" is a hex digit, return the value.
 * Otherwise return -1.
 */
  static int
parse_hex_digit(int c)
{
  return (c >= '0' && c <= '9') ? c - '0'
	: (c >= 'a' && c <= 'f') ? c - 'a' + 10
	: (c >= 'A' && c <= 'F') ? c - 'A' + 10
	: -1;
}

/*
 * If "c" is a bin digit, return the value.
 * Otherwise return -1.
 */
  static int
parse_bin_digit(int c)
{
  return (c >= '0' && c <= '1') ? c - '0'
        : -1;
}

/*
 * Ignore text on "fpi" until end-of-line or end-of-file.
 * Return the '\n' or EOF character.
 * When an error is encountered exit with an error message.
 */
  static int
skip_to_eol(FILE *fpi, int c)
{
  while (c != '\n' && c != EOF)
    c = getc_or_die(fpi);
  return c;
}

/*
 * Max. cols binary characters are decoded from the input stream per line.
 * Two adjacent garbage characters after evaluated data delimit valid data.
 * Everything up to the next newline is discarded.
 *
 * The name is historic and came from 'undo type opt h'.
 */
  static int
huntype(
  FILE *fpi,
  FILE *fpo,
  int cols,
  int hextype,
  long base_off)
{
  int c, ign_garb = 1, n1 = -1, n2 = 0, n3 = 0, p = cols, bt = 0, b = 0, bcnt = 0;
  long have_off = 0, want_off = 0;

  rewind(fpi);

  while ((c = getc(fpi)) != EOF)
    {
      if (c == '\r')	/* Doze style input file? */
	continue;

      /* Allow multiple spaces.  This doesn't work when there is normal text
       * after the hex codes in the last line that looks like hex, thus only
       * use it for PostScript format. */
      if (hextype == HEX_POSTSCRIPT && (c == ' ' || c == '\n' || c == '\t'))
	continue;

      if (hextype == HEX_NORMAL || hextype == HEX_POSTSCRIPT)
        {
	  n3 = n2;
	  n2 = n1;

	  n1 = parse_hex_digit(c);
	  if (n1 == -1 && ign_garb)
	    continue;
        }
      else /* HEX_BITS */
        {
	  n1 = parse_hex_digit(c);
	  if (n1 == -1 && ign_garb)
	    continue;

          bt = parse_bin_digit(c);
          if (bt != -1)
            {
              b = ((b << 1) | bt);
              ++bcnt;
            }
        }

      ign_garb = 0;

      if ((hextype != HEX_POSTSCRIPT) && (p >= cols))
	{
          if (hextype == HEX_NORMAL)
            {
	      if (n1 < 0)
	        {
	          p = 0;
	          continue;
	        }
	      want_off = (want_off << 4) | n1;
            }
          else /* HEX_BITS */
            {
	      if (n1 < 0)
	        {
	          p = 0;
                  bcnt = 0;
	          continue;
	        }
	      want_off = (want_off << 4) | n1;
            }
          continue;
        }

      if (base_off + want_off != have_off)
	{
	  if (fflush(fpo) != 0)
	    perror_exit(3);
#ifdef TRY_SEEK
	  if (fseek(fpo, base_off + want_off - have_off, SEEK_CUR) >= 0)
	    have_off = base_off + want_off;
#endif
	  if (base_off + want_off < have_off)
	    error_exit(5, "Sorry, cannot seek backwards.");
	  for (; have_off < base_off + want_off; have_off++)
	    putc_or_die(0, fpo);
	}

      if (hextype == HEX_NORMAL || hextype == HEX_POSTSCRIPT)
        {
          if (n2 >= 0 && n1 >= 0)
            {
              putc_or_die((n2 << 4) | n1, fpo);
              have_off++;
              want_off++;
              n1 = -1;
              if (!hextype && (++p >= cols))
              /* skip the rest of the line as garbage */
              c = skip_to_eol(fpi, c);
            }
          else if (n1 < 0 && n2 < 0 && n3 < 0)
            /* already stumbled into garbage, skip line, wait and see */
            c = skip_to_eol(fpi, c);
        }
      else /* HEX_BITS */
        {
          if (bcnt == 8)
            {
              putc_or_die(b, fpo);
              have_off++;
              want_off++;
              b = 0;
              bcnt = 0;
              if (++p >= cols)
                /* skip the rest of the line as garbage */
                 c = skip_to_eol(fpi, c);
            }
        }

      if (c == '\n')
	{
	  if (hextype == HEX_NORMAL || hextype == HEX_BITS)
	    want_off = 0;
	  p = cols;
	  ign_garb = 1;
	}
    }
  if (fflush(fpo) != 0)
    perror_exit(3);
#ifdef TRY_SEEK
  fseek(fpo, 0L, SEEK_END);
#endif
  fclose_or_die(fpi, fpo);
  return 0;
}

/*
 * Print line l. If nz is false, xxdline regards the line a line of
 * zeroes. If there are three or more consecutive lines of zeroes,
 * they are replaced by a single '*' character.
 *
 * If the output ends with more than two lines of zeroes, you
 * should call xxdline again with l being the last line and nz
 * negative. This ensures that the last line is shown even when
 * it is all zeroes.
 *
 * If nz is always positive, lines are never suppressed.
 */
  static void
xxdline(FILE *fp, char *l, int nz)
{
  static char z[LLEN+1];
  static int zero_seen = 0;

  if (!nz && zero_seen == 1)
    strcpy(z, l);

  if (nz || !zero_seen++)
    {
      if (nz)
	{
	  if (nz < 0)
	    zero_seen--;
	  if (zero_seen == 2)
	    fputs_or_die(z, fp);
	  if (zero_seen > 2)
	    fputs_or_die("*\n", fp);
	}
      if (nz >= 0 || zero_seen > 0)
	fputs_or_die(l, fp);
      if (nz)
	zero_seen = 0;
    }
}

/* This is an EBCDIC to ASCII conversion table */
/* from a proposed BTL standard April 16, 1979 */
static unsigned char etoa64[] =
{
    0040,0240,0241,0242,0243,0244,0245,0246,
    0247,0250,0325,0056,0074,0050,0053,0174,
    0046,0251,0252,0253,0254,0255,0256,0257,
    0260,0261,0041,0044,0052,0051,0073,0176,
    0055,0057,0262,0263,0264,0265,0266,0267,
    0270,0271,0313,0054,0045,0137,0076,0077,
    0272,0273,0274,0275,0276,0277,0300,0301,
    0302,0140,0072,0043,0100,0047,0075,0042,
    0303,0141,0142,0143,0144,0145,0146,0147,
    0150,0151,0304,0305,0306,0307,0310,0311,
    0312,0152,0153,0154,0155,0156,0157,0160,
    0161,0162,0136,0314,0315,0316,0317,0320,
    0321,0345,0163,0164,0165,0166,0167,0170,
    0171,0172,0322,0323,0324,0133,0326,0327,
    0330,0331,0332,0333,0334,0335,0336,0337,
    0340,0341,0342,0343,0344,0135,0346,0347,
    0173,0101,0102,0103,0104,0105,0106,0107,
    0110,0111,0350,0351,0352,0353,0354,0355,
    0175,0112,0113,0114,0115,0116,0117,0120,
    0121,0122,0356,0357,0360,0361,0362,0363,
    0134,0237,0123,0124,0125,0126,0127,0130,
    0131,0132,0364,0365,0366,0367,0370,0371,
    0060,0061,0062,0063,0064,0065,0066,0067,
    0070,0071,0372,0373,0374,0375,0376,0377
};

  static void
begin_coloring_char (char *l, int *c, int e, int ebcdic)
{
  if (ebcdic)
    {
      if ((e >= 75 && e <= 80) || (e >= 90 && e <= 97) ||
          (e >= 107 && e <= 111) || (e >= 121 && e <= 127) ||
          (e >= 129 && e <= 137) || (e >= 145 && e <= 154) ||
          (e >= 162 && e <= 169) || (e >= 192 && e <= 201) ||
          (e >= 208 && e <= 217) || (e >= 226 && e <= 233) ||
          (e >= 240 && e <= 249) || (e == 189) || (e == 64) ||
          (e == 173) || (e == 224) )
        l[(*c)++] = COLOR_GREEN;

      else if (e == 37 || e == 13 || e == 5)
        l[(*c)++] = COLOR_YELLOW;
      else if (e == 0)
        l[(*c)++] = COLOR_WHITE;
      else if (e == 255)
        l[(*c)++] = COLOR_BLUE;
      else
        l[(*c)++] = COLOR_RED;
    }
  else  /* ASCII */
    {
      #if defined(__MVS__) && __CHARSET_LIB == 0
      if (e >= 64)
        l[(*c)++] = COLOR_GREEN;
      #else
      if (e > 31 && e < 127)
        l[(*c)++] = COLOR_GREEN;
      #endif

      else if (e == 9 || e == 10 || e == 13)
        l[(*c)++] = COLOR_YELLOW;
      else if (e == 0)
        l[(*c)++] = COLOR_WHITE;
      else if (e == 255)
        l[(*c)++] = COLOR_BLUE;
      else
        l[(*c)++] = COLOR_RED;
    }
  l[(*c)++] = 'm';
}

  static int
enable_color(void)
{
#ifdef WIN32
  DWORD   mode;
  HANDLE  out;

  if (!isatty(1))
    return 0;

  out = GetStdHandle(STD_OUTPUT_HANDLE);
  GetConsoleMode(out, &mode);
  mode |= ENABLE_VIRTUAL_TERMINAL_PROCESSING;
  return (int)SetConsoleMode(out, mode);
#elif defined(UNIX)
  return isatty(STDOUT_FILENO);
#else
  return 0;
#endif
}

  int
main(int argc, char *argv[])
{
  FILE *fp, *fpo;
  int c, e, p = 0, relseek = 1, negseek = 0, revert = 0, i, x;
  int cols = 0, colsgiven = 0, nonzero = 0, autoskip = 0, hextype = HEX_NORMAL;
  int capitalize = 0, decimal_offset = 0;
  int ebcdic = 0;
  int octspergrp = -1;	/* number of octets grouped in output */
  int grplen;		/* total chars per octet group */
  long length = -1, n = 0, seekoff = 0;
  unsigned long displayoff = 0;
  static char l[LLEN+1];  /* static because it may be too big for stack */
  char *pp;
  char *varname = NULL;
  int addrlen = 9;
  int color = 0;
  char *no_color;

  no_color = getenv("NO_COLOR");
  if (no_color == NULL || no_color[0] == '\0')
    color = enable_color();

#ifdef AMIGA
  /* This program doesn't work when started from the Workbench */
  if (argc == 0)
    exit(1);
#endif

  pname = argv[0];
  for (pp = pname; *pp; )
    if (*pp++ == PATH_SEP)
      pname = pp;
#ifdef FILE_SEP
  for (pp = pname; *pp; pp++)
    if (*pp == FILE_SEP)
      {
	*pp = '\0';
	break;
      }
#endif

  while (argc >= 2)
    {
      pp = argv[1] + (!STRNCMP(argv[1], "--", 2) && argv[1][2]);
	   if (!STRNCMP(pp, "-a", 2)) autoskip = 1 - autoskip;
      else if (!STRNCMP(pp, "-b", 2)) hextype = HEX_BITS;
      else if (!STRNCMP(pp, "-e", 2)) hextype = HEX_LITTLEENDIAN;
      else if (!STRNCMP(pp, "-u", 2)) hexx = hexxa + 16;
      else if (!STRNCMP(pp, "-p", 2)) hextype = HEX_POSTSCRIPT;
      else if (!STRNCMP(pp, "-i", 2)) hextype = HEX_CINCLUDE;
      else if (!STRNCMP(pp, "-C", 2)) capitalize = 1;
      else if (!STRNCMP(pp, "-d", 2)) decimal_offset = 1;
      else if (!STRNCMP(pp, "-r", 2)) revert++;
      else if (!STRNCMP(pp, "-E", 2)) ebcdic++;
      else if (!STRNCMP(pp, "-v", 2))
	{
	  fprintf(stderr, "%s%s\n", version, osver);
	  exit(0);
	}
      else if (!STRNCMP(pp, "-c", 2))
	{
	  if (pp[2] && !STRNCMP("apitalize", pp + 2, 9))
	    capitalize = 1;
	  else if (pp[2] && STRNCMP("ols", pp + 2, 3))
	    {
	      colsgiven = 1;
	      cols = (int)strtol(pp + 2, NULL, 0);
	    }
	  else
	    {
	      if (!argv[2])
		exit_with_usage();
	      colsgiven = 1;
	      cols = (int)strtol(argv[2], NULL, 0);
	      argv++;
	      argc--;
	    }
	}
      else if (!STRNCMP(pp, "-g", 2))
	{
	  if (pp[2] && STRNCMP("roup", pp + 2, 4))
	    octspergrp = (int)strtol(pp + 2, NULL, 0);
	  else
	    {
	      if (!argv[2])
		exit_with_usage();
	      octspergrp = (int)strtol(argv[2], NULL, 0);
	      argv++;
	      argc--;
	    }
	}
      else if (!STRNCMP(pp, "-o", 2))
	{
	  int reloffset = 0;
	  int negoffset = 0;
	  if (pp[2] && STRNCMP("ffset", pp + 2, 5))
	    displayoff = strtoul(pp + 2, NULL, 0);
	  else
	    {
	      if (!argv[2])
		exit_with_usage();

	      if (argv[2][0] == '+')
	       reloffset++;
	     if (argv[2][reloffset] == '-')
	       negoffset++;

	     if (negoffset)
	       displayoff = ULONG_MAX - strtoul(argv[2] + reloffset+negoffset, NULL, 0) + 1;
	     else
	       displayoff = strtoul(argv[2] + reloffset+negoffset, NULL, 0);

	      argv++;
	      argc--;
	    }
	}
      else if (!STRNCMP(pp, "-s", 2))
	{
	  relseek = 0;
	  negseek = 0;
	  if (pp[2] && STRNCMP("kip", pp+2, 3) && STRNCMP("eek", pp+2, 3))
	    {
#ifdef TRY_SEEK
	      if (pp[2] == '+')
		relseek++;
	      if (pp[2+relseek] == '-')
		negseek++;
#endif
	      seekoff = strtol(pp + 2+relseek+negseek, (char **)NULL, 0);
	    }
	  else
	    {
	      if (!argv[2])
		exit_with_usage();
#ifdef TRY_SEEK
	      if (argv[2][0] == '+')
		relseek++;
	      if (argv[2][relseek] == '-')
		negseek++;
#endif
	      seekoff = strtol(argv[2] + relseek+negseek, (char **)NULL, 0);
	      argv++;
	      argc--;
	    }
	}
      else if (!STRNCMP(pp, "-l", 2))
	{
	  if (pp[2] && STRNCMP("en", pp + 2, 2))
	    length = strtol(pp + 2, (char **)NULL, 0);
	  else
	    {
	      if (!argv[2])
		exit_with_usage();
	      length = strtol(argv[2], (char **)NULL, 0);
	      argv++;
	      argc--;
	    }
	}
      else if (!STRNCMP(pp, "-n", 2))
        {
          if (pp[2] && STRNCMP("ame", pp + 2, 3))
            varname = pp + 2;
          else
            {
              if (!argv[2])
                exit_with_usage();
              varname = argv[2];
              argv++;
              argc--;
            }
        }
      else if (!STRNCMP(pp, "-R", 2))
        {
	  char *pw = pp + 2;
	  if (!pw[0])
	    {
	      pw = argv[2];
	      argv++;
	      argc--;
	    }
	  if (!pw)
	    exit_with_usage();
	  if (!STRNCMP(pw, "always", 6))
	    {
	      (void)enable_color();
	      color = 1;
	    }
	  else if (!STRNCMP(pw, "never", 5))
	    color = 0;
	  else if (!STRNCMP(pw, "auto", 4))
	    color = enable_color();
	  else
	    exit_with_usage();
        }
      else if (!strcmp(pp, "--"))	/* end of options */
	{
	  argv++;
	  argc--;
	  break;
	}
      else if (pp[0] == '-' && pp[1])	/* unknown option */
	exit_with_usage();
      else
	break;				/* not an option */

      argv++;				/* advance to next argument */
      argc--;
    }

  if (!colsgiven || (!cols && hextype != HEX_POSTSCRIPT))
    switch (hextype)
      {
      case HEX_POSTSCRIPT:	cols = 30; break;
      case HEX_CINCLUDE:	cols = 12; break;
      case HEX_BITS:		cols = 6; break;
      case HEX_NORMAL:
      case HEX_LITTLEENDIAN:
      default:			cols = 16; break;
      }

  if (octspergrp < 0)
    switch (hextype)
      {
      case HEX_BITS:		octspergrp = 1; break;
      case HEX_NORMAL:		octspergrp = 2; break;
      case HEX_LITTLEENDIAN:	octspergrp = 4; break;
      case HEX_POSTSCRIPT:
      case HEX_CINCLUDE:
      default:			octspergrp = 0; break;
      }

  if ((hextype == HEX_POSTSCRIPT && cols < 0) ||
      (hextype != HEX_POSTSCRIPT && cols < 1) ||
      ((hextype == HEX_NORMAL || hextype == HEX_BITS || hextype == HEX_LITTLEENDIAN)
							    && (cols > COLS)))
    {
      fprintf(stderr, "%s: invalid number of columns (max. %d).\n", pname, COLS);
      exit(1);
    }

  if (octspergrp < 1 || octspergrp > cols)
    octspergrp = cols;
  else if (hextype == HEX_LITTLEENDIAN && (octspergrp & (octspergrp-1)))
    error_exit(1, "number of octets per group must be a power of 2 with -e.");

  if (argc > 3)
    exit_with_usage();

  if (argc == 1 || (argv[1][0] == '-' && !argv[1][1]))
    BIN_ASSIGN(fp = stdin, !revert);
  else
    {
      if ((fp = fopen(argv[1], BIN_READ(!revert))) == NULL)
	{
	  fprintf(stderr,"%s: ", pname);
	  perror(argv[1]);
	  return 2;
	}
    }

  if (argc < 3 || (argv[2][0] == '-' && !argv[2][1]))
    BIN_ASSIGN(fpo = stdout, revert);
  else
    {
      int fd;
      int mode = revert ? O_WRONLY : (O_TRUNC|O_WRONLY);

      if (((fd = OPEN(argv[2], mode | BIN_CREAT(revert), 0666)) < 0) ||
	  (fpo = fdopen(fd, BIN_WRITE(revert))) == NULL)
	{
	  fprintf(stderr, "%s: ", pname);
	  perror(argv[2]);
	  return 3;
	}
      rewind(fpo);
    }
#ifdef __MVS__
  // Disable auto-conversion on input file descriptors
  __disableautocvt(fileno(fp));
#endif

  if (revert)
    switch (hextype)
      {
      case HEX_NORMAL:
      case HEX_POSTSCRIPT:
      case HEX_BITS:
        return huntype(fp, fpo, cols, hextype,
          negseek ? -seekoff : seekoff);
        break;
      default:
        error_exit(-1, "Sorry, cannot revert this type of hexdump");
      }

  if (seekoff || negseek || !relseek)
    {
#ifdef TRY_SEEK
      if (relseek)
	e = fseek(fp, negseek ? -seekoff : seekoff, SEEK_CUR);
      else
	e = fseek(fp, negseek ? -seekoff : seekoff,
						negseek ? SEEK_END : SEEK_SET);
      if (e < 0 && negseek)
	error_exit(4, "Sorry, cannot seek.");
      if (e >= 0)
	seekoff = ftell(fp);
      else
#endif
	{
	  long s = seekoff;

	  while (s--)
	    if (getc_or_die(fp) == EOF)
	    {
	      error_exit(4, "Sorry, cannot seek.");
	    }
	}
    }

  if (hextype == HEX_CINCLUDE)
    {
      /* A user-set variable name overrides fp == stdin */
      if (varname == NULL && fp != stdin)
        varname = argv[1];

      if (varname != NULL)
	{
	  FPRINTF_OR_DIE((fpo, "unsigned char %s", isdigit((unsigned char)varname[0]) ? "__" : ""));
	  for (e = 0; (c = varname[e]) != 0; e++)
	    putc_or_die(isalnum((unsigned char)c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo);
	  fputs_or_die("[] = {\n", fpo);
	}

      p = 0;
      while ((length < 0 || p < length) && (c = getc_or_die(fp)) != EOF)
	{
	  FPRINTF_OR_DIE((fpo, (hexx == hexxa) ? "%s0x%02x" : "%s0X%02X",
		(p % cols) ? ", " : (!p ? "  " : ",\n  "), c));
	  p++;
	}

      if (p)
	fputs_or_die("\n", fpo);

      if (varname != NULL)
	{
	  fputs_or_die("};\n", fpo);
	  FPRINTF_OR_DIE((fpo, "unsigned int %s", isdigit((unsigned char)varname[0]) ? "__" : ""));
	  for (e = 0; (c = varname[e]) != 0; e++)
	    putc_or_die(isalnum((unsigned char)c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo);
	  FPRINTF_OR_DIE((fpo, "_%s = %d;\n", capitalize ? "LEN" : "len", p));
	}

      fclose_or_die(fp, fpo);
      return 0;
    }

  if (hextype == HEX_POSTSCRIPT)
    {
      p = cols;
      while ((length < 0 || n < length) && (e = getc_or_die(fp)) != EOF)
	{
	  putc_or_die(hexx[(e >> 4) & 0xf], fpo);
	  putc_or_die(hexx[e & 0xf], fpo);
	  n++;
	  if (cols > 0 && !--p)
	    {
	      putc_or_die('\n', fpo);
	      p = cols;
	    }
	}
      if (cols == 0 || p < cols)
	putc_or_die('\n', fpo);
      fclose_or_die(fp, fpo);
      return 0;
    }

  /* hextype: HEX_NORMAL or HEX_BITS or HEX_LITTLEENDIAN */

  if (hextype != HEX_BITS)
    {
      grplen = octspergrp + octspergrp + 1;	/* chars per octet group */
      if (color)
        grplen += 11 * octspergrp;  /* color-code needs 11 extra characters */
    }
  else	/* hextype == HEX_BITS */
    grplen = 8 * octspergrp + 1;

  while ((length < 0 || n < length) && (e = getc_or_die(fp)) != EOF)
    {
      if (p == 0)
	{
	  addrlen = sprintf(l, decimal_offset ? "%08ld:" : "%08lx:",
				  ((unsigned long)(n + seekoff + displayoff)));
	  for (c = addrlen; c < LLEN; l[c++] = ' ')
	    ;
	}
      x = hextype == HEX_LITTLEENDIAN ? p ^ (octspergrp-1) : p;
      c = addrlen + 1 + (grplen * x) / octspergrp;
      if (hextype == HEX_NORMAL || hextype == HEX_LITTLEENDIAN)
	{
          if (color)
            {
	      COLOR_PROLOGUE
	      begin_coloring_char(l,&c,e,ebcdic);
	      l[c++] = hexx[(e >> 4) & 0xf];
	      l[c++] = hexx[e & 0xf];
	      COLOR_EPILOGUE
	    }
          else /*No colors*/
	    {
	      l[c]   = hexx[(e >> 4) & 0xf];
	      l[++c] = hexx[e & 0xf];
	    }
	}
      else /* hextype == HEX_BITS */
	{
	  for (i = 7; i >= 0; i--)
	    l[c++] = (e & (1 << i)) ? '1' : '0';
	}
      if (e)
	nonzero++;
      /* When changing this update definition of LLEN above. */
      if (hextype == HEX_LITTLEENDIAN)
	/* last group will be fully used, round up */
	c = grplen * ((cols + octspergrp - 1) / octspergrp);
      else
	c = (grplen * cols - 1) / octspergrp;

      if (color)
        {
          if (hextype == HEX_BITS)
            c += addrlen + 3 + p*12;
          else
            c = addrlen + 3 + (grplen * cols - 1)/octspergrp + p*12;

          if (hextype == HEX_LITTLEENDIAN)
            c += 1;

          COLOR_PROLOGUE
          begin_coloring_char(l,&c,e,ebcdic);
#if defined(__MVS__) && __CHARSET_LIB == 0
          if (e >= 64)
            l[c++] = e;
          else
            l[c++] = '.';
#else
          if (ebcdic)
            e = (e < 64) ? '.' : etoa64[e-64];
          l[c++] = (e > 31 && e < 127) ? e : '.';
#endif
          COLOR_EPILOGUE
          n++;
          if (++p == cols)
            {
              l[c++] = '\n';
              l[c++] = '\0';
              xxdline(fpo, l, autoskip ? nonzero : 1);
              nonzero = 0;
              p = 0;
            }
        }
      else /*no colors*/
        {
          if (ebcdic)
            e = (e < 64) ? '.' : etoa64[e-64];

          c += addrlen + 3 + p;
          l[c++] =
#if defined(__MVS__) && __CHARSET_LIB == 0
              (e >= 64)
#else
              (e > 31 && e < 127)
#endif
              ? e : '.';
          n++;
          if (++p == cols)
            {
              l[c++] = '\n';
              l[c] = '\0';
              xxdline(fpo, l, autoskip ? nonzero : 1);
              nonzero = 0;
              p = 0;
            }
        }
    }
  if (p)
    {
      l[c++] = '\n';
      l[c] = '\0';
      if (color)
        {
          c++;

          x = p;
          if (hextype == HEX_LITTLEENDIAN)
            {
              int fill = octspergrp - (p % octspergrp);
              if (fill == octspergrp) fill = 0;

              c = addrlen + 1 + (grplen * (x - (octspergrp-fill))) / octspergrp;

              for (i = 0; i < fill;i++)
                {
                  COLOR_PROLOGUE
                  l[c++] = COLOR_RED;
                  l[c++] = 'm';
                  l[c++] = ' '; /* empty space */
                  COLOR_EPILOGUE
                  x++;
                  p++;
                }
            }

          if (hextype != HEX_BITS)
            {
              c = addrlen + 1 + (grplen * x) / octspergrp;
              c += cols - p;
              c += (cols - p) / octspergrp;

              for (i = cols - p; i > 0;i--)
                {
                  COLOR_PROLOGUE
                  l[c++] = COLOR_RED;
                  l[c++] = 'm';
                  l[c++] = ' '; /* empty space */
                  COLOR_EPILOGUE
                }
            }
        }
      xxdline(fpo, l, 1);
    }
  else if (autoskip)
    xxdline(fpo, l, -1);	/* last chance to flush out suppressed lines */

  fclose_or_die(fp, fpo);
  return 0;
}

/* vi:set ts=8 sw=4 sts=2 cino+={2 cino+=n-2 : */