code: purgatorio

ref: d916a4c3823f55227ffae35738c2497256e307b5
dir: /appl/cmd/xd.b/

View raw version
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;
	}
}