#include #include #include #include #include #include #include #include "atables.h" /* printf() formats for lower/uppercase hex. */ #define LC_WORD_FMT "%04x" #define LC_BYTE_FMT "%02x" #define UC_WORD_FMT "%04X" #define UC_BYTE_FMT "%02X" /* translation table (see atables.c), choices are ata2utf, ics2utf, or magazine. */ const char **table = ata2utf; /* hardcoded ANSI-style escape sequences, work fine on all modern terminal emulators. */ const char *inverse_on = "\x1b[7m"; const char *inverse_off = "\x1b[0m"; const char *color_off = "\x1b[0m"; /* -u option changes these. */ const char *word_format = LC_WORD_FMT; const char *byte_format = LC_BYTE_FMT; /* command line options change these. */ int verbose = 0, color = 1, disp_offset = 0, maxlen = 0; int seek_whence = 0, seekpos = 0, filepos = 0, limit = 0; int graphics = 0, screencodes = 0; const char *self; void set_self(const char *argv0) { char *p; self = argv0; p = strrchr(self, '/'); if(p) self = p + 1; } void die(const char *msg) { fprintf(stderr, "%s: %s\n", self, msg); exit(1); } void print_help(void) { printf("Usage:\n %s [-a] [-g] [-i] [-l limit] [-m] [-o offset] [-s [-]seek] [-u] [-v] [file]\n", self); printf(" -a: Input is ANTIC screencodes (default is ATASCII).\n"); printf(" -g: GR.1/2 style colorization.\n"); printf(" -i: Input is XL intl charset (default is ATASCII).\n"); printf(" -l: Stop after bytes.\n"); printf(" -m: Monochrome (color off).\n"); printf(" -o: Add to displayed byte positions.\n"); printf(" -s: Seek bytes into input (with -, seek back from EOF).\n"); printf(" -u: Uppercase hex digits (default is lowercase).\n"); printf(" -v: Verbose debugging info.\n"); printf("With no [file], or -, reads from stdin.\n"); } int parse_num_arg(const char *arg) { int got; if(sscanf(arg, "0x%x", &got) != 1) if(sscanf(arg, "$%x", &got) != 1) if(sscanf(arg, "%d", &got) != 1) { fprintf(stderr, "%s: invalid numeric argument '%s'.", self, arg); exit(1); } return got; } void parse_seek_arg(const char *arg) { seek_whence = SEEK_SET; if(*arg == '-') { seek_whence = SEEK_END; } seekpos = parse_num_arg(arg); } int parse_offset_arg(const char *arg) { int got; got = parse_num_arg(arg); return got; } int parse_limit_arg(const char *arg) { int got; got = parse_num_arg(arg); if(got < 0) die("negative arguments to -l (limit) are not supported."); return got; } FILE *open_input(const char *file) { FILE *input; if(file[0] == '-' && file[1] == 0) { if(freopen(NULL, "rb", stdin)) { input = stdin; } else { fprintf(stderr, "%s: ", self); perror("(standard input)"); return NULL; } if(verbose) fprintf(stderr, "%s: reading from standard input.\n", self); } else { if(!(input = fopen(file, "rb"))) { perror(file); return NULL; } if(verbose) fprintf(stderr, "%s: reading from file '%s'.\n", self, file); } return input; } char *get_color(unsigned char c) { int color; unsigned char c7 = c & 0x7f; static char outbuf[32]; if(graphics) { if(c < 0x20) color = 2; else if(c >= 0x20 && c < 0x60) color = 3; else if(c >= 0x60 && c < 0x80) color = 2; else if(c >= 0x80 && c < 0xa0) color = 1; else if(c >= 0xa0 && c < 0xe0) color = 6; /* cyan (blue is too dark on most terminals) */ else /* c >= 0xe0 */ color = 1; } else { if(c == 0 || c == 0x9b) { color = 1; /* red */ } else if(c7 < 32 || c7 == 0x60 || c7 == 0x7b || c7 == 0x7d || c7 == 0x7e || c7 == 0x7f) { color = 3; /* yellow/orange */ } else { color = 2; /* green */ } } if(color) { sprintf(outbuf, "\x1b[0;3%dm", color); } else { outbuf[0] = '\0'; } return outbuf; } /* displaying ATASCII (not screencodes) in GR.1 or GR.2: $00-$1F get displayed in green, as $20-$2F. $20-$5F get displayed in orange, as themselves. $60-$7F get displayed in green, as $40-$5F. for $80-$FF, same thing, but green is red, and orange is blue. */ char *get_graphics(unsigned char c) { static char result[2]; c &= 0x7f; if(c < 0x20) c += 0x20; else if(c >= 0x60) c -= 0x20; result[0] = c; result[1] = 0; return result; } unsigned char screen2ata(unsigned char c) { unsigned char c7 = c & 0x7f; if(c7 < 0x40) return c + 0x20; if(c7 >= 0x40 && c7 < 0x60) return c - 0x40; return c; } void fake_seek(FILE *input) { char junkbuf[1024]; int pos = 0, chunksize; if(verbose) fprintf(stderr, "%s: faking fseek() for stdin, skipping %d bytes.\n", self, seekpos); while(pos < seekpos) { chunksize = seekpos - pos; if(chunksize > 1024) chunksize = 1024; if(fread(junkbuf, 1, chunksize, input) != chunksize) die("can't seek past end of input"); pos += chunksize; } } void seek_input(FILE *input) { if(verbose) fprintf(stderr, "%s: seeking %d bytes (whence = %d) in input.\n", self, seekpos, seek_whence); if(input == stdin) { /* can't fseek() a stream, fake it */ if(seek_whence != SEEK_SET) die("can't seek from the end of standard input."); fake_seek(input); } else { if(fseek(input, seekpos, seek_whence) < 0) { fprintf(stderr, "%s: ", self); perror("fseek() failed"); exit(1); } if((filepos = ftell(input)) < 0) { fprintf(stderr, "%s: ", self); perror("ftell() failed"); exit(1); } } } void dump_line(const unsigned char *buf, int len) { char hex[1024], asc[1024]; int hpos = 0, apos = 0, count = len; unsigned char c, inv; memset(hex, 0, sizeof(hex)); memset(asc, 0, sizeof(asc)); printf(word_format, filepos + disp_offset); fputs(": ", stdout); while(len) { c = *buf; if(screencodes) c = screen2ata(c); inv = !graphics && (c & 0x80); if(color) hpos += sprintf(hex + hpos, "%s", get_color(c)); if(inv) hpos += sprintf(hex + hpos, "%s", inverse_on); hpos += sprintf(hex + hpos, byte_format, c); if(color || inv) hpos += sprintf(hex + hpos, "%s", color_off); hex[hpos++] = ' '; if(count - len == 7) hex[hpos++] = ' '; if(color) apos += sprintf(asc + apos, "%s", get_color(c)); if(inv) apos += sprintf(asc + apos, "%s", inverse_on); apos += sprintf(asc + apos, "%s", (graphics ? get_graphics(c) : table[c & 0x7f])); if(color || inv) apos += sprintf(asc + apos, "%s", color_off); filepos++; buf++; len--; } printf("%s", hex); /* what shall we use to fill the empty spaces? */ if(count < 8) putchar(' '); for(; count < 16; count++) fputs(" ", stdout); printf(" %s\n", asc); } int a8xd(const char *file) { FILE *input; int c, len = 0, count = 0; unsigned char buf[16]; if( !(input = open_input(file)) ) return 1; if(seekpos) seek_input(input); while( (c = fgetc(input)) != EOF ) { if(limit && (count == limit)) break; count++; if(len && (len % 16 == 0)) { dump_line(buf, len); len = 0; } buf[len++] = c; } if(len) dump_line(buf, len); if(verbose) fprintf(stderr, "%s: dumped %d (0x%04x) bytes from '%s'.\n", self, count, count, file); fclose(input); return 0; } int main(int argc, char **argv) { int opt, result = 0; set_self(argv[0]); if(argc > 1 && strcmp(argv[1], "--help") == 0) { print_help(); exit(0); } if(argc > 1 && strcmp(argv[1], "--version") == 0) { printf("%s %s\n", self, VERSION); exit(0); } while( (opt = getopt(argc, argv, "vhimus:o:l:ga")) != -1) { switch(opt) { case 'v': verbose = 1; break; case 'h': print_help(); exit(0); break; case 'i': table = ics2utf; break; case 'm': color = 0; break; case 'u': word_format = UC_WORD_FMT; byte_format = UC_BYTE_FMT; break; case 's': parse_seek_arg(optarg); break; case 'o': disp_offset = parse_offset_arg(optarg); break; case 'l': limit = parse_limit_arg(optarg); break; case 'g': graphics = 1; break; case 'a': screencodes = 1; break; default: print_help(); exit(1); break; } } if(optind < argc - 1) die("Only one filename is supported."); if(optind >= argc) result = a8xd("-"); else result = a8xd(argv[optind]); exit(result); }