summaryrefslogtreecommitdiff
path: root/appl/cmd/xd.b
diff options
context:
space:
mode:
Diffstat (limited to 'appl/cmd/xd.b')
-rw-r--r--appl/cmd/xd.b316
1 files changed, 316 insertions, 0 deletions
diff --git a/appl/cmd/xd.b b/appl/cmd/xd.b
new file mode 100644
index 00000000..fa032550
--- /dev/null
+++ b/appl/cmd/xd.b
@@ -0,0 +1,316 @@
+implement Xd;
+
+#
+# based on Plan9 xd
+#
+
+include "sys.m";
+include "draw.m";
+include "bufio.m";
+
+Xd: module
+{
+ init: fn(ctxt: ref Draw->Context, argv: list of string);
+};
+
+sys : Sys;
+bufio : Bufio;
+Iobuf : import bufio;
+stdin, stdout, stderr : ref Sys->FD;
+
+wbytes := array [] of {
+ 1,
+ 2,
+ 4,
+ 8,
+};
+fmtchars : con "odx";
+fmtbases := array [] of {
+ 8,
+ 10,
+ 16,
+};
+fwidths := array [] of {
+ 3, # 1o
+ 3, # 1d
+ 2, # 1x
+ 6, # 2o
+ 5, # 2d
+ 4, # 2x
+ 11, # 4o
+ 10, # 4d
+ 8, # 4x
+ 22, # 8o
+ 20, # 8d
+ 16, # 8x
+};
+
+bytepos := array [16] of { * => 0 };
+
+formats := array [10] of (int, int, int); # (nbytes, base, fieldwidth)
+nformats := 0;
+addrbase := 16;
+repeats := 0;
+swab := 0;
+flush := 0;
+addr := big 0;
+output : ref Iobuf;
+pad : string;
+
+
+init(nil : ref Draw->Context, argv : list of string)
+{
+ sys = load Sys Sys->PATH;
+ stdin = sys->fildes(0);
+ stdout = sys->fildes(1);
+ stderr = sys->fildes(2);
+
+ bufio = load Bufio Bufio->PATH;
+ if (bufio == nil) {
+ sys->fprint(stderr, "cannot load bufio: %r\n");
+ raise "fail:init";
+ }
+ output = bufio->fopen(stdout, Sys->OWRITE);
+ if (argv == nil)
+ raise "fail:bad argv";
+
+ pad = string array [32] of { * => byte ' ' };
+
+ for (argv = tl argv; argv != nil; argv = tl argv) {
+ arg := hd argv;
+ if (arg == nil)
+ continue;
+ if (arg[0] != '-')
+ break;
+
+ if (len arg == 2) {
+ case arg[1] {
+ 'c' =>
+ addformat(0, 256);
+ 'r' =>
+ repeats = 1;
+ 's' =>
+ swab = 1;
+ 'u' =>
+ flush = 1;
+ * =>
+ usage();
+ }
+ continue;
+ }
+ # XXX should allow -x1, -x
+ if (len arg == 3) {
+ n := 0;
+ baseix := strchr(fmtchars,arg[2]);
+ if (baseix == -1)
+ usage();
+ case arg[1] {
+ 'a' =>
+ addrbase = fmtbases[baseix];
+ continue;
+ 'b' or '1' => n = 0;
+ 'w' or '2' => n = 1;
+ 'l' or '4' => n = 2;
+ 'v' or '8' => n = 3;
+ * =>
+ usage();
+ }
+ addformat(n, baseix);
+ continue;
+ }
+ usage();
+ }
+ if (nformats == 0)
+ addformat(2, 2); # "4x"
+
+ if (argv == nil)
+ dump(nil, 0);
+ else if (tl argv == nil)
+ dump(hd argv, 0);
+ else {
+ for (; argv != nil; argv = tl argv) {
+ dump(hd argv, 1);
+ }
+ }
+}
+
+usage()
+{
+ sys->fprint(stderr, "usage: xd [-u] [-r] [-s] [-a{odx}] [-c|{b1w2l4v8}{odx}] ... file ...\n");
+ raise "fail:usage";
+}
+
+strchr(s : string, ch : int) : int
+{
+ for (ix := 0; ix < len s; ix++)
+ if (s[ix] == ch)
+ return ix;
+ return -1;
+}
+
+addformat(widix, baseix : int)
+{
+ nbytes := wbytes[widix];
+ if (nformats >= len formats) {
+ sys->fprint(stderr, "xd: too many formats\n");
+ raise "fail:error";
+ }
+ fw : int;
+ if (baseix == 256) {
+ # special -c case
+ formats[nformats++] = (nbytes, 256, 2);
+ fw = 2;
+ } else {
+ fw = fwidths[baseix + (widix *len fmtbases)];
+ formats[nformats++] = (nbytes, fmtbases[baseix], fw);
+ }
+ bpos := 0;
+ for (ix := 0; ix < 16; ix += nbytes) {
+ if (bytepos[ix] >= bpos)
+ bpos = bytepos[ix];
+ else {
+ d := bpos - bytepos[ix];
+ for (dix := ix; dix < 16; dix++)
+ bytepos[dix] += d;
+ }
+ bpos += fw + 1;
+ }
+}
+
+dump(path : string, title : int)
+{
+ input := bufio->fopen(stdin, Sys->OREAD);
+ zeros := array [16] of {* => byte 0};
+
+ if (path != nil) {
+ input = bufio->open(path, Sys->OREAD);
+ if (input == nil) {
+ sys->fprint(stderr, "xd: cannot open %s: %r\n", path);
+ raise "fail:cannot open";
+ }
+ }
+
+ if (title) {
+ output.puts(path);
+ output.putc('\n');
+ }
+
+ addr = big 0;
+ star := 0;
+ obuf: array of byte;
+
+ for (;;) {
+ n := 0;
+ buf := array [16] of byte;
+ while (n < 16 && (r := input.read(buf[n:], 16 - n)) > 0)
+ n += r;
+ if (n < 16)
+ buf[n:] = zeros[n:];
+ if (swab)
+ doswab(buf);
+ if (n == 16 && repeats) {
+ if (obuf != nil && buf[0]==obuf[0]) {
+ for (i := 0; i < 16; i++)
+ if (obuf[i] != buf[i])
+ break;
+ if (i == 16) {
+ addr += big 16;
+ if (star == 0) {
+ star++;
+ output.puts("*\n");
+ }
+ continue;
+ }
+ }
+ obuf = buf;
+ star = 0;
+ }
+ for (fmt := 0; fmt < nformats; fmt++) {
+ if (fmt == 0)
+ output.puts(big2str(addr, 7, addrbase, '0'));
+ else
+ output.puts(big2str(addr, 7, addrbase, ' '));
+ output.putc(' ');
+ (w, b, fw) := formats[fmt];
+ pdata(fw, w, b, n, buf);
+ output.putc('\n');
+ if (flush)
+ output.flush();
+ }
+ addr += big n;
+ if (n < 16) {
+ output.puts(big2str(addr, 7, addrbase, '0'));
+ output.putc('\n');
+ if (flush)
+ output.flush();
+ break;
+ }
+ }
+ output.flush();
+}
+
+hexchars : con "0123456789abcdef";
+
+big2str(b : big, minw, base, padc : int) : string
+{
+ s := "";
+ do {
+ d := int (b % big base);
+ s[len s] = hexchars[d];
+ b /= big base;
+ } while (b > big 0);
+ t := "";
+ if (len s < minw)
+ t = string array [minw] of { * => byte padc };
+ else
+ t = s;
+ for (i := len s - 1; i >= 0; i--)
+ t[len t - 1 - i] = s[i];
+ return t;
+}
+
+pdata(fw, n, base, dlen : int, data : array of byte)
+{
+ nout := 0;
+ text := "";
+
+ for (i := 0; i < dlen; i += n) {
+ if (i != 0) {
+ padlen := bytepos[i] - nout;
+ output.puts(pad[0:padlen]);
+ nout += padlen;
+ }
+ if (base == 256) {
+ # special -c case
+ ch := int data[i];
+ case ch {
+ '\t' => text = "\\t";
+ '\r' => text = "\\r";
+ '\n' => text = "\\n";
+ '\b' => text = "\\b";
+ * =>
+ if (ch >= 16r7f || ' ' > ch)
+ text = sys->sprint("%.2x", ch);
+ else
+ text = sys->sprint("%c", ch);
+ }
+ } else {
+ v := big data[i];
+ for (ix := 1; ix < n; ix++)
+ v = (v << 8) + big data[i+ix];
+ text = big2str(v, fw, base, '0');
+ }
+ output.puts(text);
+ nout += len text;
+ }
+}
+
+doswab(b : array of byte)
+{
+ ix := 0;
+ for (i := 0; i < 4; i++) {
+ (b[ix], b[ix+3]) = (b[ix+3], b[ix]);
+ (b[ix+1], b[ix+2]) = (b[ix+2], b[ix+1]);
+ ix += 4;
+ }
+}