1 /***********************************************************************
2 *                                                                      *
3 *               This software is part of the ast package               *
4 *          Copyright (c) 2003-2011 AT&T Intellectual Property          *
5 *                      and is licensed under the                       *
6 *                 Eclipse Public License, Version 1.0                  *
7 *                    by AT&T Intellectual Property                     *
8 *                                                                      *
9 *                A copy of the License is available at                 *
10 *          http://www.eclipse.org/org/documents/epl-v10.html           *
11 *         (with md5 checksum b35adb5213ca9657e911e9befb180842)         *
12 *                                                                      *
13 *              Information and Software Systems Research               *
14 *                            AT&T Research                             *
15 *                           Florham Park NJ                            *
16 *                                                                      *
17 *                 Glenn Fowler <gsf@research.att.com>                  *
18 *                  David Korn <dgk@research.att.com>                   *
19 *                   Phong Vo <kpv@research.att.com>                    *
20 *                                                                      *
21 ***********************************************************************/
22 #pragma prototyped
23 
24 /*
25  * uuencode familiy coders
26  */
27 
28 #include <codex.h>
29 #include <ctype.h>
30 
31 #define UUIN		3
32 #define UUOUT		4
33 #define UUCHUNK		15
34 
35 #define UU_END		(UCHAR_MAX)
36 #define UU_IGN		(UCHAR_MAX-1)
37 #define UU_PAD		(UCHAR_MAX-2)
38 
39 typedef struct
40 {
41 	int		pad;
42 	int		fill;
43 	int		length;
44 	const char	map[65];
45 } Data_t;
46 
47 typedef struct State_s
48 {
49 	Codex_t*	codex;
50 
51 	const Data_t*	data;
52 
53 	unsigned char*	bb;
54 	unsigned char*	bp;
55 	unsigned char*	bl;
56 	unsigned char*	be;
57 	unsigned char*	map;
58 	unsigned char*	pb;
59 	unsigned char*	pp;
60 
61 	int		c1;
62 	int		c2;
63 	int		nl;
64 	int		string;
65 	int		text;
66 
67 	unsigned char	mapbuf[UCHAR_MAX + 2];
68 	unsigned char	buf[SF_BUFSIZE];
69 	unsigned char	peek[UUIN];
70 } State_t;
71 
72 static const Data_t	uu_base64 =
73 {
74 	'=',
75 	0,
76 	0,
77 	"ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
78 };
79 
80 static const Data_t	uu_bsd =
81 {
82 	0,
83 	0156,
84 	1,
85 	"`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
86 };
87 
88 static const Data_t	uu_posix =
89 {
90 	0,
91 	0,
92 	1,
93 	" !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
94 };
95 
96 #define GETCHAR(p)		((p)->bp < (p)->be ? (int)*(p)->bp++ : fill(p))
97 #define PUTCHAR(p,s,e,c)	((s<e) ? (*s++=(c)) : (*p->pp++=(c)))
98 
99 static int
fill(State_t * state)100 fill(State_t* state)
101 {
102 	ssize_t	r;
103 
104 	state->bp = state->buf;
105 	if ((r = sfrd(state->codex->sp, state->bp, sizeof(state->buf), &state->codex->sfdisc)) <= 0)
106 	{
107 		state->be = state->bp;
108 		return EOF;
109 	}
110 	state->be = state->bp + r;
111 	return *state->bp++;
112 }
113 
114 static int
flush(register State_t * state)115 flush(register State_t* state)
116 {
117 	uint32_t	b;
118 	int		c3;
119 	int		x;
120 
121 	x = 1;
122 	if (state->c1 >= 0)
123 	{
124 		c3 = state->data->fill;
125 		x++;
126 		if (state->c2 < 0)
127 		{
128 			state->c2 = c3;
129 			x++;
130 		}
131 		b = (state->c1 << 16) | (state->c2 << 8) | c3;
132 		*state->bp++ = state->data->map[b >> 18];
133 		*state->bp++ = state->data->map[(b >> 12) & 077];
134 		*state->bp++ = x == 3 && state->data->pad ? state->data->pad : state->data->map[(b >> 6) & 077];
135 		*state->bp++ = state->data->pad ? state->data->pad : state->data->map[b & 077];
136 		state->c1 = state->c2 = -1;
137 	}
138 	if ((state->bl - state->bp) < UUOUT * UUCHUNK || state->bp > state->buf + !!state->bb)
139 	{
140 		if (state->bb)
141 			*state->bb = state->data->map[((state->bp - state->bb - x) / UUOUT) * UUIN + 1];
142 		if (state->string)
143 		{
144 			if (*(state->bp - 1) == '\n')
145 				state->bp--;
146 		}
147 		else
148 		{
149 			if (*(state->bp - 1) != '\n')
150 				*state->bp++ = '\n';
151 		}
152 		x = state->bp - state->buf;
153 		state->bp = state->buf;
154 		state->bl = state->bp + UUOUT * UUCHUNK;
155 		if (sfwr(state->codex->sp, state->buf, x, &state->codex->sfdisc) != x)
156 			return EOF;
157 	}
158 	return 0;
159 }
160 
161 static int
uu_open(Codex_t * p,char * const args[],Codexnum_t flags)162 uu_open(Codex_t* p, char* const args[], Codexnum_t flags)
163 {
164 	register State_t*	state;
165 	register char*		s;
166 	register char**		a;
167 	unsigned char*		m;
168 	unsigned char*		q;
169 	const Data_t*		data;
170 	int			c;
171 	int			n;
172 	int			string;
173 	int			text;
174 
175 	data = &uu_posix;
176 	string = text = 0;
177 	a = (char**)args + 1;
178 	while (s = *++a)
179 		if (streq(s, "base64") || streq(s, "mime"))
180 			data = &uu_base64;
181 		else if (streq(s, "bsd") || streq(s, "ucb"))
182 			data = &uu_bsd;
183 		else if (streq(s, "posix"))
184 			data = &uu_posix;
185 		else if (streq(s, "string"))
186 		{
187 			if (data != &uu_base64)
188 			{
189 				if (p->disc->errorf)
190 					(*p->disc->errorf)(NiL, p->disc, 2, "%s: %s: option valid for base64/mime only", p->meth->name, s);
191 				return -1;
192 			}
193 			string = 1;
194 		}
195 		else if (streq(s, "text"))
196 			text = 1;
197 		else
198 		{
199 			if (p->disc->errorf)
200 				(*p->disc->errorf)(NiL, p->disc, 2, "%s: %s: unknown option", p->meth->name, s);
201 			return -1;
202 		}
203 	if (!(state = newof(0, State_t, 1, 0)))
204 	{
205 		if (p->disc->errorf)
206 			(*p->disc->errorf)(NiL, p->disc, 2, "out of space");
207 		return -1;
208 	}
209 	state->data = data;
210 	state->string = string;
211 	state->text = text;
212 	if (p->flags & CODEX_DECODE)
213 	{
214 		n = data->length ? 0 : UU_IGN;
215 		q = state->mapbuf;
216 		memset(q, n, sizeof(state->mapbuf));
217 		state->map = ++q;
218 		q[EOF] = UU_END;
219 		if (n)
220 			q[data->pad] = UU_PAD;
221 		for (m = (unsigned char*)data->map; c = *m; m++)
222 			q[c] =  m - (unsigned char*)data->map;
223 	}
224 	p->data = state;
225 	state->codex = p;
226 	return 0;
227 }
228 
229 static int
uu_init(Codex_t * p)230 uu_init(Codex_t* p)
231 {
232 	register State_t*	state = (State_t*)p->data;
233 	int			n;
234 
235 	state->bp = state->buf;
236 	if (p->flags & CODEX_ENCODE)
237 	{
238 		n = UUOUT * UUCHUNK + state->data->length + 1;
239 		state->be = state->bp + (sizeof(state->buf) / n) * n;
240 		if (state->data->length)
241 		{
242 			state->bb = state->bp;
243 			*state->bp++ = state->data->map[UUIN * UUCHUNK];
244 		}
245 		state->bl = state->bp + UUOUT * UUCHUNK;
246 		state->c1 = state->c2 = -1;
247 	}
248 	else
249 	{
250 		state->be = state->bp;
251 		state->pb = state->pp = state->peek;
252 		if (state->data->length)
253 			state->nl = -1;
254 		state->c1 = state->c2 = -1;
255 	}
256 	return 0;
257 }
258 
259 static ssize_t
uu_read(Sfio_t * sp,void * buf,size_t n,Sfdisc_t * disc)260 uu_read(Sfio_t* sp, void* buf, size_t n, Sfdisc_t* disc)
261 {
262 	register State_t*	state = (State_t*)CODEX(disc)->data;
263 	register char*		s = (char*)buf;
264 	register char*		e = s + n;
265 	register uint32_t	b;
266 	register int		c;
267 	register int		x;
268 
269 	if (state->pb < state->pp)
270 	{
271 		while (s < e && state->pb < state->pp)
272 			*s++ = *state->pb++;
273 		if (state->pb == state->pp)
274 			state->pb = state->pp = state->peek;
275 	}
276 	if (state->data->length)
277 		while (s < e)
278 		{
279 			switch (c = GETCHAR(state))
280 			{
281 			case EOF:
282 				goto done;
283 			case '\n':
284 				state->nl = -1;
285 				continue;
286 			}
287 			if (state->nl < 0)
288 				state->nl = state->map[c];
289 			else if (state->nl > 0)
290 			{
291 				b = state->map[c];
292 				if ((c = GETCHAR(state)) == EOF)
293 					c = 0;
294 				else
295 					c = state->map[c];
296 				b = (b << 6) | c;
297 				if ((c = GETCHAR(state)) == EOF)
298 					c = 0;
299 				else
300 					c = state->map[c];
301 				b = (b << 6) | c;
302 				if ((c = GETCHAR(state)) == EOF)
303 					c = 0;
304 				else
305 					c = state->map[c];
306 				b = (b << 6) | c;
307 				if (state->text)
308 				{
309 					if ((c = (b >> 16) & 0xFF) != '\r')
310 						PUTCHAR(state, s, e, c);
311 					if ((c = (b >> 8) & 0xFF) != '\r')
312 						PUTCHAR(state, s, e, c);
313 					if ((c = b & 0xFF) != '\r')
314 						PUTCHAR(state, s, e, c);
315 				}
316 				else
317 				{
318 					PUTCHAR(state, s, e, (b >> 16));
319 					PUTCHAR(state, s, e, (b >> 8));
320 					PUTCHAR(state, s, e, b);
321 				}
322 				if ((state->nl -= 3) < 0)
323 					while (state->nl++ < 0)
324 						s--;
325 			}
326 		}
327 	else
328 		while (s < e)
329 		{
330 			while ((c = state->map[GETCHAR(state)]) >= 64)
331 				if (c != UU_IGN)
332 					goto done;
333 			b = c;
334 			while ((c = state->map[GETCHAR(state)]) >= 64)
335 				if (c != UU_IGN)
336 				{
337 					if (state->codex->disc->errorf)
338 						(*state->codex->disc->errorf)(NiL, state->codex->disc, 1, "%c: extra input character ignored", c);
339 					goto done;
340 				}
341 			b = (b << 6) | c;
342 			while ((c = state->map[GETCHAR(state)]) >= 64)
343 				if (c != UU_IGN)
344 				{
345 					if (state->text)
346 					{
347 						if ((x = (b >> 4) & 0xFF) != '\r')
348 							PUTCHAR(state, s, e, x);
349 					}
350 					else
351 						PUTCHAR(state, s, e, (b >> 4));
352 					goto done;
353 				}
354 			b = (b << 6) | c;
355 			while ((c = state->map[GETCHAR(state)]) >= 64)
356 				if (c != UU_IGN)
357 				{
358 					if (state->text)
359 					{
360 						if ((x = (b >> 10) & 0xFF) != '\r')
361 							PUTCHAR(state, s, e, x);
362 						if ((x = (b >> 2) & 0xFF) != '\r')
363 							PUTCHAR(state, s, e, x);
364 					}
365 					else
366 					{
367 						PUTCHAR(state, s, e, (b >> 10));
368 						PUTCHAR(state, s, e, (b >> 2));
369 					}
370 					goto done;
371 				}
372 			b = (b << 6) | c;
373 			if (state->text)
374 			{
375 				if ((x = (b >> 16) & 0xFF) != '\r')
376 					PUTCHAR(state, s, e, x);
377 				if ((x = (b >> 8) & 0xFF) != '\r')
378 					PUTCHAR(state, s, e, x);
379 				if ((x = b & 0xFF) != '\r')
380 					PUTCHAR(state, s, e, x);
381 			}
382 			else
383 			{
384 				PUTCHAR(state, s, e, (b >> 16));
385 				PUTCHAR(state, s, e, (b >> 8));
386 				PUTCHAR(state, s, e, b);
387 			}
388 		}
389  done:
390 	return s - (char*)buf;
391 }
392 
393 static ssize_t
uu_write(Sfio_t * sp,const void * buf,size_t n,Sfdisc_t * disc)394 uu_write(Sfio_t* sp, const void* buf, size_t n, Sfdisc_t* disc)
395 {
396 	register State_t*	state = (State_t*)CODEX(disc)->data;
397 	register unsigned char*	s;
398 	register unsigned char*	e;
399 	register uint32_t	b;
400 	register int		c1;
401 	register int		c2;
402 	register int		c3;
403 
404 	s = (unsigned char*)buf;
405 	e = s + n;
406 	if ((c1 = state->c1) >= 0)
407 	{
408 		state->c1 = -1;
409 		if ((c2 = state->c2) >= 0)
410 		{
411 			state->c2 = -1;
412 			goto get_3;
413 		}
414 		goto get_2;
415 	}
416 	while (s < e)
417 	{
418 		do
419 		{
420 			if (state->nl)
421 			{
422 				state->nl = 0;
423 				c1 = '\n';
424 				goto get_2;
425 			}
426 			if (s >= e)
427 				break;
428 			c1 = *s++;
429 			if (state->text && c1 == '\n')
430 			{
431 				c1 = '\r';
432 				c2 = '\n';
433 				goto get_3;
434 			}
435  get_2:
436 			if (s >= e)
437 			{
438 				state->c1 = c1;
439 				return n;
440 			}
441 			c2 = *s++;
442 			if (state->text && c2 == '\n')
443 			{
444 				c2 = '\r';
445 				c3 = '\n';
446 				goto put_123;
447 			}
448  get_3:
449 			if (s >= e)
450 			{
451 				state->c1 = c1;
452 				state->c2 = c2;
453 				return n;
454 			}
455 			c3 = *s++;
456 			if (state->text && c3 == '\n')
457 			{
458 				state->nl = 1;
459 				c3 = '\r';
460 			}
461  put_123:
462 			b = (c1 << 16) | (c2 << 8) | c3;
463 			*state->bp++ = state->data->map[b >> 18];
464 			*state->bp++ = state->data->map[(b >> 12) & 077];
465 			*state->bp++ = state->data->map[(b >> 6) & 077];
466 			*state->bp++ = state->data->map[b & 077];
467 		} while (state->bp < state->bl);
468 		if (!state->string)
469 			*state->bp++ = '\n';
470 		if (state->bp >= state->be)
471 		{
472 			if (sfwr(sp, state->buf, state->bp - state->buf, disc) != (state->bp - state->buf))
473 				return -1;
474 			state->bp = state->buf;
475 		}
476 		if (state->bb)
477 		{
478 			state->bb = state->bp;
479 			*state->bp++ = state->data->map[UUIN * UUCHUNK];
480 		}
481 		state->bl = state->bp + UUOUT * UUCHUNK;
482 	}
483 	return n;
484 }
485 
486 static int
uu_sync(Codex_t * p)487 uu_sync(Codex_t* p)
488 {
489 	return (p->flags & CODEX_ENCODE) ? flush((State_t*)p->data) : 0;
490 }
491 
492 Codexmeth_t	codex_uu =
493 {
494 	"uu",
495 	"uuencode printable encoding.",
496 	"[+posix?Posix \buuencode\b(1). This is the default.]"
497 	"[+base64|mime?MIME base64 encoding.]"
498 	"[+bsd|ucb?BSD \buuencode\b(1).]"
499 	"[+string?Encode into a string with no separators (base64 only).]"
500 	"[+text?Encode \\n => \\r\\n, decode \\r\\n => \\n.]"
501 	"[+(version)?codex-uu (AT&T Research) 2010-01-15]"
502 	"[+(author)?Glenn Fowler <gsf@research.att.com>]",
503 	CODEX_DECODE|CODEX_ENCODE|CODEX_UU,
504 	0,
505 	0,
506 	uu_open,
507 	0,
508 	uu_init,
509 	0,
510 	uu_read,
511 	uu_write,
512 	uu_sync,
513 	0,
514 	0,
515 	0,
516 	0,
517 	CODEXNEXT(uu)
518 };
519 
520 CODEXLIB(uu)
521