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