aboutsummaryrefslogtreecommitdiffstats
path: root/usr/xxd.c
diff options
context:
space:
mode:
authoruser <user@clank>2026-09-24 13:56:42 +0200
committeruser <user@clank>2026-09-24 13:56:42 +0200
commit0fa1b946ec5fd3f5cba49215ea9ab62c4004aa53 (patch)
tree2d50213ab441f2543ba129241ceda29c5d9b496d /usr/xxd.c
parentircd: enforce unique nicknames (433), announce renames, fold case (diff)
downloadgbos-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.c82
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);
+}