xref: /inferno-os/appl/cmd/uudecode.b (revision 37da2899f40661e3e9631e497da8dc59b971cbd0)
1implement Uudecode;
2
3include "sys.m";
4	sys : Sys;
5include "draw.m";
6include "string.m";
7	str : String;
8include "bufio.m";
9	bufio : Bufio;
10	Iobuf : import bufio;
11
12Uudecode : module
13{
14	init : fn(nil : ref Draw->Context, argv : list of string);
15};
16
17fatal(s : string)
18{
19	sys->fprint(sys->fildes(2), "%s\n", s);
20	exit;
21}
22
23usage()
24{
25	fatal("usage: uudecode [ -p ] [ encodedfile... ]");
26}
27
28init(nil : ref Draw->Context, argv : list of string)
29{
30	fd : ref Sys->FD;
31
32	tostdout := 0;
33	sys = load Sys Sys->PATH;
34	str = load String String->PATH;
35	bufio = load Bufio Bufio->PATH;
36	argv = tl argv;
37	if (argv != nil && hd argv == "-p") {
38		tostdout = 1;
39		argv = tl argv;
40	}
41	if (argv != nil) {
42		for (; argv != nil; argv = tl argv) {
43			fd = sys->open(hd argv, Sys->OREAD);
44			if (fd == nil)
45				fatal(sys->sprint("cannot open %s", hd argv));
46			decode(fd, tostdout);
47		}
48	}
49	else
50		decode(sys->fildes(0), tostdout);
51}
52
53code(c : byte) : int
54{
55	return (int c - ' ')&16r3f;
56}
57
58LEN : con 45;
59
60decode(ifd : ref Sys->FD, tostdout : int)
61{
62	mode : int;
63	ofile : string;
64
65	bio := bufio->fopen(ifd, Bufio->OREAD);
66	if (bio == nil)
67		fatal("cannot open input for buffered io: %r");
68	while ((s := bio.gets('\n')) != nil) {
69		if (len s >= 6 && s[0:6] == "begin ") {
70			(n, l) := sys->tokenize(s, " \n");
71			if (n < 3)
72				fatal("bad begin line");
73			(mode, nil) = str->toint(hd tl l, 8);
74			ofile = hd tl tl l;
75			break;
76		}
77	}
78	if (ofile == nil)
79		fatal("no begin line");
80	if (tostdout)
81		ofd := sys->fildes(1);
82	else {
83		if (ofile[0] == '~')	# ~user/file
84			ofile = "/usr/" + ofile[1:];
85		ofd = sys->create(ofile, Sys->OWRITE, 8r666);
86		if (ofd == nil)
87			fatal(sys->sprint("cannot create %s: %r", ofile));
88	}
89	ob := array[LEN] of byte;
90	while ((s = bio.gets('\n')) != nil) {
91		b := array of byte s;
92		n := code(b[0]);
93		if (n == 0 && (len b != 2 || b[1] != byte '\n'))
94			fatal("bad 0 count line");
95		if (n <= 0)
96			break;
97		if (n > LEN)
98			fatal("too many bytes on line");
99		e := 0; f := 0;
100		if (n%3 == 1) {
101			e = 2; f = 4;
102		}
103		else if (n%3 == 2) {
104			e = 3; f = 4;
105		}
106		if (len b < 4*(n/3)+e+2 || len b > 4*(n/3)+f+2)
107			fatal("bad uuencode count");
108		b = b[1:];
109		i := 0;
110		nl := n;
111		for (j := 0; nl > 0; j += 4) {
112			if (nl >= 1)
113				ob[i++] = byte (code(b[j+0])<<2 | code(b[j+1])>>4);
114			if (nl >= 2)
115				ob[i++] = byte (code(b[j+1])<<4 | code(b[j+2])>>2);
116			if (nl >= 3)
117				ob[i++] = byte (code(b[j+2])<<6 | code(b[j+3])>>0);
118			nl -= 3;
119		}
120		if (sys->write(ofd, ob, i) != i)
121			fatal("bad write to output: %r");
122	}
123	s = bio.gets('\n');
124	if (s == nil || len s < 4 || s[0:4] != "end\n")
125		fatal("missing end line");
126	if (!tostdout) {
127		d := sys->nulldir;
128		d.mode = mode;
129		if (sys->fwstat(ofd, d) < 0)
130			fatal(sys->sprint("cannot wstat %s: %r", ofile));
131	}
132}
133