shithub: purgatorio

ref: ec35f468e0eba87c9f09cbbe5fa8af2591e6f914
dir: /appl/cmd/uudecode.b/

View raw version
implement Uudecode;

include "sys.m";
	sys : Sys;
include "draw.m";
include "string.m";
	str : String;
include "bufio.m";
	bufio : Bufio;
	Iobuf : import bufio;

Uudecode : module
{
	init : fn(nil : ref Draw->Context, argv : list of string);
};

fatal(s : string)
{
	sys->fprint(sys->fildes(2), "%s\n", s);
	exit;
}

usage()
{
	fatal("usage: uudecode [ -p ] [ encodedfile... ]");
}

init(nil : ref Draw->Context, argv : list of string)
{
	fd : ref Sys->FD;

	tostdout := 0;
	sys = load Sys Sys->PATH;
	str = load String String->PATH;
	bufio = load Bufio Bufio->PATH;
	argv = tl argv;
	if (argv != nil && hd argv == "-p") {
		tostdout = 1;
		argv = tl argv;
	}
	if (argv != nil) {
		for (; argv != nil; argv = tl argv) {
			fd = sys->open(hd argv, Sys->OREAD);
			if (fd == nil)
				fatal(sys->sprint("cannot open %s", hd argv));
			decode(fd, tostdout);
		}
	}
	else
		decode(sys->fildes(0), tostdout);
}

code(c : byte) : int
{
	return (int c - ' ')&16r3f;
}

LEN : con 45;
			
decode(ifd : ref Sys->FD, tostdout : int)
{
	mode : int;
	ofile : string;

	bio := bufio->fopen(ifd, Bufio->OREAD);
	if (bio == nil)
		fatal("cannot open input for buffered io: %r");
	while ((s := bio.gets('\n')) != nil) {
		if (len s >= 6 && s[0:6] == "begin ") {
			(n, l) := sys->tokenize(s, " \n");
			if (n < 3)
				fatal("bad begin line");
			(mode, nil) = str->toint(hd tl l, 8);
			ofile = hd tl tl l;
			break;
		}
	}
	if (ofile == nil)
		fatal("no begin line");
	if (tostdout)
		ofd := sys->fildes(1);
	else {
		if (ofile[0] == '~')	# ~user/file
			ofile = "/usr/" + ofile[1:];
		ofd = sys->create(ofile, Sys->OWRITE, 8r666);
		if (ofd == nil)
			fatal(sys->sprint("cannot create %s: %r", ofile));
	}
	ob := array[LEN] of byte;
	while ((s = bio.gets('\n')) != nil) {
		b := array of byte s;
		n := code(b[0]);
		if (n == 0 && (len b != 2 || b[1] != byte '\n'))
			fatal("bad 0 count line");
		if (n <= 0)
			break;
		if (n > LEN)
			fatal("too many bytes on line");
		e := 0; f := 0;
		if (n%3 == 1) {
			e = 2; f = 4;
		}
		else if (n%3 == 2) {
			e = 3; f = 4;
		}
		if (len b < 4*(n/3)+e+2 || len b > 4*(n/3)+f+2)
			fatal("bad uuencode count");
		b = b[1:];
		i := 0;
		nl := n;
		for (j := 0; nl > 0; j += 4) {
			if (nl >= 1)
				ob[i++] = byte (code(b[j+0])<<2 | code(b[j+1])>>4);
			if (nl >= 2)
				ob[i++] = byte (code(b[j+1])<<4 | code(b[j+2])>>2);
			if (nl >= 3)
				ob[i++] = byte (code(b[j+2])<<6 | code(b[j+3])>>0);
			nl -= 3;
		}
		if (sys->write(ofd, ob, i) != i)
			fatal("bad write to output: %r");	
	}
	s = bio.gets('\n');
	if (s == nil || len s < 4 || s[0:4] != "end\n")
		fatal("missing end line");
	if (!tostdout) {
		d := sys->nulldir;
		d.mode = mode;
		if (sys->fwstat(ofd, d) < 0)
			fatal(sys->sprint("cannot wstat %s: %r", ofile));
	}
}