diff options
| author | user <user@clank> | 2026-09-24 13:56:42 +0200 |
|---|---|---|
| committer | user <user@clank> | 2026-09-24 13:56:42 +0200 |
| commit | 0fa1b946ec5fd3f5cba49215ea9ab62c4004aa53 (patch) | |
| tree | 2d50213ab441f2543ba129241ceda29c5d9b496d /usr/xxd.c | |
| parent | ircd: enforce unique nicknames (433), announce renames, fold case (diff) | |
| download | gbos-0fa1b946ec5fd3f5cba49215ea9ab62c4004aa53.tar.gz gbos-0fa1b946ec5fd3f5cba49215ea9ab62c4004aa53.tar.xz gbos-0fa1b946ec5fd3f5cba49215ea9ab62c4004aa53.zip | |
usr: xxd - hex dump a file or stdin
Eight bytes per line rather than the usual sixteen: offset + 8 hex pairs +
the ASCII column is 38 of the terminal's 40 columns, where 16 would wrap
every line and make the dump unreadable. -c overrides it.
No -s (skip): there is no seek syscall, so it streams from the start - the
same reason tail(1) has to keep a ring.
/# xxd h
0000: 48 65 6c 6c 6f 20 67 62 Hello gb
0008: 6f 73 0d 0a os..
blk(1) could already peek at raw disk blocks; nothing could look inside a
file. Verified against known bytes, from stdin, with -c, through a pipe, and
on a missing file.
Diffstat (limited to 'usr/xxd.c')
| -rw-r--r-- | usr/xxd.c | 82 |
1 files changed, 82 insertions, 0 deletions
diff --git a/usr/xxd.c b/usr/xxd.c new file mode 100644 index 0000000..8e255ae --- /dev/null +++ b/usr/xxd.c @@ -0,0 +1,82 @@ +#include "gbos.h" +/* xxd [-c N] [file]: hex dump a file, or stdin. + * + * Eight bytes per line, not the usual sixteen: offset + 8 hex pairs + the + * ASCII column is 38 of the terminal's 40 columns, where 16 would wrap every + * line and make the dump unreadable. -c overrides it anyway. + * + * There is no seek syscall, so this streams from the start - which is also + * why there is no -s (skip). */ + +#define MAXC 16 + +static char hexd(unsigned char v) { return (char)(v < 10 ? '0' + v : 'a' + (v - 10)); } +static void hex2(unsigned char v) { + putc(hexd((unsigned char)(v >> 4))); + putc(hexd((unsigned char)(v & 15))); +} +static void hex4(unsigned int v) { + hex2((unsigned char)(v >> 8)); + hex2((unsigned char)v); +} + +void main(void) { + char *argv[8]; + unsigned char buf[MAXC]; + unsigned char argc = argv_parse(argv, 8); + char *cv = optval(argv, argc, 'c'); + unsigned char cols = 8, fd = NOFD, n = 0, i; + unsigned int off = 0; + char *fname = 0; + int ch; + + if (cv) { + cols = atou(cv); + if (cols == 0 || cols > MAXC) cols = 8; + } + for (i = 0; i < argc; i++) { + if (argv[i][0] == '-') continue; + if (cv == argv[i]) continue; + fname = argv[i]; + break; + } + if (fname) { + fd = open(fname, O_READ); + if (fd == EISDIR) { + puts("xxd: is a directory"); + nl(); + sexit(2); + } + if (fd == NOFD) { + puts("xxd: no such file"); + nl(); + sexit(2); + } + } + + for (;;) { + if (fd == NOFD) { + char c = readc(); + ch = (c == EOF) ? -1 : c; + } else ch = fgetc(fd); + if (ch >= 0) buf[n++] = (unsigned char)ch; + if (n == cols || (ch < 0 && n)) { /* full line, or the last one */ + hex4(off); + puts(": "); + for (i = 0; i < cols; i++) { + if (i < n) hex2(buf[i]); + else { + putc(' '); + putc(' '); + } /* pad so ASCII stays aligned */ + putc(' '); + } + for (i = 0; i < n; i++) putc((buf[i] >= 32 && buf[i] < 127) ? (char)buf[i] : '.'); + nl(); + off += n; + n = 0; + } + if (ch < 0) break; + } + if (fd != NOFD) close(fd); +} |
