From a6262645aecb7c2dce55dc2fc24c5eb1511c4d0e Mon Sep 17 00:00:00 2001 From: "B. Watson" Date: Sat, 29 Jun 2024 19:15:50 -0400 Subject: a8xd: added (ATASCII-flavored hex dump). --- Makefile | 6 ++- a8xd.1 | 182 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ a8xd.c | 172 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ a8xd.rst | 108 +++++++++++++++++++++++++++++++++++++ 4 files changed, 466 insertions(+), 2 deletions(-) create mode 100644 a8xd.1 create mode 100644 a8xd.c create mode 100644 a8xd.rst diff --git a/Makefile b/Makefile index 4afaa47..47cf854 100644 --- a/Makefile +++ b/Makefile @@ -16,9 +16,9 @@ CC=gcc CFLAGS=-Wall $(COPT) -ansi -D_GNU_SOURCE -DVERSION=\"$(VERSION)\" # BINS and SCRIPTS go in $BINDIR, DOCS go in $DOCDIR -BINS=a8eol atr2xfd atrsize axe blob2c blob2xex cart2xex cxrefbas dumpbas fenders protbas renumbas rom2cart unmac65 unprotbas vxrefbas xex1to2 xexamine xexcat xexsplit xfd2atr listbas a8cat +BINS=a8eol atr2xfd atrsize axe blob2c blob2xex cart2xex cxrefbas dumpbas fenders protbas renumbas rom2cart unmac65 unprotbas vxrefbas xex1to2 xexamine xexcat xexsplit xfd2atr listbas a8cat a8xd SCRIPTS=dasm2atasm a8utf8 -MANS=a8eol.1 xfd2atr.1 atr2xfd.1 blob2c.1 cart2xex.1 fenders.1 xexsplit.1 xexcat.1 atrsize.1 rom2cart.1 unmac65.1 axe.1 dasm2atasm.1 a8utf8.1 blob2xex.1 xexamine.1 xex1to2.1 unprotbas.1 protbas.1 renumbas.1 dumpbas.1 vxrefbas.1 cxrefbas.1 listbas.1 a8cat.1 +MANS=a8eol.1 xfd2atr.1 atr2xfd.1 blob2c.1 cart2xex.1 fenders.1 xexsplit.1 xexcat.1 atrsize.1 rom2cart.1 unmac65.1 axe.1 dasm2atasm.1 a8utf8.1 blob2xex.1 xexamine.1 xex1to2.1 unprotbas.1 protbas.1 renumbas.1 dumpbas.1 vxrefbas.1 cxrefbas.1 listbas.1 a8cat.1 a8xd.1 MAN5S=xex.5 MAN7S=atascii.7 DOCS=README.txt equates.inc *.dasm LICENSE ksiders/atr.txt @@ -72,6 +72,8 @@ atables.o: atables.c atables.h a8cat: a8cat.c atables.o wtable.o +a8xd: a8xd.c atables.o + subdirs: for dir in $(SUBDIRS); do make -C $$dir COPT="$(COPT)"; done diff --git a/a8xd.1 b/a8xd.1 new file mode 100644 index 0000000..0e73cd6 --- /dev/null +++ b/a8xd.1 @@ -0,0 +1,182 @@ +.\" Man page generated from reStructuredText. +. +. +.nr rst2man-indent-level 0 +. +.de1 rstReportMargin +\\$1 \\n[an-margin] +level \\n[rst2man-indent-level] +level margin: \\n[rst2man-indent\\n[rst2man-indent-level]] +- +\\n[rst2man-indent0] +\\n[rst2man-indent1] +\\n[rst2man-indent2] +.. +.de1 INDENT +.\" .rstReportMargin pre: +. RS \\$1 +. nr rst2man-indent\\n[rst2man-indent-level] \\n[an-margin] +. nr rst2man-indent-level +1 +.\" .rstReportMargin post: +.. +.de UNINDENT +. RE +.\" indent \\n[an-margin] +.\" old: \\n[rst2man-indent\\n[rst2man-indent-level]] +.nr rst2man-indent-level -1 +.\" new: \\n[rst2man-indent\\n[rst2man-indent-level]] +.in \\n[rst2man-indent\\n[rst2man-indent-level]]u +.. +.TH "A8XD" 1 "2024-06-29" "0.2.1" "Urchlay's Atari 8-bit Tools" +.SH NAME +a8xd \- Atari ASCII aware hex dump +.SH SYNOPSIS +.sp +\fIa8xd\fP [\fB\-i\fP] [\fB\-m\fP] [\fB\-v\fP] [\fIinfile\fP] +.SH DESCRIPTION +.sp +\fBa8xd\fP is a hex dump utility, similar to \fBxxd\fP(1), but it +understands and prints ATASCII characters rather than ASCII. +.sp +The ATASCII codes are converted to UTF\-8, in the same way as +\fBa8cat\fP(1). Codes with the high bit set are displayed in inverse +video, using ANSI/VT\-100 escape sequences. +.sp +By default, the output is colorized: +.INDENT 0.0 +.INDENT 3.5 +.INDENT 0.0 +.IP \(bu 2 +Normal characters are green. This includes alphanumerics, spaces, and +punctuation. +.IP \(bu 2 +Codes \fB$00\fP (null, or ATASCII heart) and \fB$9B\fP (EOL) are red. +.IP \(bu 2 +Control characters are yellow. This includes codes \fB$01\fP to \fB$1F\fP, +(control + letters, escape, punctuation, and arrow keys), \fB$60\fP, \fB$7B\fP, \fB$7D\fP +(clear screen), \fB$7E\fP (backspace), and \fB$7F\fP (tab). +.UNINDENT +.UNINDENT +.UNINDENT +.sp +The colors and inverse rendering apply to both the ATASCII and hex bytes. +.sp +Without \fIinfile\fP, or if \fIinfile\fP is \fB\-\fP, \fBa8xd\fP reads from standard input. +.SH OPTIONS +.sp +Options marked with \fB*\fP are not yet implemented. +.INDENT 0.0 +.TP +.B \-i +Print XL/XE International Character Set conversions instead of ATASCII. +.UNINDENT +.INDENT 0.0 +.TP +.B \-l \fIlen\fP +Stop after dumping \fIlen\fP bytes. \fIlen\fP may be given in decimal or hex (with +leading \fI0x\fP or \fI$\fP). +.UNINDENT +.INDENT 0.0 +.TP +.B \-m +Monochrome mode. Disables color, but ATASCII characters with the high bit +set are still displayed in inverse. +.TP +.B \-n +\fB*\fP Narrow dump mode. Displays 8 bytes per line. Maximum line length is +less than 40 columns. Use this if your terminal is less than 75 columns +wide. +.UNINDENT +.INDENT 0.0 +.TP +.B \-o \fIoffset\fP +\fB*\fP Add \fIoffset\fP to displayed file position. \fIoffset\fP can be given in decimal +or hex (with leading \fI0x\fP or \fI$\fP). +.TP +.B \-s \fI[\-]seek\fP +\fB*\fP Start at \fIseek\fP bytes. Without \fI\-\fP, this is an absolute offset. With \fI\-\fP, +it\(aqs relative to the end of the file. The \fI\-\fP option won\(aqt work when +reading from standard input. \fIseek\fP may be given in decimal or hex +(with leading \fI0x\fP or \fI$\fP). +.UNINDENT +.INDENT 0.0 +.TP +.B \-u +\fB*\fP Use uppercase letters; the default is lowercase. +.TP +.B \-v +Verbose. Currently, this option is accepted, but it doesn\(aqt do anything yet. +.UNINDENT +.INDENT 0.0 +.TP +.B \fB\-\-\fP +End of options; the nex argument is the filename. Use this if you\(aqre +trying to work with files whose names begin with \fI\-\fP\&. +.TP +.B \fB\-h\fP, \fB\-\-help\fP +Show built\-in help and exit. +.TP +.B \fB\-\-version\fP +Show version number and exit. +.UNINDENT +.SH NOTES +.sp +\fBa8xd\fP supports a useful subset of \fBxxd\fP(1) options. The main things +missing are: +.INDENT 0.0 +.IP \(bu 2 +\fB\-r\fP (revert). +.IP \(bu 2 +\fB\-include\fP (output as C include) and all options related to it. +.IP \(bu 2 +\fB\-g\fP (grouping; \fBa8xd\fP always uses a group size of 1 byte). +.IP \(bu 2 +\fB\-E\fP (EBCDIC mode). +.IP \(bu 2 +\fB\-p\fP (PostScript/continuous dump). +.IP \(bu 2 +\fB\-cols\fP (\fBa8xd\fP only supports 8 or 16 column dumps). +.IP \(bu 2 +\fB\-b\fP (bits mode). +.UNINDENT +.SH COPYRIGHT +.sp +WTFPL. See \fI\%http://www.wtfpl.net/txt/copying/\fP for details. +.SH AUTHOR +.INDENT 0.0 +.IP B. 3 +Watson <\fI\%urchlay@slackware.uk\fP>; Urchlay on irc.libera.chat \fI##atari\fP\&. +.UNINDENT +.SH SEE ALSO +.sp +\fBa8eol\fP(1), +\fBa8utf8\fP(1), +\fBatr2xfd\fP(1), +\fBatrsize\fP(1), +\fBaxe\fP(1), +\fBblob2c\fP(1), +\fBblob2xex\fP(1), +\fBcart2xex\fP(1), +\fBcxrefbas\fP(1), +\fBdasm2atasm\fP(1), +\fBdumpbas\fP(1), +\fBf2toxex\fP(1), +\fBfenders\fP(1), +\fBlistbas\fP(1), +\fBprotbas\fP(1), +\fBrenumbas\fP(1), +\fBrom2cart\fP(1), +\fBunmac65\fP(1), +\fBunprotbas\fP(1), +\fBvxrefbas\fP(1), +\fBxexamine\fP(1), +\fBxexcat\fP(1), +\fBxexsplit\fP(1), +\fBxfd2atr\fP(1), +\fBxex\fP(5), +\fBatascii\fP(7). +.sp +Any good Atari 8\-bit book: \fIDe Re Atari\fP, \fIThe Atari BASIC Reference +Manual\fP, the \fIOS Users\(aq Guide\fP, \fIMapping the Atari\fP, etc. +.\" Generated by docutils manpage writer. +. diff --git a/a8xd.c b/a8xd.c new file mode 100644 index 0000000..52a8092 --- /dev/null +++ b/a8xd.c @@ -0,0 +1,172 @@ +#include +#include +#include +#include +#include +#include +#include + +#include "atables.h" + +const char **table = ata2utf; +const char *inverse_on = "\x1b[7m"; +const char *inverse_off = "\x1b[0m"; + +int verbose = 0, color = 1; + +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: %s [-v] [file ...]\n", self); +} + +FILE *open_input(const char *file) { + FILE *input; + + if(file[0] == '-' && file[1] == 0) { + if(verbose) + fprintf(stderr, "%s: reading from standard input.\n", self); + if(freopen(NULL, "rb", stdin)) { + input = stdin; + } else { + perror("(standard input)"); + return NULL; + } + } else if(!(input = fopen(file, "rb"))) { + perror(file); + return NULL; + } + + if(verbose) + fprintf(stderr, "%s: reading from file '%s'.\n", self, file); + + return input; +} + +const char *color_off = "\x1b[0m"; + +char *get_color(unsigned char c) { + int color; + unsigned char c7 = c & 0x7f; + static char outbuf[32]; + + 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; + } + + if(color) { + sprintf(outbuf, "\x1b[0;3%dm", color); + if(c & 0x80) strcat(outbuf, inverse_on); + } else { + outbuf[0] = '\0'; + } + + return outbuf; +} + +void dump_line(const unsigned char *buf, int len) { + char hex[1024], asc[1024]; + static int filepos = 0; + int hpos = 0, apos = 0, count = len; + + memset(hex, 0, sizeof(hex)); + memset(asc, 0, sizeof(asc)); + + printf("%04x: ", filepos); + while(len) { + if(color) hpos += sprintf(hex + hpos, "%s", get_color(*buf)); + hpos += sprintf(hex + hpos, "%02x", *buf); + if(color) hpos += sprintf(hex + hpos, "%s", color_off); + hex[hpos++] = ' '; + if(count - len == 7) hex[hpos++] = ' '; + + if(*buf & 0x80) apos += sprintf(asc + apos, "%s", inverse_on); + if(color) apos += sprintf(asc + apos, "%s", get_color(*buf)); + apos += sprintf(asc + apos, "%s", table[*buf & 0x7f]); + if(color || *buf & 0x80) 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; + unsigned char buf[16]; + + if( !(input = open_input(file)) ) + return 1; + + while( (c = fgetc(input)) != EOF ) { + if(len && (len % 16 == 0)) { + dump_line(buf, len); + len = 0; + } + buf[len++] = c; + } + + if(len) dump_line(buf, len); + + 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, "vim")) != -1) { + switch(opt) { + case 'v': verbose = 1; break; + case 'i': table = ics2utf; break; + case 'm': color = 0; break; + default: print_help(); exit(1); break; + } + } + + if(optind >= argc) { + result = a8xd("-"); + } else { + while(optind < argc) { + result += a8xd(argv[optind]); + optind++; + } + } + exit(result); +} diff --git a/a8xd.rst b/a8xd.rst new file mode 100644 index 0000000..9d6d00d --- /dev/null +++ b/a8xd.rst @@ -0,0 +1,108 @@ +==== +a8xd +==== + +-------------------------- +Atari ASCII aware hex dump +-------------------------- + +.. include:: manhdr.rst + +SYNOPSIS +======== + +*a8xd* [**-i**] [**-m**] [**-v**] [*infile*] + +DESCRIPTION +=========== + +**a8xd** is a hex dump utility, similar to **xxd**\(1), but it +understands and prints ATASCII characters rather than ASCII. + +The ATASCII codes are converted to UTF-8, in the same way as +**a8cat**\(1). Codes with the high bit set are displayed in inverse +video, using ANSI/VT-100 escape sequences. + +By default, the output is colorized: + + - Normal characters are green. This includes alphanumerics, spaces, and + punctuation. + + - Codes **$00** (null, or ATASCII heart) and **$9B** (EOL) are red. + + - Control characters are yellow. This includes codes **$01** to **$1F**, + (control + letters, escape, punctuation, and arrow keys), **$60**, **$7B**, **$7D** + (clear screen), **$7E** (backspace), and **$7F** (tab). + +The colors and inverse rendering apply to both the ATASCII and hex bytes. + +Without *infile*, or if *infile* is **-**, **a8xd** reads from standard input. + +OPTIONS +======= + +Options marked with **\*** are not yet implemented. + +-i + Print XL/XE International Character Set conversions instead of ATASCII. + +-l *len* + Stop after dumping *len* bytes. *len* may be given in decimal or hex (with + leading *0x* or *$*). + +-m + Monochrome mode. Disables color, but ATASCII characters with the high bit + set are still displayed in inverse. + +-n + **\*** Narrow dump mode. Displays 8 bytes per line. Maximum line length is + less than 40 columns. Use this if your terminal is less than 75 columns + wide. + +-o *offset* + **\*** Add *offset* to displayed file position. *offset* can be given in decimal + or hex (with leading *0x* or *$*). + +-s *[-]seek* + **\*** Start at *seek* bytes. Without *-*, this is an absolute offset. With *-*, + it's relative to the end of the file. The *-* option won't work when + reading from standard input. *seek* may be given in decimal or hex + (with leading *0x* or *$*). + +-u + **\*** Use uppercase letters; the default is lowercase. + +-v + Verbose. Currently, this option is accepted, but it doesn't do anything yet. + +**--** + End of options; the nex argument is the filename. Use this if you're + trying to work with files whose names begin with *-*. + +**-h**, **--help** + Show built-in help and exit. + +**--version** + Show version number and exit. + +NOTES +===== + +**a8xd** supports a useful subset of **xxd**\(1) options. The main things +missing are: + +- **-r** (revert). + +- **-include** (output as C include) and all options related to it. + +- **-g** (grouping; **a8xd** always uses a group size of 1 byte). + +- **-E** (EBCDIC mode). + +- **-p** (PostScript/continuous dump). + +- **-cols** (**a8xd** only supports 8 or 16 column dumps). + +- **-b** (bits mode). + +.. include:: manftr.rst -- cgit v1.2.3