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