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; } }