xref: /vim-8.2.3635/src/xxd/xxd.c (revision 786e05be)
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  *	    ([email protected])
44  * 22.05.97 added -g (group octets) option ([email protected]).
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 ([email protected])
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 <[email protected]> (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 /*
256  * If "c" is a hex digit, return the value.
257  * Otherwise return -1.
258  */
259   static int
parse_hex_digit(int c)260 parse_hex_digit(int c)
261 {
262   return (c >= '0' && c <= '9') ? c - '0'
263 	: (c >= 'a' && c <= 'f') ? c - 'a' + 10
264 	: (c >= 'A' && c <= 'F') ? c - 'A' + 10
265 	: -1;
266 }
267 
268 /*
269  * Ignore text on "fpi" until end-of-line or end-of-file.
270  * Return the '\n' or EOF character.
271  * When an error is encountered exit with an error message.
272  */
273   static int
skip_to_eol(FILE * fpi,int c)274 skip_to_eol(FILE *fpi, int c)
275 {
276   while (c != '\n' && c != EOF)
277     c = getc(fpi);
278   if (c == EOF && ferror(fpi))
279     perror_exit(2);
280   return c;
281 }
282 
283 /*
284  * Max. cols binary characters are decoded from the input stream per line.
285  * Two adjacent garbage characters after evaluated data delimit valid data.
286  * Everything up to the next newline is discarded.
287  *
288  * The name is historic and came from 'undo type opt h'.
289  */
290   static int
huntype(FILE * fpi,FILE * fpo,int cols,int hextype,long base_off)291 huntype(
292   FILE *fpi,
293   FILE *fpo,
294   int cols,
295   int hextype,
296   long base_off)
297 {
298   int c, ign_garb = 1, n1 = -1, n2 = 0, n3, p = cols;
299   long have_off = 0, want_off = 0;
300 
301   rewind(fpi);
302 
303   while ((c = getc(fpi)) != EOF)
304     {
305       if (c == '\r')	/* Doze style input file? */
306 	continue;
307 
308       /* Allow multiple spaces.  This doesn't work when there is normal text
309        * after the hex codes in the last line that looks like hex, thus only
310        * use it for PostScript format. */
311       if (hextype == HEX_POSTSCRIPT && (c == ' ' || c == '\n' || c == '\t'))
312 	continue;
313 
314       n3 = n2;
315       n2 = n1;
316 
317       n1 = parse_hex_digit(c);
318       if (n1 == -1 && ign_garb)
319 	continue;
320 
321       ign_garb = 0;
322 
323       if (!hextype && (p >= cols))
324 	{
325 	  if (n1 < 0)
326 	    {
327 	      p = 0;
328 	      continue;
329 	    }
330 	  want_off = (want_off << 4) | n1;
331 	  continue;
332 	}
333 
334       if (base_off + want_off != have_off)
335 	{
336 	  if (fflush(fpo) != 0)
337 	    perror_exit(3);
338 #ifdef TRY_SEEK
339 	  if (fseek(fpo, base_off + want_off - have_off, SEEK_CUR) >= 0)
340 	    have_off = base_off + want_off;
341 #endif
342 	  if (base_off + want_off < have_off)
343 	    error_exit(5, "sorry, cannot seek backwards.");
344 	  for (; have_off < base_off + want_off; have_off++)
345 	    if (putc(0, fpo) == EOF)
346 	      perror_exit(3);
347 	}
348 
349       if (n2 >= 0 && n1 >= 0)
350 	{
351 	  if (putc((n2 << 4) | n1, fpo) == EOF)
352 	    perror_exit(3);
353 	  have_off++;
354 	  want_off++;
355 	  n1 = -1;
356 	  if (!hextype && (++p >= cols))
357 	    /* skip the rest of the line as garbage */
358 	    c = skip_to_eol(fpi, c);
359 	}
360       else if (n1 < 0 && n2 < 0 && n3 < 0)
361         /* already stumbled into garbage, skip line, wait and see */
362 	c = skip_to_eol(fpi, c);
363 
364       if (c == '\n')
365 	{
366 	  if (!hextype)
367 	    want_off = 0;
368 	  p = cols;
369 	  ign_garb = 1;
370 	}
371     }
372   if (fflush(fpo) != 0)
373     perror_exit(3);
374 #ifdef TRY_SEEK
375   fseek(fpo, 0L, SEEK_END);
376 #endif
377   if (fclose(fpo) != 0)
378     perror_exit(3);
379   if (fclose(fpi) != 0)
380     perror_exit(2);
381   return 0;
382 }
383 
384 /*
385  * Print line l. If nz is false, xxdline regards the line a line of
386  * zeroes. If there are three or more consecutive lines of zeroes,
387  * they are replaced by a single '*' character.
388  *
389  * If the output ends with more than two lines of zeroes, you
390  * should call xxdline again with l being the last line and nz
391  * negative. This ensures that the last line is shown even when
392  * it is all zeroes.
393  *
394  * If nz is always positive, lines are never suppressed.
395  */
396   static void
xxdline(FILE * fp,char * l,int nz)397 xxdline(FILE *fp, char *l, int nz)
398 {
399   static char z[LLEN+1];
400   static int zero_seen = 0;
401 
402   if (!nz && zero_seen == 1)
403     strcpy(z, l);
404 
405   if (nz || !zero_seen++)
406     {
407       if (nz)
408 	{
409 	  if (nz < 0)
410 	    zero_seen--;
411 	  if (zero_seen == 2)
412 	    if (fputs(z, fp) == EOF)
413 	      perror_exit(3);
414 	  if (zero_seen > 2)
415 	    if (fputs("*\n", fp) == EOF)
416 	      perror_exit(3);
417 	}
418       if (nz >= 0 || zero_seen > 0)
419 	if (fputs(l, fp) == EOF)
420 	  perror_exit(3);
421       if (nz)
422 	zero_seen = 0;
423     }
424 }
425 
426 /* This is an EBCDIC to ASCII conversion table */
427 /* from a proposed BTL standard April 16, 1979 */
428 static unsigned char etoa64[] =
429 {
430     0040,0240,0241,0242,0243,0244,0245,0246,
431     0247,0250,0325,0056,0074,0050,0053,0174,
432     0046,0251,0252,0253,0254,0255,0256,0257,
433     0260,0261,0041,0044,0052,0051,0073,0176,
434     0055,0057,0262,0263,0264,0265,0266,0267,
435     0270,0271,0313,0054,0045,0137,0076,0077,
436     0272,0273,0274,0275,0276,0277,0300,0301,
437     0302,0140,0072,0043,0100,0047,0075,0042,
438     0303,0141,0142,0143,0144,0145,0146,0147,
439     0150,0151,0304,0305,0306,0307,0310,0311,
440     0312,0152,0153,0154,0155,0156,0157,0160,
441     0161,0162,0136,0314,0315,0316,0317,0320,
442     0321,0345,0163,0164,0165,0166,0167,0170,
443     0171,0172,0322,0323,0324,0133,0326,0327,
444     0330,0331,0332,0333,0334,0335,0336,0337,
445     0340,0341,0342,0343,0344,0135,0346,0347,
446     0173,0101,0102,0103,0104,0105,0106,0107,
447     0110,0111,0350,0351,0352,0353,0354,0355,
448     0175,0112,0113,0114,0115,0116,0117,0120,
449     0121,0122,0356,0357,0360,0361,0362,0363,
450     0134,0237,0123,0124,0125,0126,0127,0130,
451     0131,0132,0364,0365,0366,0367,0370,0371,
452     0060,0061,0062,0063,0064,0065,0066,0067,
453     0070,0071,0372,0373,0374,0375,0376,0377
454 };
455 
456   int
main(int argc,char * argv[])457 main(int argc, char *argv[])
458 {
459   FILE *fp, *fpo;
460   int c, e, p = 0, relseek = 1, negseek = 0, revert = 0;
461   int cols = 0, nonzero = 0, autoskip = 0, hextype = HEX_NORMAL;
462   int capitalize = 0, decimal_offset = 0;
463   int ebcdic = 0;
464   int octspergrp = -1;	/* number of octets grouped in output */
465   int grplen;		/* total chars per octet group */
466   long length = -1, n = 0, seekoff = 0;
467   unsigned long displayoff = 0;
468   static char l[LLEN+1];  /* static because it may be too big for stack */
469   char *pp;
470   int addrlen = 9;
471 
472 #ifdef AMIGA
473   /* This program doesn't work when started from the Workbench */
474   if (argc == 0)
475     exit(1);
476 #endif
477 
478   pname = argv[0];
479   for (pp = pname; *pp; )
480     if (*pp++ == PATH_SEP)
481       pname = pp;
482 #ifdef FILE_SEP
483   for (pp = pname; *pp; pp++)
484     if (*pp == FILE_SEP)
485       {
486 	*pp = '\0';
487 	break;
488       }
489 #endif
490 
491   while (argc >= 2)
492     {
493       pp = argv[1] + (!STRNCMP(argv[1], "--", 2) && argv[1][2]);
494 	   if (!STRNCMP(pp, "-a", 2)) autoskip = 1 - autoskip;
495       else if (!STRNCMP(pp, "-b", 2)) hextype = HEX_BITS;
496       else if (!STRNCMP(pp, "-e", 2)) hextype = HEX_LITTLEENDIAN;
497       else if (!STRNCMP(pp, "-u", 2)) hexx = hexxa + 16;
498       else if (!STRNCMP(pp, "-p", 2)) hextype = HEX_POSTSCRIPT;
499       else if (!STRNCMP(pp, "-i", 2)) hextype = HEX_CINCLUDE;
500       else if (!STRNCMP(pp, "-C", 2)) capitalize = 1;
501       else if (!STRNCMP(pp, "-d", 2)) decimal_offset = 1;
502       else if (!STRNCMP(pp, "-r", 2)) revert++;
503       else if (!STRNCMP(pp, "-E", 2)) ebcdic++;
504       else if (!STRNCMP(pp, "-v", 2))
505 	{
506 	  fprintf(stderr, "%s%s\n", version, osver);
507 	  exit(0);
508 	}
509       else if (!STRNCMP(pp, "-c", 2))
510 	{
511 	  if (pp[2] && !STRNCMP("apitalize", pp + 2, 9))
512 	    capitalize = 1;
513 	  else if (pp[2] && STRNCMP("ols", pp + 2, 3))
514 	    cols = (int)strtol(pp + 2, NULL, 0);
515 	  else
516 	    {
517 	      if (!argv[2])
518 		exit_with_usage();
519 	      cols = (int)strtol(argv[2], NULL, 0);
520 	      argv++;
521 	      argc--;
522 	    }
523 	}
524       else if (!STRNCMP(pp, "-g", 2))
525 	{
526 	  if (pp[2] && STRNCMP("roup", pp + 2, 4))
527 	    octspergrp = (int)strtol(pp + 2, NULL, 0);
528 	  else
529 	    {
530 	      if (!argv[2])
531 		exit_with_usage();
532 	      octspergrp = (int)strtol(argv[2], NULL, 0);
533 	      argv++;
534 	      argc--;
535 	    }
536 	}
537       else if (!STRNCMP(pp, "-o", 2))
538 	{
539 	  int reloffset = 0;
540 	  int negoffset = 0;
541 	  if (pp[2] && STRNCMP("ffset", pp + 2, 5))
542 	    displayoff = strtoul(pp + 2, NULL, 0);
543 	  else
544 	    {
545 	      if (!argv[2])
546 		exit_with_usage();
547 
548 	      if (argv[2][0] == '+')
549 	       reloffset++;
550 	     if (argv[2][reloffset] == '-')
551 	       negoffset++;
552 
553 	     if (negoffset)
554 	       displayoff = ULONG_MAX - strtoul(argv[2] + reloffset+negoffset, NULL, 0) + 1;
555 	     else
556 	       displayoff = strtoul(argv[2] + reloffset+negoffset, NULL, 0);
557 
558 	      argv++;
559 	      argc--;
560 	    }
561 	}
562       else if (!STRNCMP(pp, "-s", 2))
563 	{
564 	  relseek = 0;
565 	  negseek = 0;
566 	  if (pp[2] && STRNCMP("kip", pp+2, 3) && STRNCMP("eek", pp+2, 3))
567 	    {
568 #ifdef TRY_SEEK
569 	      if (pp[2] == '+')
570 		relseek++;
571 	      if (pp[2+relseek] == '-')
572 		negseek++;
573 #endif
574 	      seekoff = strtol(pp + 2+relseek+negseek, (char **)NULL, 0);
575 	    }
576 	  else
577 	    {
578 	      if (!argv[2])
579 		exit_with_usage();
580 #ifdef TRY_SEEK
581 	      if (argv[2][0] == '+')
582 		relseek++;
583 	      if (argv[2][relseek] == '-')
584 		negseek++;
585 #endif
586 	      seekoff = strtol(argv[2] + relseek+negseek, (char **)NULL, 0);
587 	      argv++;
588 	      argc--;
589 	    }
590 	}
591       else if (!STRNCMP(pp, "-l", 2))
592 	{
593 	  if (pp[2] && STRNCMP("en", pp + 2, 2))
594 	    length = strtol(pp + 2, (char **)NULL, 0);
595 	  else
596 	    {
597 	      if (!argv[2])
598 		exit_with_usage();
599 	      length = strtol(argv[2], (char **)NULL, 0);
600 	      argv++;
601 	      argc--;
602 	    }
603 	}
604       else if (!strcmp(pp, "--"))	/* end of options */
605 	{
606 	  argv++;
607 	  argc--;
608 	  break;
609 	}
610       else if (pp[0] == '-' && pp[1])	/* unknown option */
611 	exit_with_usage();
612       else
613 	break;				/* not an option */
614 
615       argv++;				/* advance to next argument */
616       argc--;
617     }
618 
619   if (!cols)
620     switch (hextype)
621       {
622       case HEX_POSTSCRIPT:	cols = 30; break;
623       case HEX_CINCLUDE:	cols = 12; break;
624       case HEX_BITS:		cols = 6; break;
625       case HEX_NORMAL:
626       case HEX_LITTLEENDIAN:
627       default:			cols = 16; break;
628       }
629 
630   if (octspergrp < 0)
631     switch (hextype)
632       {
633       case HEX_BITS:		octspergrp = 1; break;
634       case HEX_NORMAL:		octspergrp = 2; break;
635       case HEX_LITTLEENDIAN:	octspergrp = 4; break;
636       case HEX_POSTSCRIPT:
637       case HEX_CINCLUDE:
638       default:			octspergrp = 0; break;
639       }
640 
641   if (cols < 1 || ((hextype == HEX_NORMAL || hextype == HEX_BITS || hextype == HEX_LITTLEENDIAN)
642 							    && (cols > COLS)))
643     {
644       fprintf(stderr, "%s: invalid number of columns (max. %d).\n", pname, COLS);
645       exit(1);
646     }
647 
648   if (octspergrp < 1 || octspergrp > cols)
649     octspergrp = cols;
650   else if (hextype == HEX_LITTLEENDIAN && (octspergrp & (octspergrp-1)))
651     error_exit(1, "number of octets per group must be a power of 2 with -e.");
652 
653   if (argc > 3)
654     exit_with_usage();
655 
656   if (argc == 1 || (argv[1][0] == '-' && !argv[1][1]))
657     BIN_ASSIGN(fp = stdin, !revert);
658   else
659     {
660       if ((fp = fopen(argv[1], BIN_READ(!revert))) == NULL)
661 	{
662 	  fprintf(stderr,"%s: ", pname);
663 	  perror(argv[1]);
664 	  return 2;
665 	}
666     }
667 
668   if (argc < 3 || (argv[2][0] == '-' && !argv[2][1]))
669     BIN_ASSIGN(fpo = stdout, revert);
670   else
671     {
672       int fd;
673       int mode = revert ? O_WRONLY : (O_TRUNC|O_WRONLY);
674 
675       if (((fd = OPEN(argv[2], mode | BIN_CREAT(revert), 0666)) < 0) ||
676 	  (fpo = fdopen(fd, BIN_WRITE(revert))) == NULL)
677 	{
678 	  fprintf(stderr, "%s: ", pname);
679 	  perror(argv[2]);
680 	  return 3;
681 	}
682       rewind(fpo);
683     }
684 
685   if (revert)
686     {
687       if (hextype && (hextype != HEX_POSTSCRIPT))
688 	error_exit(-1, "sorry, cannot revert this type of hexdump");
689       return huntype(fp, fpo, cols, hextype,
690 		negseek ? -seekoff : seekoff);
691     }
692 
693   if (seekoff || negseek || !relseek)
694     {
695 #ifdef TRY_SEEK
696       if (relseek)
697 	e = fseek(fp, negseek ? -seekoff : seekoff, SEEK_CUR);
698       else
699 	e = fseek(fp, negseek ? -seekoff : seekoff,
700 						negseek ? SEEK_END : SEEK_SET);
701       if (e < 0 && negseek)
702 	error_exit(4, "sorry cannot seek.");
703       if (e >= 0)
704 	seekoff = ftell(fp);
705       else
706 #endif
707 	{
708 	  long s = seekoff;
709 
710 	  while (s--)
711 	    if (getc(fp) == EOF)
712 	    {
713 	      if (ferror(fp))
714 		{
715 		  perror_exit(2);
716 		}
717 	      else
718 		{
719 		  error_exit(4, "sorry cannot seek.");
720 		}
721 	    }
722 	}
723     }
724 
725   if (hextype == HEX_CINCLUDE)
726     {
727       if (fp != stdin)
728 	{
729 	  if (fprintf(fpo, "unsigned char %s", isdigit((int)argv[1][0]) ? "__" : "") < 0)
730 	    perror_exit(3);
731 	  for (e = 0; (c = argv[1][e]) != 0; e++)
732           if (putc(isalnum(c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo) == EOF)
733 	      perror_exit(3);
734 	  if (fputs("[] = {\n", fpo) == EOF)
735 	    perror_exit(3);
736 	}
737 
738       p = 0;
739       c = 0;
740       while ((length < 0 || p < length) && (c = getc(fp)) != EOF)
741 	{
742 	  if (fprintf(fpo, (hexx == hexxa) ? "%s0x%02x" : "%s0X%02X",
743 		(p % cols) ? ", " : &",\n  "[2*!p],  c) < 0)
744 	    perror_exit(3);
745 	  p++;
746 	}
747       if (c == EOF && ferror(fp))
748 	perror_exit(2);
749 
750       if (p && fputs("\n", fpo) == EOF)
751 	perror_exit(3);
752       if (fputs(&"};\n"[3 * (fp == stdin)], fpo) == EOF)
753 	perror_exit(3);
754 
755       if (fp != stdin)
756 	{
757 	  if (fprintf(fpo, "unsigned int %s", isdigit((int)argv[1][0]) ? "__" : "") < 0)
758 	    perror_exit(3);
759 	  for (e = 0; (c = argv[1][e]) != 0; e++)
760         if (putc(isalnum(c) ? CONDITIONAL_CAPITALIZE(c) : '_', fpo) == EOF)
761 	      perror_exit(3);
762 	  if (fprintf(fpo, "_%s = %d;\n", capitalize ? "LEN" : "len", p) < 0)
763 	    perror_exit(3);
764 	}
765 
766       if (fclose(fp))
767 	perror_exit(2);
768       if (fclose(fpo))
769 	perror_exit(3);
770       return 0;
771     }
772 
773   if (hextype == HEX_POSTSCRIPT)
774     {
775       p = cols;
776       e = 0;
777       while ((length < 0 || n < length) && (e = getc(fp)) != EOF)
778 	{
779 	  if (putc(hexx[(e >> 4) & 0xf], fpo) == EOF
780 		  || putc(hexx[e & 0xf], fpo) == EOF)
781 	    perror_exit(3);
782 	  n++;
783 	  if (!--p)
784 	    {
785 	      if (putc('\n', fpo) == EOF)
786 		perror_exit(3);
787 	      p = cols;
788 	    }
789 	}
790       if (e == EOF && ferror(fp))
791 	perror_exit(2);
792       if (p < cols)
793 	if (putc('\n', fpo) == EOF)
794 	  perror_exit(3);
795       if (fclose(fp))
796 	perror_exit(2);
797       if (fclose(fpo))
798 	perror_exit(3);
799       return 0;
800     }
801 
802   /* hextype: HEX_NORMAL or HEX_BITS or HEX_LITTLEENDIAN */
803 
804   if (hextype != HEX_BITS)
805     grplen = octspergrp + octspergrp + 1;	/* chars per octet group */
806   else	/* hextype == HEX_BITS */
807     grplen = 8 * octspergrp + 1;
808 
809   e = 0;
810   while ((length < 0 || n < length) && (e = getc(fp)) != EOF)
811     {
812       if (p == 0)
813 	{
814 	  if (decimal_offset)
815 		addrlen = sprintf(l, "%08ld:",
816 				  ((unsigned long)(n + seekoff + displayoff)));
817 	  else
818 		addrlen = sprintf(l, "%08lx:",
819 				  ((unsigned long)(n + seekoff + displayoff)));
820 	  for (c = addrlen; c < LLEN; l[c++] = ' ');
821 	}
822       if (hextype == HEX_NORMAL)
823 	{
824 	  l[c = (addrlen + 1 + (grplen * p) / octspergrp)] = hexx[(e >> 4) & 0xf];
825 	  l[++c]				  = hexx[ e       & 0xf];
826 	}
827       else if (hextype == HEX_LITTLEENDIAN)
828 	{
829 	  int x = p ^ (octspergrp-1);
830 	  l[c = (addrlen + 1 + (grplen * x) / octspergrp)] = hexx[(e >> 4) & 0xf];
831 	  l[++c]				  = hexx[ e       & 0xf];
832 	}
833       else /* hextype == HEX_BITS */
834 	{
835 	  int i;
836 
837 	  c = (addrlen + 1 + (grplen * p) / octspergrp) - 1;
838 	  for (i = 7; i >= 0; i--)
839 	    l[++c] = (e & (1 << i)) ? '1' : '0';
840 	}
841       if (e)
842 	nonzero++;
843       if (ebcdic)
844 	e = (e < 64) ? '.' : etoa64[e-64];
845       /* When changing this update definition of LLEN above. */
846       l[addrlen + 3 + (grplen * cols - 1)/octspergrp + p] =
847 #ifdef __MVS__
848 	  (e >= 64)
849 #else
850 	  (e > 31 && e < 127)
851 #endif
852 	  ? e : '.';
853       n++;
854       if (++p == cols)
855 	{
856 	  l[c = (addrlen + 3 + (grplen * cols - 1)/octspergrp + p)] = '\n'; l[++c] = '\0';
857 	  xxdline(fpo, l, autoskip ? nonzero : 1);
858 	  nonzero = 0;
859 	  p = 0;
860 	}
861     }
862   if (e == EOF && ferror(fp))
863     perror_exit(2);
864   if (p)
865     {
866       l[c = (addrlen + 3 + (grplen * cols - 1)/octspergrp + p)] = '\n'; l[++c] = '\0';
867       xxdline(fpo, l, 1);
868     }
869   else if (autoskip)
870     xxdline(fpo, l, -1);	/* last chance to flush out suppressed lines */
871 
872   if (fclose(fp))
873     perror_exit(2);
874   if (fclose(fpo))
875     perror_exit(3);
876   return 0;
877 }
878 
879 /* vi:set ts=8 sw=4 sts=2 cino+={2 cino+=n-2 : */
880