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  *
58  * (c) 1990-1998 by Juergen Weigert (jnweiger@gmail.com)
59  *
60  * I hereby grant permission to distribute and use xxd
61  * under X11-MIT or GPL-2.0 (at the user's choice).
62  *
63  * Contributions by Bram Moolenaar et al.
64  */
65 
66 /* Visual Studio 2005 has 'deprecated' many of the standard CRT functions */
67 #if _MSC_VER >= 1400
68 # define _CRT_SECURE_NO_DEPRECATE
69 # define _CRT_NONSTDC_NO_DEPRECATE
70 #endif
71 #if !defined(CYGWIN) && defined(__CYGWIN__)
72 # define CYGWIN
73 #endif
74 
75 #include <stdio.h>
76 #ifdef VAXC
77 # include <file.h>
78 #else
79 # include <fcntl.h>
80 #endif
81 #if defined(WIN32) || defined(CYGWIN)
82 # include <io.h>	/* for setmode() */
83 #else
84 # ifdef UNIX
85 #  include <unistd.h>
86 # endif
87 #endif
88 #include <stdlib.h>
89 #include <string.h>	/* for strncmp() */
90 #include <ctype.h>	/* for isalnum() */
91 #include <limits.h>
92 #if __MWERKS__ && !defined(BEBOX)
93 # include <unix.h>	/* for fdopen() on MAC */
94 #endif
95 
96 
97 /*  This corrects the problem of missing prototypes for certain functions
98  *  in some GNU installations (e.g. SunOS 4.1.x).
99  *  Darren Hiebert <darren@hmi.com> (sparc-sun-sunos4.1.3_U1/2.7.2.2)
100  */
101 #if defined(__GNUC__) && defined(__STDC__)
102 # ifndef __USE_FIXED_PROTOTYPES__
103 #  define __USE_FIXED_PROTOTYPES__
104 # endif
105 #endif
106 
107 #ifndef __USE_FIXED_PROTOTYPES__
108 /*
109  * This is historic and works only if the compiler really has no prototypes:
110  *
111  * Include prototypes for Sun OS 4.x, when using an ANSI compiler.
112  * FILE is defined on OS 4.x, not on 5.x (Solaris).
113  * if __SVR4 is defined (some Solaris versions), don't include this.
114  */
115 #if defined(sun) && defined(FILE) && !defined(__SVR4) && defined(__STDC__)
116 #  define __P(a) a
117 /* excerpt from my sun_stdlib.h */
118 extern int fprintf __P((FILE *, char *, ...));
119 extern int fputs   __P((char *, FILE *));
120 extern int _flsbuf __P((unsigned char, FILE *));
121 extern int _filbuf __P((FILE *));
122 extern int fflush  __P((FILE *));
123 extern int fclose  __P((FILE *));
124 extern int fseek   __P((FILE *, long, int));
125 extern int rewind  __P((FILE *));
126 
127 extern void perror __P((char *));
128 # endif
129 #endif
130 
131 extern long int strtol();
132 extern long int ftell();
133 
134 char version[] = "xxd 2021-10-22 by Juergen Weigert et al.";
135 #ifdef WIN32
136 char osver[] = " (Win32)";
137 #else
138 char osver[] = "";
139 #endif
140 
141 #if defined(WIN32)
142 # define BIN_READ(yes)  ((yes) ? "rb" : "rt")
143 # define BIN_WRITE(yes) ((yes) ? "wb" : "wt")
144 # define BIN_CREAT(yes) ((yes) ? (O_CREAT|O_BINARY) : O_CREAT)
145 # define BIN_ASSIGN(fp, yes) setmode(fileno(fp), (yes) ? O_BINARY : O_TEXT)
146 # define PATH_SEP '\\'
147 #elif defined(CYGWIN)
148 # define BIN_READ(yes)  ((yes) ? "rb" : "rt")
149 # define BIN_WRITE(yes) ((yes) ? "wb" : "w")
150 # define BIN_CREAT(yes) ((yes) ? (O_CREAT|O_BINARY) : O_CREAT)
151 # define BIN_ASSIGN(fp, yes) ((yes) ? (void) setmode(fileno(fp), O_BINARY) : (void) (fp))
152 # define PATH_SEP '/'
153 #else
154 # ifdef VMS
155 #  define BIN_READ(dummy)  "r"
156 #  define BIN_WRITE(dummy) "w"
157 #  define BIN_CREAT(dummy) O_CREAT
158 #  define BIN_ASSIGN(fp, dummy) fp
159 #  define PATH_SEP ']'
160 #  define FILE_SEP '.'
161 # else
162 #  define BIN_READ(dummy)  "r"
163 #  define BIN_WRITE(dummy) "w"
164 #  define BIN_CREAT(dummy) O_CREAT
165 #  define BIN_ASSIGN(fp, dummy) fp
166 #  define PATH_SEP '/'
167 # endif
168 #endif
169 
170 /* open has only to arguments on the Mac */
171 #if __MWERKS__
172 # define OPEN(name, mode, umask) open(name, mode)
173 #else
174 # define OPEN(name, mode, umask) open(name, mode, umask)
175 #endif
176 
177 #ifdef AMIGA
178 # define STRNCMP(s1, s2, l) strncmp(s1, s2, (size_t)l)
179 #else
180 # define STRNCMP(s1, s2, l) strncmp(s1, s2, l)
181 #endif
182 
183 #ifndef __P
184 # if defined(__STDC__) || defined(WIN32)
185 #  define __P(a) a
186 # else
187 #  define __P(a) ()
188 # endif
189 #endif
190 
191 #define TRY_SEEK	/* attempt to use lseek, or skip forward by reading */
192 #define COLS 256	/* change here, if you ever need more columns */
193 #define LLEN ((2*(int)sizeof(unsigned long)) + 4 + (9*COLS-1) + COLS + 2)
194 
195 char hexxa[] = "0123456789abcdef0123456789ABCDEF", *hexx = hexxa;
196 
197 /* the different hextypes known by this program: */
198 #define HEX_NORMAL 0
199 #define HEX_POSTSCRIPT 1
200 #define HEX_CINCLUDE 2
201 #define HEX_BITS 3		/* not hex a dump, but bits: 01111001 */
202 #define HEX_LITTLEENDIAN 4
203 
204 #define CONDITIONAL_CAPITALIZE(c) (capitalize ? toupper((int)c) : c)
205 
206 static char *pname;
207 
208   static void
exit_with_usage(void)209 exit_with_usage(void)
210 {
211   fprintf(stderr, "Usage:\n       %s [options] [infile [outfile]]\n", pname);
212   fprintf(stderr, "    or\n       %s -r [-s [-]offset] [-c cols] [-ps] [infile [outfile]]\n", pname);
213   fprintf(stderr, "Options:\n");
214   fprintf(stderr, "    -a          toggle autoskip: A single '*' replaces nul-lines. Default off.\n");
215   fprintf(stderr, "    -b          binary digit dump (incompatible with -ps,-i,-r). Default hex.\n");
216   fprintf(stderr, "    -C          capitalize variable names in C include file style (-i).\n");
217   fprintf(stderr, "    -c cols     format <cols> octets per line. Default 16 (-i: 12, -ps: 30).\n");
218   fprintf(stderr, "    -E          show characters in EBCDIC. Default ASCII.\n");
219   fprintf(stderr, "    -e          little-endian dump (incompatible with -ps,-i,-r).\n");
220   fprintf(stderr, "    -g bytes    number of octets per group in normal output. Default 2 (-e: 4).\n");
221   fprintf(stderr, "    -h          print this summary.\n");
222   fprintf(stderr, "    -i          output in C include file style.\n");
223   fprintf(stderr, "    -l len      stop after <len> octets.\n");
224   fprintf(stderr, "    -o off      add <off> to the displayed file position.\n");
225   fprintf(stderr, "    -ps         output in postscript plain hexdump style.\n");
226   fprintf(stderr, "    -r          reverse operation: convert (or patch) hexdump into binary.\n");
227   fprintf(stderr, "    -r -s off   revert with <off> added to file positions found in hexdump.\n");
228   fprintf(stderr, "    -d          show offset in decimal instead of hex.\n");
229   fprintf(stderr, "    -s %sseek  start at <seek> bytes abs. %sinfile offset.\n",
230 #ifdef TRY_SEEK
231 	  "[+][-]", "(or +: rel.) ");
232 #else
233 	  "", "");
234 #endif
235   fprintf(stderr, "    -u          use upper case hex letters.\n");
236   fprintf(stderr, "    -v          show version: \"%s%s\".\n", version, osver);
237   exit(1);
238 }
239 
240   static void
perror_exit(int ret)241 perror_exit(int ret)
242 {
243   fprintf(stderr, "%s: ", pname);
244   perror(NULL);
245   exit(ret);
246 }
247 
248   static void
error_exit(int ret,char * msg)249 error_exit(int ret, char *msg)
250 {
251   fprintf(stderr, "%s: %s\n", pname, msg);
252   exit(ret);
253 }
254 
255   static int
getc_or_die(FILE * fpi)256 getc_or_die(FILE *fpi)
257 {
258   int c = getc(fpi);
259   if (c == EOF && ferror(fpi))
260     perror_exit(2);
261   return c;
262 }
263 
264   static void
putc_or_die(int c,FILE * fpo)265 putc_or_die(int c, FILE *fpo)
266 {
267   if (putc(c, fpo) == EOF)
268     perror_exit(3);
269 }
270 
271   static void
fputs_or_die(char * s,FILE * fpo)272 fputs_or_die(char *s, FILE *fpo)
273 {
274   if (fputs(s, fpo) == EOF)
275     perror_exit(3);
276 }
277 
278 /* Use a macro to allow for different arguments. */
279 #define FPRINTF_OR_DIE(args) if (fprintf args < 0) perror_exit(3)
280 
281   static void
fclose_or_die(FILE * fpi,FILE * fpo)282 fclose_or_die(FILE *fpi, FILE *fpo)
283 {
284   if (fclose(fpo) != 0)
285     perror_exit(3);
286   if (fclose(fpi) != 0)
287     perror_exit(2);
288 }
289 
290 /*
291  * If "c" is a hex digit, return the value.
292  * Otherwise return -1.
293  */
294   static int
parse_hex_digit(int c)295 parse_hex_digit(int c)
296 {
297   return (c >= '0' && c <= '9') ? c - '0'
298 	: (c >= 'a' && c <= 'f') ? c - 'a' + 10
299 	: (c >= 'A' && c <= 'F') ? c - 'A' + 10
300 	: -1;
301 }
302 
303 /*
304  * Ignore text on "fpi" until end-of-line or end-of-file.
305  * Return the '\n' or EOF character.
306  * When an error is encountered exit with an error message.
307  */
308   static int
skip_to_eol(FILE * fpi,int c)309 skip_to_eol(FILE *fpi, int c)
310 {
311   while (c != '\n' && c != EOF)
312     c = getc_or_die(fpi);
313   return c;
314 }
315 
316 /*
317  * Max. cols binary characters are decoded from the input stream per line.
318  * Two adjacent garbage characters after evaluated data delimit valid data.
319  * Everything up to the next newline is discarded.
320  *
321  * The name is historic and came from 'undo type opt h'.
322  */
323   static int
huntype(FILE * fpi,FILE * fpo,int cols,int hextype,long base_off)324 huntype(
325   FILE *fpi,
326   FILE *fpo,
327   int cols,
328   int hextype,
329   long base_off)
330 {
331   int c, ign_garb = 1, n1 = -1, n2 = 0, n3, p = cols;
332   long have_off = 0, want_off = 0;
333 
334   rewind(fpi);
335 
336   while ((c = getc(fpi)) != EOF)
337     {
338       if (c == '\r')	/* Doze style input file? */
339 	continue;
340 
341       /* Allow multiple spaces.  This doesn't work when there is normal text
342        * after the hex codes in the last line that looks like hex, thus only
343        * use it for PostScript format. */
344       if (hextype == HEX_POSTSCRIPT && (c == ' ' || c == '\n' || c == '\t'))
345 	continue;
346 
347       n3 = n2;
348       n2 = n1;
349 
350       n1 = parse_hex_digit(c);
351       if (n1 == -1 && ign_garb)
352 	continue;
353 
354       ign_garb = 0;
355 
356       if (!hextype && (p >= cols))
357 	{
358 	  if (n1 < 0)
359 	    {
360 	      p = 0;
361 	      continue;
362 	    }
363 	  want_off = (want_off << 4) | n1;
364 	  continue;
365 	}
366 
367       if (base_off + want_off != have_off)
368 	{
369 	  if (fflush(fpo) != 0)
370 	    perror_exit(3);
371 #ifdef TRY_SEEK
372 	  if (fseek(fpo, base_off + want_off - have_off, SEEK_CUR) >= 0)
373 	    have_off = base_off + want_off;
374 #endif
375 	  if (base_off + want_off < have_off)
376 	    error_exit(5, "Sorry, cannot seek backwards.");
377 	  for (; have_off < base_off + want_off; have_off++)
378 	    putc_or_die(0, fpo);
379 	}
380 
381       if (n2 >= 0 && n1 >= 0)
382 	{
383 	  putc_or_die((n2 << 4) | n1, fpo);
384 	  have_off++;
385 	  want_off++;
386 	  n1 = -1;
387 	  if (!hextype && (++p >= cols))
388 	    /* skip the rest of the line as garbage */
389 	    c = skip_to_eol(fpi, c);
390 	}
391       else if (n1 < 0 && n2 < 0 && n3 < 0)
392         /* already stumbled into garbage, skip line, wait and see */
393 	c = skip_to_eol(fpi, c);
394 
395       if (c == '\n')
396 	{
397 	  if (!hextype)
398 	    want_off = 0;
399 	  p = cols;
400 	  ign_garb = 1;
401 	}
402     }
403   if (fflush(fpo) != 0)
404     perror_exit(3);
405 #ifdef TRY_SEEK
406   fseek(fpo, 0L, SEEK_END);
407 #endif
408   fclose_or_die(fpi, fpo);
409   return 0;
410 }
411 
412 /*
413  * Print line l. If nz is false, xxdline regards the line a line of
414  * zeroes. If there are three or more consecutive lines of zeroes,
415  * they are replaced by a single '*' character.
416  *
417  * If the output ends with more than two lines of zeroes, you
418  * should call xxdline again with l being the last line and nz
419  * negative. This ensures that the last line is shown even when
420  * it is all zeroes.
421  *
422  * If nz is always positive, lines are never suppressed.
423  */
424   static void
xxdline(FILE * fp,char * l,int nz)425 xxdline(FILE *fp, char *l, int nz)
426 {
427   static char z[LLEN+1];
428   static int zero_seen = 0;
429 
430   if (!nz && zero_seen == 1)
431     strcpy(z, l);
432 
433   if (nz || !zero_seen++)
434     {
435       if (nz)
436 	{
437 	  if (nz < 0)
438 	    zero_seen--;
439 	  if (zero_seen == 2)
440 	    fputs_or_die(z, fp);
441 	  if (zero_seen > 2)
442 	    fputs_or_die("*\n", fp);
443 	}
444       if (nz >= 0 || zero_seen > 0)
445 	fputs_or_die(l, fp);
446       if (nz)
447 	zero_seen = 0;
448     }
449 }
450 
451 /* This is an EBCDIC to ASCII conversion table */
452 /* from a proposed BTL standard April 16, 1979 */
453 static unsigned char etoa64[] =
454 {
455     0040,0240,0241,0242,0243,0244,0245,0246,
456     0247,0250,0325,0056,0074,0050,0053,0174,
457     0046,0251,0252,0253,0254,0255,0256,0257,
458     0260,0261,0041,0044,0052,0051,0073,0176,
459     0055,0057,0262,0263,0264,0265,0266,0267,
460     0270,0271,0313,0054,0045,0137,0076,0077,
461     0272,0273,0274,0275,0276,0277,0300,0301,
462     0302,0140,0072,0043,0100,0047,0075,0042,
463     0303,0141,0142,0143,0144,0145,0146,0147,
464     0150,0151,0304,0305,0306,0307,0310,0311,
465     0312,0152,0153,0154,0155,0156,0157,0160,
466     0161,0162,0136,0314,0315,0316,0317,0320,
467     0321,0345,0163,0164,0165,0166,0167,0170,
468     0171,0172,0322,0323,0324,0133,0326,0327,
469     0330,0331,0332,0333,0334,0335,0336,0337,
470     0340,0341,0342,0343,0344,0135,0346,0347,
471     0173,0101,0102,0103,0104,0105,0106,0107,
472     0110,0111,0350,0351,0352,0353,0354,0355,
473     0175,0112,0113,0114,0115,0116,0117,0120,
474     0121,0122,0356,0357,0360,0361,0362,0363,
475     0134,0237,0123,0124,0125,0126,0127,0130,
476     0131,0132,0364,0365,0366,0367,0370,0371,
477     0060,0061,0062,0063,0064,0065,0066,0067,
478     0070,0071,0372,0373,0374,0375,0376,0377
479 };
480 
481   int
main(int argc,char * argv[])482 main(int argc, char *argv[])
483 {
484   FILE *fp, *fpo;
485   int c, e, p = 0, relseek = 1, negseek = 0, revert = 0;
486   int cols = 0, nonzero = 0, autoskip = 0, hextype = HEX_NORMAL;
487   int capitalize = 0, decimal_offset = 0;
488   int ebcdic = 0;
489   int octspergrp = -1;	/* number of octets grouped in output */
490   int grplen;		/* total chars per octet group */
491   long length = -1, n = 0, seekoff = 0;
492   unsigned long displayoff = 0;
493   static char l[LLEN+1];  /* static because it may be too big for stack */
494   char *pp;
495   int addrlen = 9;
496 
497 #ifdef AMIGA
498   /* This program doesn't work when started from the Workbench */
499   if (argc == 0)
500     exit(1);
501 #endif
502 
503   pname = argv[0];
504   for (pp = pname; *pp; )
505     if (*pp++ == PATH_SEP)
506       pname = pp;
507 #ifdef FILE_SEP
508   for (pp = pname; *pp; pp++)
509     if (*pp == FILE_SEP)
510       {
511 	*pp = '\0';
512 	break;
513       }
514 #endif
515 
516   while (argc >= 2)
517     {
518       pp = argv[1] + (!STRNCMP(argv[1], "--", 2) && argv[1][2]);
519 	   if (!STRNCMP(pp, "-a", 2)) autoskip = 1 - autoskip;
520       else if (!STRNCMP(pp, "-b", 2)) hextype = HEX_BITS;
521       else if (!STRNCMP(pp, "-e", 2)) hextype = HEX_LITTLEENDIAN;
522       else if (!STRNCMP(pp, "-u", 2)) hexx = hexxa + 16;
523       else if (!STRNCMP(pp, "-p", 2)) hextype = HEX_POSTSCRIPT;
524       else if (!STRNCMP(pp, "-i", 2)) hextype = HEX_CINCLUDE;
525       else if (!STRNCMP(pp, "-C", 2)) capitalize = 1;
526       else if (!STRNCMP(pp, "-d", 2)) decimal_offset = 1;
527       else if (!STRNCMP(pp, "-r", 2)) revert++;
528       else if (!STRNCMP(pp, "-E", 2)) ebcdic++;
529       else if (!STRNCMP(pp, "-v", 2))
530 	{
531 	  fprintf(stderr, "%s%s\n", version, osver);
532 	  exit(0);
533 	}
534       else if (!STRNCMP(pp, "-c", 2))
535 	{
536 	  if (pp[2] && !STRNCMP("apitalize", pp + 2, 9))
537 	    capitalize = 1;
538 	  else if (pp[2] && STRNCMP("ols", pp + 2, 3))
539 	    cols = (int)strtol(pp + 2, NULL, 0);
540 	  else
541 	    {
542 	      if (!argv[2])
543 		exit_with_usage();
544 	      cols = (int)strtol(argv[2], NULL, 0);
545 	      argv++;
546 	      argc--;
547 	    }
548 	}
549       else if (!STRNCMP(pp, "-g", 2))
550 	{
551 	  if (pp[2] && STRNCMP("roup", pp + 2, 4))
552 	    octspergrp = (int)strtol(pp + 2, NULL, 0);
553 	  else
554 	    {
555 	      if (!argv[2])
556 		exit_with_usage();
557 	      octspergrp = (int)strtol(argv[2], NULL, 0);
558 	      argv++;
559 	      argc--;
560 	    }
561 	}
562       else if (!STRNCMP(pp, "-o", 2))
563 	{
564 	  int reloffset = 0;
565 	  int negoffset = 0;
566 	  if (pp[2] && STRNCMP("ffset", pp + 2, 5))
567 	    displayoff = strtoul(pp + 2, NULL, 0);
568 	  else
569 	    {
570 	      if (!argv[2])
571 		exit_with_usage();
572 
573 	      if (argv[2][0] == '+')
574 	       reloffset++;
575 	     if (argv[2][reloffset] == '-')
576 	       negoffset++;
577 
578 	     if (negoffset)
579 	       displayoff = ULONG_MAX - strtoul(argv[2] + reloffset+negoffset, NULL, 0) + 1;
580 	     else
581 	       displayoff = strtoul(argv[2] + reloffset+negoffset, NULL, 0);
582 
583 	      argv++;
584 	      argc--;
585 	    }
586 	}
587       else if (!STRNCMP(pp, "-s", 2))
588 	{
589 	  relseek = 0;
590 	  negseek = 0;
591 	  if (pp[2] && STRNCMP("kip", pp+2, 3) && STRNCMP("eek", pp+2, 3))
592 	    {
593 #ifdef TRY_SEEK
594 	      if (pp[2] == '+')
595 		relseek++;
596 	      if (pp[2+relseek] == '-')
597 		negseek++;
598 #endif
599 	      seekoff = strtol(pp + 2+relseek+negseek, (char **)NULL, 0);
600 	    }
601 	  else
602 	    {
603 	      if (!argv[2])
604 		exit_with_usage();
605 #ifdef TRY_SEEK
606 	      if (argv[2][0] == '+')
607 		relseek++;
608 	      if (argv[2][relseek] == '-')
609 		negseek++;
610 #endif
611 	      seekoff = strtol(argv[2] + relseek+negseek, (char **)NULL, 0);
612 	      argv++;
613 	      argc--;
614 	    }
615 	}
616       else if (!STRNCMP(pp, "-l", 2))
617 	{
618 	  if (pp[2] && STRNCMP("en", pp + 2, 2))
619 	    length = strtol(pp + 2, (char **)NULL, 0);
620 	  else
621 	    {
622 	      if (!argv[2])
623 		exit_with_usage();
624 	      length = strtol(argv[2], (char **)NULL, 0);
625 	      argv++;
626 	      argc--;
627 	    }
628 	}
629       else if (!strcmp(pp, "--"))	/* end of options */
630 	{
631 	  argv++;
632 	  argc--;
633 	  break;
634 	}
635       else if (pp[0] == '-' && pp[1])	/* unknown option */
636 	exit_with_usage();
637       else
638 	break;				/* not an option */
639 
640       argv++;				/* advance to next argument */
641       argc--;
642     }
643 
644   if (!cols)
645     switch (hextype)
646       {
647       case HEX_POSTSCRIPT:	cols = 30; break;
648       case HEX_CINCLUDE:	cols = 12; break;
649       case HEX_BITS:		cols = 6; break;
650       case HEX_NORMAL:
651       case HEX_LITTLEENDIAN:
652       default:			cols = 16; break;
653       }
654 
655   if (octspergrp < 0)
656     switch (hextype)
657       {
658       case HEX_BITS:		octspergrp = 1; break;
659       case HEX_NORMAL:		octspergrp = 2; break;
660       case HEX_LITTLEENDIAN:	octspergrp = 4; break;
661       case HEX_POSTSCRIPT:
662       case HEX_CINCLUDE:
663       default:			octspergrp = 0; break;
664       }
665 
666   if (cols < 1 || ((hextype == HEX_NORMAL || hextype == HEX_BITS || hextype == HEX_LITTLEENDIAN)
667 							    && (cols > COLS)))
668     {
669       fprintf(stderr, "%s: invalid number of columns (max. %d).\n", pname, COLS);
670       exit(1);
671     }
672 
673   if (octspergrp < 1 || octspergrp > cols)
674     octspergrp = cols;
675   else if (hextype == HEX_LITTLEENDIAN && (octspergrp & (octspergrp-1)))
676     error_exit(1, "number of octets per group must be a power of 2 with -e.");
677 
678   if (argc > 3)
679     exit_with_usage();
680 
681   if (argc == 1 || (argv[1][0] == '-' && !argv[1][1]))
682     BIN_ASSIGN(fp = stdin, !revert);
683   else
684     {
685       if ((fp = fopen(argv[1], BIN_READ(!revert))) == NULL)
686 	{
687 	  fprintf(stderr,"%s: ", pname);
688 	  perror(argv[1]);
689 	  return 2;
690 	}
691     }
692 
693   if (argc < 3 || (argv[2][0] == '-' && !argv[2][1]))
694     BIN_ASSIGN(fpo = stdout, revert);
695   else
696     {
697       int fd;
698       int mode = revert ? O_WRONLY : (O_TRUNC|O_WRONLY);
699 
700       if (((fd = OPEN(argv[2], mode | BIN_CREAT(revert), 0666)) < 0) ||
701 	  (fpo = fdopen(fd, BIN_WRITE(revert))) == NULL)
702 	{
703 	  fprintf(stderr, "%s: ", pname);
704 	  perror(argv[2]);
705 	  return 3;
706 	}
707       rewind(fpo);
708     }
709 
710   if (revert)
711     {
712       if (hextype && (hextype != HEX_POSTSCRIPT))
713 	error_exit(-1, "Sorry, cannot revert this type of hexdump");
714       return huntype(fp, fpo, cols, hextype,
715 		negseek ? -seekoff : seekoff);
716     }
717 
718   if (seekoff || negseek || !relseek)
719     {
720 #ifdef TRY_SEEK
721       if (relseek)
722 	e = fseek(fp, negseek ? -seekoff : seekoff, SEEK_CUR);
723       else
724 	e = fseek(fp, negseek ? -seekoff : seekoff,
725 						negseek ? SEEK_END : SEEK_SET);
726       if (e < 0 && negseek)
727 	error_exit(4, "Sorry, cannot seek.");
728       if (e >= 0)
729 	seekoff = ftell(fp);
730       else
731 #endif
732 	{
733 	  long s = seekoff;
734 
735 	  while (s--)
736 	    if (getc_or_die(fp) == EOF)
737 	    {
738 	      error_exit(4, "Sorry, cannot seek.");
739 	    }
740 	}
741     }
742 
743   if (hextype == HEX_CINCLUDE)
744     {
745       if (fp != stdin)
746 	{
747 	  FPRINTF_OR_DIE((fpo, "unsigned char %s", isdigit((int)argv[1][0]) ? "__" : ""));
748 	  for (e = 0; (c = argv[1][e]) != 0; e++)
749 	    putc_or_die(isalnum(c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo);
750 	  fputs_or_die("[] = {\n", fpo);
751 	}
752 
753       p = 0;
754       c = 0;
755       while ((length < 0 || p < length) && (c = getc_or_die(fp)) != EOF)
756 	{
757 	  FPRINTF_OR_DIE((fpo, (hexx == hexxa) ? "%s0x%02x" : "%s0X%02X",
758 		(p % cols) ? ", " : (!p ? "  " : ",\n  "), c));
759 	  p++;
760 	}
761 
762       if (p)
763 	fputs_or_die("\n", fpo);
764 
765       if (fp != stdin)
766 	{
767 	  fputs_or_die("};\n", fpo);
768 	  FPRINTF_OR_DIE((fpo, "unsigned int %s", isdigit((int)argv[1][0]) ? "__" : ""));
769 	  for (e = 0; (c = argv[1][e]) != 0; e++)
770 	    putc_or_die(isalnum(c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo);
771 	  FPRINTF_OR_DIE((fpo, "_%s = %d;\n", capitalize ? "LEN" : "len", p));
772 	}
773 
774       fclose_or_die(fp, fpo);
775       return 0;
776     }
777 
778   if (hextype == HEX_POSTSCRIPT)
779     {
780       p = cols;
781       while ((length < 0 || n < length) && (e = getc_or_die(fp)) != EOF)
782 	{
783 	  putc_or_die(hexx[(e >> 4) & 0xf], fpo);
784 	  putc_or_die(hexx[e & 0xf], fpo);
785 	  n++;
786 	  if (!--p)
787 	    {
788 	      putc_or_die('\n', fpo);
789 	      p = cols;
790 	    }
791 	}
792       if (p < cols)
793 	putc_or_die('\n', fpo);
794       fclose_or_die(fp, fpo);
795       return 0;
796     }
797 
798   /* hextype: HEX_NORMAL or HEX_BITS or HEX_LITTLEENDIAN */
799 
800   if (hextype != HEX_BITS)
801     grplen = octspergrp + octspergrp + 1;	/* chars per octet group */
802   else	/* hextype == HEX_BITS */
803     grplen = 8 * octspergrp + 1;
804 
805   while ((length < 0 || n < length) && (e = getc_or_die(fp)) != EOF)
806     {
807       int x;
808 
809       if (p == 0)
810 	{
811 	  addrlen = sprintf(l, decimal_offset ? "%08ld:" : "%08lx:",
812 				  ((unsigned long)(n + seekoff + displayoff)));
813 	  for (c = addrlen; c < LLEN; l[c++] = ' ');
814 	}
815       x = hextype == HEX_LITTLEENDIAN ? p ^ (octspergrp-1) : p;
816       c = addrlen + 1 + (grplen * x) / octspergrp;
817       if (hextype == HEX_NORMAL || hextype == HEX_LITTLEENDIAN)
818 	{
819 	  l[c]   = hexx[(e >> 4) & 0xf];
820 	  l[++c] = hexx[e & 0xf];
821 	}
822       else /* hextype == HEX_BITS */
823 	{
824 	  int i;
825 	  for (i = 7; i >= 0; i--)
826 	    l[c++] = (e & (1 << i)) ? '1' : '0';
827 	}
828       if (e)
829 	nonzero++;
830       if (ebcdic)
831 	e = (e < 64) ? '.' : etoa64[e-64];
832       /* When changing this update definition of LLEN above. */
833       c = addrlen + 3 + (grplen * cols - 1)/octspergrp + p;
834       l[c++] =
835 #ifdef __MVS__
836 	  (e >= 64)
837 #else
838 	  (e > 31 && e < 127)
839 #endif
840 	  ? e : '.';
841       n++;
842       if (++p == cols)
843 	{
844 	  l[c] = '\n';
845 	  l[++c] = '\0';
846 	  xxdline(fpo, l, autoskip ? nonzero : 1);
847 	  nonzero = 0;
848 	  p = 0;
849 	}
850     }
851   if (p)
852     {
853       l[c] = '\n';
854       l[++c] = '\0';
855       xxdline(fpo, l, 1);
856     }
857   else if (autoskip)
858     xxdline(fpo, l, -1);	/* last chance to flush out suppressed lines */
859 
860   fclose_or_die(fp, fpo);
861   return 0;
862 }
863 
864 /* vi:set ts=8 sw=4 sts=2 cino+={2 cino+=n-2 : */
865