1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1998-2012 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 <glenn.s.fowler@gmail.com> *
18 * *
19 ***********************************************************************/
20 #pragma prototyped
21 /*
22 * Glenn Fowler
23 * AT&T Research
24 *
25 * uuencode/uudecode methods
26 */
27
28 static char id[] = "\n@(#)$Id: uulib (AT&T Research) 2003-01-07 $\0\n";
29
30 static const char lib[] = "libuu:uu";
31
32 #include "uulib.h"
33
34 #include <ctype.h>
35 #include <error.h>
36 #include <ls.h>
37 #include <modex.h>
38 #include <sfdisc.h>
39
40 #define UU_BEGIN "begin"
41 #define UU_BEGIN_LEN (sizeof(UU_BEGIN)-1)
42
43 #define UUIN 3
44 #define UUOUT 4
45 #define UUCHUNK 15
46 #define UULINE 76
47
48 #define UU_END (UCHAR_MAX)
49 #define UU_IGN (UCHAR_MAX-1)
50 #define UU_PAD (UCHAR_MAX-2)
51
52 typedef struct
53 {
54 const char* name;
55 unsigned long flag;
56 } Option_t;
57
58 static const Option_t options[] =
59 {
60 "text", UU_TEXT,
61 };
62
63 /*
64 * initialize the uu map from dp
65 */
66
67 static unsigned char*
uu_map(register Uudata_t * dp,char * map)68 uu_map(register Uudata_t* dp, char* map)
69 {
70 register int c;
71 register char* p;
72 register unsigned char* m;
73 int x;
74
75 x = (dp->flags & UU_LENGTH) ? 0 : UU_IGN;
76 p = map;
77 memset(p, x, UCHAR_MAX + 2);
78 if (x)
79 {
80 p++;
81 p[dp->pad] = UU_PAD;
82 p[EOF] = UU_END;
83 }
84 for (m = (unsigned char*)dp->map; c = *m; m++)
85 p[c] = m - (unsigned char*)dp->map;
86 return (unsigned char*)p;
87 }
88
89 /*
90 * grab uu header from input
91 */
92
93 static int
uu_header(register Uu_t * uu)94 uu_header(register Uu_t* uu)
95 {
96 register char* s;
97 register int c;
98 int n;
99 int k;
100 Uumeth_t* meth;
101 char* t;
102
103 c = *UU_BEGIN;
104 for (;;)
105 {
106 if (!(s = sfgetr(uu->ip, '\n', 1)))
107 {
108 if (uu->disc->errorf)
109 (*uu->disc->errorf)(uu, uu->disc, 2, "unknown encoding");
110 break;
111 }
112 if (*s == c && !strncasecmp(s, UU_BEGIN, UU_BEGIN_LEN) && (meth = uumeth(s)))
113 {
114 s += UU_BEGIN_LEN + strlen(meth->id);
115 while (*s == '-')
116 {
117 for (t = ++s; isalnum(*s); s++);
118 k = s - t;
119 for (n = 0; n < elementsof(options); n++)
120 if (strlen(options[n].name) == k && !strncasecmp(options[n].name, t, k))
121 {
122 uu->flags |= options[n].flag;
123 break;
124 }
125 }
126 if (*s++ == ' ')
127 {
128 uu->mode = strperm(s, &t, 0) & ~(S_IXUSR|S_IXGRP|S_IXOTH);
129 if (*t++ == ' ')
130 {
131 if (!uu->path)
132 {
133 uu->path = strdup(t);
134 uu->flags |= UU_FREEPATH;
135 }
136 if (meth->name != uu->meth.name)
137 {
138 if (!(((Uudata_t*)uu->meth.data)->flags & UU_DEFAULT) && !streq(meth->id, uu->meth.id) && uu->disc->errorf)
139 (*uu->disc->errorf)(uu, uu->disc, 1, "switching to %s encoding", meth->name);
140 uu->meth = *meth;
141 }
142 return 0;
143 }
144 }
145 }
146 }
147 return -1;
148 }
149
150 /*
151 * uu encode input to output
152 */
153
154 static int
uu_encode(register Uu_t * uu)155 uu_encode(register Uu_t* uu)
156 {
157 register char* e;
158 register char* p;
159 register unsigned char* m;
160 register int c;
161 register int c1;
162 register int c2;
163 register int c3;
164 register unsigned long b;
165 int length;
166 int nl;
167 int pad;
168 int text;
169 Uudata_t* dp;
170 struct stat st;
171 char buf[UUOUT * (UUCHUNK + 1)];
172
173 dp = (Uudata_t*)uu->meth.data;
174 m = (unsigned char*)dp->map;
175 length = !!(dp->flags & UU_LENGTH);
176 text = !!(uu->flags & UU_TEXT);
177 pad = dp->pad;
178 if (uu->flags & UU_HEADER)
179 {
180 if (fstat(sffileno(uu->ip), &st) || !(c = modex(st.st_mode) & 0777))
181 c = 0644;
182 sfprintf(uu->op, "%s%s%s %03o %s\n", UU_BEGIN, uu->meth.id, text ? "-text" : "", c, uu->path ? uu->path : "-");
183 }
184 if (length)
185 *buf = m[UUIN * UUCHUNK];
186 p = buf + length;
187 e = p + UUOUT * UUCHUNK;
188 nl = 0;
189 for (;;)
190 {
191 do {
192 if (nl)
193 {
194 nl = 0;
195 c1 = '\n';
196 goto get_2;
197 }
198 if ((c1 = sfgetc(uu->ip)) == EOF)
199 {
200 if (length)
201 *buf = m[((p - buf - length) / UUOUT) * UUIN];
202 goto eof;
203 }
204 if (text && c1 == '\n')
205 {
206 c1 = '\r';
207 c2 = '\n';
208 goto get_3;
209 }
210 get_2:
211 if ((c2 = sfgetc(uu->ip)) == EOF)
212 {
213 if (length)
214 *buf = m[((p - buf - length) / UUOUT) * UUIN + 1];
215 c2 = dp->fill;
216 c3 = dp->fill;
217 b = (c1 << 16) | (c2 << 8) | c3;
218 *p++ = m[b >> 18];
219 *p++ = m[(b >> 12) & 077];
220 *p++ = pad ? pad : m[(b >> 6) & 077];
221 *p++ = pad ? pad : m[b & 077];
222 goto eof;
223 }
224 if (text && c2 == '\n')
225 {
226 c2 = '\r';
227 c3 = '\n';
228 goto put_123;
229 }
230 get_3:
231 if ((c3 = sfgetc(uu->ip)) == EOF)
232 {
233 if (length)
234 *buf = m[((p - buf - length) / UUOUT) * UUIN + 2];
235 c3 = dp->fill;
236 b = (c1 << 16) | (c2 << 8) | c3;
237 *p++ = m[b >> 18];
238 *p++ = m[(b >> 12) & 077];
239 *p++ = m[(b >> 6) & 077];
240 *p++ = pad ? pad : m[b & 077];
241 goto eof;
242 }
243 if (text && c3 == '\n')
244 {
245 nl = 1;
246 c3 = '\r';
247 }
248 put_123:
249 b = (c1 << 16) | (c2 << 8) | c3;
250 *p++ = m[b >> 18];
251 *p++ = m[(b >> 12) & 077];
252 *p++ = m[(b >> 6) & 077];
253 *p++ = m[b & 077];
254 } while (p < e);
255 *p++ = '\n';
256 sfwrite(uu->op, buf, p - buf);
257 p = buf + length;
258 }
259 eof:
260 if (p > buf + length)
261 {
262 *p++ = '\n';
263 sfwrite(uu->op, buf, p - buf);
264 }
265 if (length)
266 sfprintf(uu->op, "%c\n", m[0]);
267 if (uu->flags & UU_HEADER)
268 sfputr(uu->op, dp->end, '\n');
269 return 0;
270 }
271
272 /*
273 * uu decode input to output
274 */
275
276 static int
uu_decode(register Uu_t * uu)277 uu_decode(register Uu_t* uu)
278 {
279 register Uudata_t* dp;
280 register char* s;
281 register char* e;
282 register char* p;
283 register unsigned char* m;
284 register int c;
285 register unsigned long n;
286 int text;
287 int tl;
288 int x;
289 char* t;
290 char buf[UUIN * UUCHUNK + 1];
291 char map[UCHAR_MAX + 2];
292
293 dp = (Uudata_t*)uu->meth.data;
294 if (uu->path && (uu->flags & UU_CLOSEOUT) && (dp->flags & uu->flags & UU_HEADER) && chmod(uu->path, uu->mode) && uu->disc->errorf)
295 (*uu->disc->errorf)(uu, uu->disc, ERROR_SYSTEM|2, "%s: cannot change mode to %s", uu->path, fmtperm(uu->mode));
296 text = !!(uu->flags & UU_TEXT);
297 m = uu_map(dp, map);
298 if (dp->flags & UU_LENGTH)
299 {
300 t = (char*)dp->end;
301 tl = strlen(t) + 1;
302 while (((s = sfgetr(uu->ip, '\n', 0)) || (s = sfgetr(uu->ip, '\n', -1))) && ((n = sfvalue(uu->ip)) != tl || !strneq(s, t, tl - 1)))
303 if (c = m[*((unsigned char*)s++)])
304 {
305 if (c > sizeof(buf))
306 {
307 if (uu->disc->errorf)
308 (*uu->disc->errorf)(uu, uu->disc, 2, "input is not %s encoded", uu->meth.name);
309 return -1;
310 }
311 p = buf;
312 e = s + (c + UUIN - 1) / UUIN * UUOUT;
313 while (s < e)
314 {
315 n = m[*((unsigned char*)s++)];
316 n = (n << 6) | ((s < e) ? m[*((unsigned char*)s++)] : 0);
317 n = (n << 6) | ((s < e) ? m[*((unsigned char*)s++)] : 0);
318 n = (n << 6) | ((s < e) ? m[*((unsigned char*)s++)] : 0);
319 if (text)
320 {
321 if ((x = (n >> 16) & 0xFF) == '\r')
322 c--;
323 else
324 *p++ = x;
325 if ((x = (n >> 8) & 0xFF) == '\r')
326 c--;
327 else
328 *p++ = x;
329 if ((x = n & 0xFF) == '\r')
330 c--;
331 else
332 *p++ = x;
333 }
334 else
335 {
336 *p++ = (n >> 16);
337 *p++ = (n >> 8);
338 *p++ = n;
339 }
340 }
341 sfwrite(uu->op, buf, c);
342 }
343 if (!s && (uu->flags & UU_HEADER) && uu->disc->errorf)
344 (*uu->disc->errorf)(uu, uu->disc, 1, "end sequence `%s' omitted", t);
345 }
346 else
347 {
348 for (;;)
349 {
350 while ((c = m[sfgetc(uu->ip)]) >= 64)
351 if (c != UU_IGN)
352 goto pad;
353 n = c;
354 while ((c = m[sfgetc(uu->ip)]) >= 64)
355 if (c != UU_IGN)
356 {
357 if (uu->disc->errorf)
358 (*uu->disc->errorf)(uu, uu->disc, 1, "%c: extra input character ignored", c);
359 goto pad;
360 }
361 n = (n << 6) | c;
362 while ((c = m[sfgetc(uu->ip)]) >= 64)
363 if (c != UU_IGN)
364 {
365 if (text)
366 {
367 if ((x = (n >> 4) & 0xFF) != '\r')
368 sfputc(uu->op, x);
369 }
370 else sfputc(uu->op, n >> 4);
371 goto pad;
372 }
373 n = (n << 6) | c;
374 while ((c = m[sfgetc(uu->ip)]) >= 64)
375 if (c != UU_IGN)
376 {
377 if (text)
378 {
379 if ((x = (n >> 10) & 0xFF) != '\r')
380 sfputc(uu->op, x);
381 if ((x = (n >> 2) & 0xFF) != '\r')
382 sfputc(uu->op, x);
383 }
384 else
385 {
386 sfputc(uu->op, n >> 10);
387 sfputc(uu->op, n >> 2);
388 }
389 goto pad;
390 }
391 n = (n << 6) | c;
392 if (text)
393 {
394 if ((x = (n >> 16) & 0xFF) != '\r')
395 sfputc(uu->op, x);
396 if ((x = (n >> 8) & 0xFF) != '\r')
397 sfputc(uu->op, x);
398 if ((x = n & 0xFF) != '\r')
399 sfputc(uu->op, x);
400 }
401 else
402 {
403 sfputc(uu->op, (n >> 16));
404 sfputc(uu->op, (n >> 8));
405 sfputc(uu->op, (n));
406 }
407 }
408 pad:
409 n = c == UU_PAD;
410 while ((c = sfgetc(uu->ip)) != EOF)
411 if (c == dp->pad && ++n >= 4)
412 break;
413 if (n < 4 && (uu->flags & UU_HEADER) && uu->disc->errorf)
414 (*uu->disc->errorf)(uu, uu->disc, 1, "input end sequence `%s' omitted", dp->end);
415 }
416 return 0;
417 }
418
419 static const char hex[] = "0123456789ABCDEFabcdef";
420
421 /*
422 * quoted-printable encode input to output
423 */
424
425 static int
qp_encode(register Uu_t * uu)426 qp_encode(register Uu_t* uu)
427 {
428 register unsigned char* s;
429 register unsigned char* e;
430 register char* b;
431 register char* x;
432 register int c;
433 char buf[UULINE + 1];
434
435 b = buf;
436 x = b + UULINE - 4;
437 while ((s = (unsigned char*)sfgetr(uu->ip, '\n', 0)) || (s = (unsigned char*)sfgetr(uu->ip, '\n', -1)))
438 {
439 e = s + sfvalue(uu->ip);
440 switch (*s)
441 {
442 case 'F':
443 if ((e - s) >= 5 && strneq((char*)s, "From ", 5))
444 {
445 c = *s++;
446 goto quote;
447 }
448 break;
449 case '.':
450 if ((e - s) == 2)
451 {
452 c = *s++;
453 goto quote;
454 }
455 break;
456 }
457 while (s < e)
458 {
459 if ((c = *s++) == '\n')
460 {
461 *b++ = c;
462 sfwrite(uu->op, buf, b - buf);
463 b = buf;
464 break;
465 }
466 if (b >= x)
467 {
468 *b++ = '=';
469 *b++ = '\n';
470 sfwrite(uu->op, buf, b - buf);
471 b = buf;
472 }
473 if (c == ' ' || c == '\t')
474 {
475 if (s < e && *s != '\n')
476 {
477 *b++ = c;
478 continue;
479 }
480 }
481 else if (isprint(c) && !iscntrl(c) && c != '=')
482 {
483 *b++ = c;
484 continue;
485 }
486 quote:
487 *b++ = '=';
488 *b++ = hex[(c >> 4) & 0xF];
489 *b++ = hex[c & 0xF];
490 }
491 }
492 if (b > buf)
493 {
494 *b++ = '=';
495 *b++ = '\n';
496 sfwrite(uu->op, buf, b - buf);
497 }
498 return 0;
499 }
500
501 /*
502 * quoted-printable decode input to output
503 */
504
505 static int
qp_decode(register Uu_t * uu)506 qp_decode(register Uu_t* uu)
507 {
508 register unsigned char* s;
509 register unsigned char* b;
510 register unsigned char* x;
511 register int c;
512 register int d;
513
514 short xeh[UCHAR_MAX + 1];
515
516 for (c = 0; c < elementsof(xeh); c++)
517 xeh[c] = -1;
518 for (c = 0; c < elementsof(hex) - 1; c++)
519 xeh[hex[c]] = c >= 16 ? (c - 6) : c;
520 while (s = (unsigned char*)sfgetr(uu->ip, '\n', 1))
521 {
522 if (((b = s + sfvalue(uu->ip)) > s) && !*--b)
523 {
524 while (b > s && ((c = *(b - 1)) == ' ' || c == '\t'))
525 b--;
526 *b = 0;
527 }
528 x = b = s;
529 for (;;)
530 {
531 switch (c = *s++)
532 {
533 case 0:
534 *b++ = '\n';
535 break;
536 case '=':
537 if ((c = xeh[*s++]) < 0 || (d = xeh[*s++]) < 0)
538 break;
539 *b++ = (c << 4) | d;
540 continue;
541 default:
542 *b++ = c;
543 continue;
544 }
545 break;
546 }
547 sfwrite(uu->op, x, b - x);
548 }
549 return 0;
550 }
551
552 /*
553 * binhex based on
554 *
555 * xbin Version 2.3 09/30/85
556 * Dave Johnson, Brown University Computer Science
557 */
558
559 #define BX_REPEAT 0x90
560
561 #define BX_OLD (UU_METHOD<<0)
562
563 typedef struct
564 {
565 int col;
566 int eof;
567 int last;
568 int repeat;
569 off_t size;
570 unsigned long crc;
571 unsigned char* qp;
572 unsigned char* qe;
573 unsigned char qbuf[3];
574 char map[UCHAR_MAX + 2];
575 } Bx_t;
576
577 /*
578 * add c to the binhex Q format crc
579 */
580
581 static int
bx_q_crc(register Bx_t * bx,int c)582 bx_q_crc(register Bx_t* bx, int c)
583 {
584 register int i = 8;
585 register unsigned int k = c;
586 register unsigned long crc = bx->crc;
587
588 while (i--)
589 {
590 k <<= 1;
591 if ((crc <<= 1) & 0x10000)
592 crc = (crc & 0xFFFF) ^ 0x1021;
593 crc ^= k >> 8;
594 k &= 0xFF;
595 }
596 bx->crc = crc;
597 return c;
598 }
599
600 /*
601 * return next binhex Q format char
602 */
603
604 static int
bx_q_getc(register Uu_t * uu,register Bx_t * bx)605 bx_q_getc(register Uu_t* uu, register Bx_t* bx)
606 {
607 register int c;
608 register unsigned char* ip;
609 register unsigned char* ie;
610 register unsigned char* m;
611 int x;
612 unsigned long crc;
613 unsigned char ibuf[4];
614
615 if (bx->repeat > 0)
616 {
617 bx->repeat--;
618 return bx_q_crc(bx, bx->last);
619 }
620 if (bx->qp >= bx->qe)
621 {
622 if (bx->eof)
623 return EOF;
624 bx->qp = bx->qbuf;
625 m = (unsigned char*)bx->map + 1;
626 ie = (ip = ibuf) + sizeof(ibuf);
627 while (ip < ie)
628 {
629 while ((c = m[sfgetc(uu->ip)]) >= 64)
630 if (c != UU_IGN)
631 {
632 bx->eof = 1;
633 bx->qe = bx->qbuf;
634 if ((c = (ip - ibuf) - 1) <= 0)
635 return EOF;
636 bx->qe += c;
637 break;
638 }
639 *ip++ = c;
640 }
641 ip = ibuf;
642 ie = bx->qp;
643 ie[0] = (ip[0] << 2) | (ip[1] >> 4);
644 ie[1] = (ip[1] << 4) | (ip[2] >> 2);
645 ie[2] = (ip[2] << 6) | (ip[3] );
646 }
647 if ((c = *bx->qp++) == BX_REPEAT && !bx->repeat)
648 {
649 c = bx->last;
650 crc = bx->crc;
651 bx->repeat = -1;
652 x = bx_q_getc(uu, bx);
653 bx->crc = crc;
654 switch (x)
655 {
656 case EOF:
657 return EOF;
658 case 0:
659 bx->repeat = 0;
660 c = BX_REPEAT;
661 break;
662 case 1:
663 bx->repeat = 0;
664 break;
665 default:
666 bx->repeat = x - 2;
667 break;
668 }
669 }
670 return bx->last = bx_q_crc(bx, c);
671 }
672
673 /*
674 * return binhex Q format n byte int
675 */
676
677 static long
bx_q_getn(register Uu_t * uu,register Bx_t * bx,register int n)678 bx_q_getn(register Uu_t* uu, register Bx_t* bx, register int n)
679 {
680 register long v = 0;
681
682 while (n--)
683 v = (v << 8) | bx_q_getc(uu, bx);
684 return v;
685 }
686
687 /*
688 * return binhex Q format buffer of size n
689 */
690
691 static ssize_t
bx_q_gets(register Uu_t * uu,register Bx_t * bx,register char * s,size_t n)692 bx_q_gets(register Uu_t* uu, register Bx_t* bx, register char* s, size_t n)
693 {
694 register int c;
695 register char* e;
696
697 e = s + n;
698 while (s < e)
699 {
700 if ((c = bx_q_getc(uu, bx)) == EOF)
701 return -1;
702 *s++ = c;
703 }
704 return n;
705 }
706
707 /*
708 * low level for bx_q_putc()
709 */
710
711 static int
bx_q_put(register Uu_t * uu,register Bx_t * bx,register int c)712 bx_q_put(register Uu_t* uu, register Bx_t* bx, register int c)
713 {
714 register unsigned char* p;
715 register unsigned char* m;
716
717 *bx->qp++ = c;
718 if (bx->qp >= bx->qe)
719 {
720 m = (unsigned char*)((Uudata_t*)uu->meth.data)->map;
721 p = bx->qp = bx->qbuf;
722 c = (p[0] << 16) | (p[1] << 8) | p[2];
723 sfputc(uu->op, m[(c >> 18) & 0x3f]);
724 sfputc(uu->op, m[(c >> 12) & 0x3f]);
725 sfputc(uu->op, m[(c >> 6) & 0x3f]);
726 sfputc(uu->op, m[(c ) & 0x3f]);
727 if ((bx->col += 4) >= 63)
728 {
729 bx->col = 0;
730 sfputc(uu->op, '\n');
731 }
732 }
733 return 0;
734 }
735
736 /*
737 * output binhex Q format char
738 */
739
740 static int
bx_q_putc(register Uu_t * uu,register Bx_t * bx,register int c)741 bx_q_putc(register Uu_t* uu, register Bx_t* bx, register int c)
742 {
743 if (c == bx->last)
744 {
745 if (!bx->repeat++)
746 {
747 bx_q_put(uu, bx, c);
748 bx_q_put(uu, bx, BX_REPEAT);
749 }
750 bx_q_crc(bx, c);
751 if (bx->repeat >= 0xFF)
752 {
753 bx_q_put(uu, bx, bx->repeat);
754 bx->repeat = 0;
755 bx->last = -1;
756 }
757 }
758 else
759 {
760 if (bx->repeat)
761 {
762 bx_q_put(uu, bx, bx->repeat);
763 bx->repeat = 0;
764 bx->last = -1;
765 }
766 if (c == BX_REPEAT)
767 {
768 bx_q_put(uu, bx, c);
769 bx_q_put(uu, bx, 0);
770 bx->last = -1;
771 }
772 else if (c == '\n' && (uu->flags & UU_TEXT))
773 {
774 bx_q_put(uu, bx, '\r');
775 bx_q_crc(bx, '\r');
776 bx_q_put(uu, bx, '\n');
777 bx->last = -1;
778 }
779 else
780 {
781 bx_q_put(uu, bx, c);
782 bx->last = c;
783 }
784 bx_q_crc(bx, c);
785 }
786 return 0;
787 }
788
789 /*
790 * output binhex Q format n byte int
791 */
792
793 static int
bx_q_putn(register Uu_t * uu,register Bx_t * bx,register unsigned long v,int n)794 bx_q_putn(register Uu_t* uu, register Bx_t* bx, register unsigned long v, int n)
795 {
796 switch (n)
797 {
798 case 4: bx_q_putc(uu, bx, (v >> 24) & 0xFF);
799 case 3: bx_q_putc(uu, bx, (v >> 16) & 0xFF);
800 case 2: bx_q_putc(uu, bx, (v >> 8) & 0xFF);
801 case 1: bx_q_putc(uu, bx, (v >> 0) & 0xFF);
802 }
803 return 0;
804 }
805
806 /*
807 * grab binhex header from input
808 */
809
810 static int
bx_header(register Uu_t * uu)811 bx_header(register Uu_t* uu)
812 {
813 register Bx_t* bx = (Bx_t*)(uu + 1);
814 Uudata_t* dp = (Uudata_t*)uu->meth.data;
815 register int c;
816 register int bol;
817 register char* s;
818 register char* m;
819 unsigned long crc;
820 unsigned long crx;
821 char buf[UCHAR_MAX + 2];
822
823 if (uu->flags & UU_HEADER)
824 do
825 {
826 if (!(s = sfgetr(uu->ip, '\n', 0)))
827 {
828 if (uu->disc->errorf)
829 (*uu->disc->errorf)(uu, uu->disc, 2, "unknown encoding");
830 return -1;
831 }
832 } while (*s != '(' || strncmp(s, "(This file", 10));
833 bol = 1;
834 for (;;)
835 {
836 switch (c = sfgetc(uu->ip))
837 {
838 case EOF:
839 return -1;
840 case '\n':
841 case '\r':
842 bol = 1;
843 break;
844 case ':':
845 if (bol)
846 {
847 /*
848 * Q format
849 *
850 * 1 n name length
851 * n s name
852 * 4 s type
853 * 4 s author
854 * 2 n flags
855 * 4 n data size
856 * 4 n resource size
857 * 2 n header checksum
858 */
859
860 memset(s = bx->map, UU_END, sizeof(bx->map));
861 for (s++, m = (char*)dp->map; c = *m; m++)
862 s[c] = m - (char*)dp->map;
863 s['\n'] = UU_IGN;
864 s['\r'] = UU_IGN;
865 bx->qp = bx->qe = bx->qbuf + sizeof(bx->qbuf);
866 if ((c = bx_q_getc(uu, bx)) == EOF)
867 return -1;
868 if (bx_q_gets(uu, bx, buf, c + 1) < 0)
869 return -1;
870 if (!uu->path)
871 {
872 uu->path = strdup(buf);
873 uu->flags |= UU_FREEPATH;
874 }
875 if (bx_q_gets(uu, bx, buf, 4) < 0)
876 return -1;
877 if (bx_q_gets(uu, bx, buf, 4) < 0)
878 return -1;
879 bx_q_getn(uu, bx, 2);
880 bx->size = bx_q_getn(uu, bx, 4);
881 bx_q_getn(uu, bx, 4);
882 bx_q_crc(bx, 0);
883 bx_q_crc(bx, 0);
884 crc = bx->crc;
885 crx = bx_q_getn(uu, bx, 2);
886 if (crc != crx)
887 {
888 if (uu->disc->errorf)
889 (*uu->disc->errorf)(uu, uu->disc, 2, "%s format header checksum mismatch", uu->meth.name);
890 return -1;
891 }
892 return 0;
893 }
894 break;
895 case '#':
896 if (bol)
897 {
898 /*
899 * old format
900 */
901
902 sfungetc(uu->ip, c);
903 uu->flags |= BX_OLD;
904
905 /*
906 * #<TYPE><AUTH>$<flag>
907 */
908
909 if (!sfgetr(uu->ip, '\n', 0))
910 return -1;
911 return 0;
912 }
913 break;
914 default:
915 bol = 0;
916 break;
917 }
918 }
919 }
920
921 /*
922 * old binhex line decode
923 */
924
925 static int
bx_o_decode(register Uu_t * uu,Bx_t * bx,char * buf,register size_t n)926 bx_o_decode(register Uu_t* uu, Bx_t* bx, char* buf, register size_t n)
927 {
928 register int c;
929 register int d;
930 register unsigned long crc = bx->crc;
931 register unsigned char* m = (unsigned char*)bx->map;
932 register char* t = (char*)hex;
933 register unsigned char* s = (unsigned char*)buf;
934
935 memset(m, UU_END, sizeof(bx->map));
936 for (c = 0; c < elementsof(hex); c++)
937 m[t[c]] = c;
938 n <= 2;
939 while (n--)
940 {
941 if ((c = m[*s++]) == UU_END || (d = m[*s++]) == UU_END)
942 return -1;
943 crc += c = (c << 4) | d;
944 sfputc(uu->op, c);
945 }
946 bx->crc = crc;
947 return 0;
948 }
949
950 /*
951 * old binhex compressed line decode
952 */
953
954 #define BX_O_MASK(c) (((c)-0x20)&0x3F)
955 #define BX_O_CRC(s,c) ((s=(s+c)&0xFF),(s=((s<<3)&0xFF)|(s>>13)))
956
957 static int
bx_c_decode(register Uu_t * uu,Bx_t * bx,register char * s,size_t n)958 bx_c_decode(register Uu_t* uu, Bx_t* bx, register char* s, size_t n)
959 {
960 register int c;
961 register int oc;
962 register unsigned long crc;
963 char* e;
964 int ic;
965 char buf[SF_BUFSIZE];
966
967 crc = bx->crc;
968 oc = (BX_O_MASK(s[0]) << 2) | (BX_O_MASK(s[1]) >> 4);
969 ic = ((oc / 3) + 1) * 4;
970 if (ic > SF_BUFSIZE)
971 ic = SF_BUFSIZE;
972 if (n > SF_BUFSIZE)
973 n = SF_BUFSIZE;
974 memcpy(buf, s, n);
975 s = buf + n;
976 e = buf + ic;
977 while (s < e)
978 *s++ = ' ';
979 s = buf;
980 while ((oc -= 3) >= 0)
981 {
982 c = (BX_O_MASK(s[0]) << 2) | (BX_O_MASK(s[1]) >> 4);
983 BX_O_CRC(crc, c);
984 sfputc(uu->op, c);
985 c = (BX_O_MASK(s[1]) << 4) | (BX_O_MASK(s[2]) >> 2);
986 BX_O_CRC(crc, c);
987 sfputc(uu->op, c);
988 c = (BX_O_MASK(s[2]) << 6) | (BX_O_MASK(s[3]) );
989 BX_O_CRC(crc, c);
990 sfputc(uu->op, c);
991 s += 4;
992 }
993 bx->crc = crc;
994 return 0;
995 }
996
997 /*
998 * binhex decode input to output
999 */
1000
1001 static int
bx_decode(register Uu_t * uu)1002 bx_decode(register Uu_t* uu)
1003 {
1004 register Bx_t* bx = (Bx_t*)(uu + 1);
1005 register off_t n;
1006 register int c;
1007 register char* s;
1008 unsigned long crc;
1009 int (*decode)(Uu_t*, Bx_t*, char*, size_t);
1010
1011 crc = 0x10000;
1012 bx->crc = 0;
1013 if (uu->flags & BX_OLD)
1014 {
1015 decode = bx_o_decode;
1016 c = 0;
1017 while (s = sfgetr(uu->ip, '\n', 0))
1018 if (*s++ == '*' && *s++ == '*' && *s++ == '*')
1019 {
1020 switch (*s)
1021 {
1022 case 'C':
1023 switch (s[1])
1024 {
1025 case 'O':
1026 if (c || strncmp(s, "COMPRESSED", 10))
1027 continue;
1028 decode = bx_c_decode;
1029 continue;
1030 case 'H':
1031 if (decode == bx_c_decode || strncmp(s, "CHECKSUM:", 9))
1032 continue;
1033 crc = bx->crc & 0xFF;
1034 bx->crc = strtoul(s + 9, NiL, 16) & 0xFF;
1035 break;
1036 case 'R':
1037 if (decode == bx_o_decode || strncmp(s, "CRC:", 4))
1038 continue;
1039 crc = bx->crc & 0xFFFF;
1040 bx->crc = strtoul(s + 4, NiL, 16) & 0xFFFF;
1041 break;
1042 default:
1043 continue;
1044 }
1045 break;
1046 case 'D':
1047 if (strncmp(s, "DATA", 4))
1048 continue;
1049 while (s = sfgetr(uu->ip, '\n', 0))
1050 {
1051 if (strneq(s, "***END", 6))
1052 break;
1053 if ((*decode)(uu, bx, s, sfvalue(uu->ip) - 1) < 0)
1054 return -1;
1055 }
1056 break;
1057 case 'R':
1058 if (c || strncmp(s, "RESOURCE", 8))
1059 continue;
1060 c = 1;
1061 continue;
1062 default:
1063 continue;
1064 }
1065 break;
1066 }
1067 }
1068 else
1069 {
1070 if ((n = bx->size) > 0)
1071 while (n--)
1072 {
1073 if ((c = bx_q_getc(uu, bx)) == EOF)
1074 return -1;
1075 sfputc(uu->op, c);
1076 }
1077
1078 /*
1079 * check the header crc
1080 */
1081
1082 bx_q_crc(bx, 0);
1083 bx_q_crc(bx, 0);
1084 crc = bx->crc;
1085 bx->crc = bx_q_getn(uu, bx, 2);
1086 }
1087 if (crc != bx->crc)
1088 {
1089 if (uu->disc->errorf)
1090 {
1091 if (crc == 0x10000)
1092 (*uu->disc->errorf)(uu, uu->disc, 2, "%s format checksum missing", uu->meth.name);
1093 else
1094 (*uu->disc->errorf)(uu, uu->disc, 2, "%s format data checksum mismatch", uu->meth.name);
1095 }
1096 return -1;
1097 }
1098 return 0;
1099 }
1100
1101 /*
1102 * binhex encode input to output
1103 */
1104
1105 static int
bx_encode(register Uu_t * uu)1106 bx_encode(register Uu_t* uu)
1107 {
1108 register Bx_t* bx = (Bx_t*)(uu + 1);
1109 register unsigned char* m;
1110 register int c;
1111 register int i;
1112 struct stat st;
1113
1114 bx->last = -1;
1115 bx->qp = bx->qbuf;
1116 bx->qe = bx->qbuf + sizeof(bx->qbuf);
1117 if (fstat(sffileno(uu->ip), &st))
1118 st.st_size = 0;
1119 sfprintf(uu->op, "(This file must be converted with BinHex 4.0)\n:");
1120 if (!(m = (unsigned char*)uu->path))
1121 m = (unsigned char*)"-";
1122 if ((c = strlen((char*)m)) > 63)
1123 c = 63;
1124 bx_q_putc(uu, bx, c);
1125 for (i = 0; i < c; i++)
1126 bx_q_putc(uu, bx, m[i]);
1127 bx_q_putc(uu, bx, 0);
1128 bx_q_putn(uu, bx, 0, 4);
1129 bx_q_putn(uu, bx, 0, 4);
1130 bx_q_putn(uu, bx, 0xF800, 2);
1131 bx_q_putn(uu, bx, st.st_size, 4);
1132 bx_q_putn(uu, bx, 0, 4);
1133 bx_q_crc(bx, 0);
1134 bx_q_crc(bx, 0);
1135 bx_q_putn(uu, bx, bx->crc, 2);
1136 bx->crc = 0;
1137 while ((c = sfgetc(uu->ip)) != EOF)
1138 bx_q_putc(uu, bx, c);
1139 bx_q_crc(bx, 0);
1140 bx_q_crc(bx, 0);
1141 bx_q_putn(uu, bx, bx->crc, 2);
1142 bx->crc = 0;
1143 bx_q_crc(bx, 0);
1144 bx_q_crc(bx, 0);
1145 bx_q_putn(uu, bx, bx->crc, 2);
1146 while (bx->qp != bx->qbuf)
1147 bx_q_putc(uu, bx, 0);
1148 sfputc(uu->op, ':');
1149 sfputc(uu->op, '\n');
1150 return 0;
1151 }
1152
1153 /*
1154 * cat input to output
1155 */
1156
1157 static int
cat(register Uu_t * uu)1158 cat(register Uu_t* uu)
1159 {
1160 return sfmove(uu->ip, uu->op, SF_UNBOUND, -1) >= 0 && sfeof(uu->ip) ? 0 : -1;
1161 }
1162
1163 static Uudata_t uu_posix =
1164 {
1165 "end",
1166 0,
1167 0,
1168 UU_HEADER|UU_LENGTH,
1169 0,
1170 " !\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
1171 };
1172
1173 static const Uudata_t uu_ucb =
1174 {
1175 "end",
1176 0,
1177 0156,
1178 UU_HEADER|UU_LENGTH,
1179 0,
1180 "`!\"#$%&'()*+,-./0123456789:;<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[\\]^_"
1181 };
1182
1183 static const Uudata_t uu_base64 =
1184 {
1185 "====",
1186 '=',
1187 0,
1188 0,
1189 0,
1190 "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
1191 };
1192
1193 static const Uudata_t uu_bx =
1194 {
1195 0,
1196 0,
1197 0,
1198 UU_HEADER|UU_HEADERMUST,
1199 sizeof(Bx_t),
1200 "!\"#$%&'()*+,-012345689@ABCDEFGHIJKLMNPQRSTUVXYZ[`abcdefhijklmpqr"
1201 };
1202
1203 static const Uumeth_t methods[] =
1204 {
1205
1206 {
1207 "posix", "uuencode", "",
1208 uu_header, uu_encode, uu_decode, (void*)&uu_posix
1209 },
1210 {
1211 "ucb", "bsd", "",
1212 uu_header, uu_encode, uu_decode, (void*)&uu_ucb
1213 },
1214 {
1215 "mime", "base64", "-base64",
1216 0, uu_encode, uu_decode, (void*)&uu_base64
1217 },
1218 {
1219 "quoted-printable", "qp", "",
1220 0, qp_encode, qp_decode, 0
1221 },
1222 {
1223 "binhex", "mac-binhex", "",
1224 bx_header, bx_encode, bx_decode, (void*)&uu_bx
1225 },
1226 {
1227 "sevenbit", "7bit", "",
1228 0, cat, cat, 0
1229 },
1230
1231 { 0 }
1232
1233 };
1234
1235 /*
1236 * list the method names/alternates on fp
1237 */
1238
1239 int
uulist(Sfio_t * fp)1240 uulist(Sfio_t* fp)
1241 {
1242 register const Uumeth_t* mp;
1243
1244 sfprintf(fp, "ENCODING ALIAS\n");
1245 for (mp = methods; mp->name; mp++)
1246 sfprintf(fp, "%-17s %s\n", mp->name, mp->alias);
1247 return 0;
1248 }
1249
1250 /*
1251 * return method pointer given name
1252 */
1253
1254 Uumeth_t*
uumeth(const char * name)1255 uumeth(const char* name)
1256 {
1257 register const Uumeth_t* mp;
1258 register int c;
1259 register const char* v;
1260 register int vl;
1261 const char* np;
1262
1263 /*
1264 * first entry is the default
1265 */
1266
1267 if (!name || !*name)
1268 {
1269 ((Uudata_t*)methods->data)->flags |= UU_DEFAULT;
1270 return (Uumeth_t*)methods;
1271 }
1272 if ((*name == 'x' || *name == 'X') && *(name + 1) == '-')
1273 name += 2;
1274 if (*name == 'b' && !strncasecmp(name, UU_BEGIN, UU_BEGIN_LEN))
1275 {
1276 /*
1277 * id prefix match
1278 */
1279
1280 if ((c = *(name += UU_BEGIN_LEN)) == ' ')
1281 return (Uumeth_t*)methods;
1282 for (mp = methods; mp->name; mp++)
1283 if (*mp->id == c && !strncasecmp(name, mp->id, strlen(mp->id)))
1284 return (Uumeth_t*)mp;
1285 if (c == '-')
1286 return (Uumeth_t*)methods;
1287 }
1288 else
1289 {
1290 c = *name;
1291 for (v = name + strlen(name); v > name && (isdigit(*(v - 1)) || *(v - 1) == '.'); v--);
1292 vl = *v ? (v - name) : 0;
1293
1294 /*
1295 * exact name or alias match
1296 */
1297
1298 for (;;)
1299 {
1300 for (mp = methods; mp->name; mp++)
1301 if (*mp->name == c && (!strcasecmp(name, mp->name) || vl && !strncasecmp(name, mp->name, vl)) ||
1302 mp->alias && *mp->alias == c && (!strcasecmp(name, mp->alias) || vl && !strncasecmp(name, mp->alias, vl)))
1303 return (Uumeth_t*)mp;
1304 np = name;
1305 if (!(name = strchr(name, '/')))
1306 break;
1307 if (((c = *++name) == 'x' || *name == 'X') && *(name + 1) == '-')
1308 c = *(name += 2);
1309 if (vl)
1310 vl -= (name - np);
1311 }
1312
1313 /*
1314 * first char name match
1315 */
1316
1317 for (mp = methods; mp->name; mp++)
1318 if (*mp->name == c)
1319 return (Uumeth_t*)mp;
1320 }
1321 return 0;
1322 }
1323
1324 /*
1325 * open an encode/decode handle
1326 */
1327
1328 Uu_t*
uuopen(Uudisc_t * disc,Uumeth_t * meth)1329 uuopen(Uudisc_t* disc, Uumeth_t* meth)
1330 {
1331 register Uu_t* uu;
1332 register Uudata_t* data;
1333 int extra;
1334
1335 if (data = (Uudata_t*)meth->data)
1336 extra = data->size;
1337 else
1338 extra = 0;
1339 if (!(uu = newof(0, Uu_t, 1, extra)))
1340 return 0;
1341 uu->id = lib;
1342 uu->disc = disc;
1343 uu->meth = *meth;
1344 return uu;
1345 }
1346
1347 /*
1348 * close an encode/decode handle
1349 */
1350
1351 int
uuclose(Uu_t * uu)1352 uuclose(Uu_t* uu)
1353 {
1354 if (!uu)
1355 return -1;
1356 if ((uu->flags & UU_FREEPATH) && uu->path)
1357 free(uu->path);
1358 free(uu);
1359 return 0;
1360 }
1361
1362 /*
1363 * common encode/decode tail
1364 */
1365
1366 static ssize_t
uuop(register Uu_t * uu,Uu_f fun)1367 uuop(register Uu_t* uu, Uu_f fun)
1368 {
1369 int n;
1370 ssize_t r;
1371 Sfoff_t p;
1372
1373 if (uu->count != SF_UNBOUND && ((p = sfseek(uu->ip, (Sfoff_t)0, SEEK_CUR)) < 0 || !(uu->ip = sfdcsubstream(NiL, uu->lp = uu->ip, p, uu->count))))
1374 {
1375 if (uu->disc->errorf)
1376 (*uu->disc->errorf)(uu, uu->disc, 2, "cannot initialize substream at %I*d for %I*d bytes", sizeof(p), p, sizeof(uu->count), uu->count);
1377 return -1;
1378 }
1379 p = sfseek(uu->op, (Sfoff_t)0, SEEK_CUR);
1380 n = (*fun)(uu);
1381 if (uu->lp)
1382 {
1383 sfclose(uu->ip);
1384 uu->ip = uu->lp;
1385 uu->lp = 0;
1386 }
1387 if (n < 0)
1388 r = -1;
1389 else if (sfsync(uu->op) || sferror(uu->op))
1390 {
1391 r = -1;
1392 if (uu->disc->errorf)
1393 (*uu->disc->errorf)(uu, uu->disc, 2, "write error");
1394 }
1395 else if (sferror(uu->ip))
1396 {
1397 r = -1;
1398 if (uu->disc->errorf)
1399 (*uu->disc->errorf)(uu, uu->disc, 2, "read error");
1400 }
1401 else
1402 r = sfseek(uu->op, (Sfoff_t)0, SEEK_CUR) - p;
1403 if (uu->flags & UU_CLOSEOUT)
1404 {
1405 uu->flags &= ~UU_CLOSEOUT;
1406 sfclose(uu->op);
1407 }
1408 return r;
1409 }
1410
1411 /*
1412 * encode n bytes (or all if SF_UNBOUND) from ip to op
1413 */
1414
1415 ssize_t
uuencode(register Uu_t * uu,Sfio_t * ip,Sfio_t * op,size_t n,const char * path)1416 uuencode(register Uu_t* uu, Sfio_t* ip, Sfio_t* op, size_t n, const char* path)
1417 {
1418 if (!uu->meth.encodef)
1419 {
1420 if (uu->disc->errorf)
1421 (*uu->disc->errorf)(uu, uu->disc, 2, "%s format encoding not supported", uu->meth.name);
1422 return -1;
1423 }
1424 if (!(uu->ip = ip) || !(uu->op = op))
1425 return -1;
1426 uu->count = n;
1427 if ((uu->flags & UU_FREEPATH) && uu->path)
1428 free(uu->path);
1429 uu->path = (char*)path;
1430 uu->flags = uu->disc->flags;
1431 return uuop(uu, uu->meth.encodef);
1432 }
1433
1434 /*
1435 * decode n bytes (or all if SF_UNBOUND) from ip to op
1436 */
1437
1438 ssize_t
uudecode(register Uu_t * uu,Sfio_t * ip,Sfio_t * op,size_t n,const char * path)1439 uudecode(register Uu_t* uu, Sfio_t* ip, Sfio_t* op, size_t n, const char* path)
1440 {
1441 register char* s;
1442 unsigned char* m;
1443 int c;
1444 int headerpath;
1445 Uudata_t* data;
1446 const Uumeth_t* mp;
1447 char map[UCHAR_MAX + 2];
1448
1449 if (!uu->meth.decodef)
1450 {
1451 if (uu->disc->errorf)
1452 (*uu->disc->errorf)(uu, uu->disc, 2, "%s format decoding not supported", uu->meth.name);
1453 return -1;
1454 }
1455 if (!(uu->ip = ip))
1456 return -1;
1457 uu->op = op;
1458 uu->count = n;
1459 if ((uu->flags & UU_FREEPATH) && uu->path)
1460 free(uu->path);
1461 uu->path = (char*)path;
1462 uu->flags = uu->disc->flags;
1463 data = (Uudata_t*)uu->meth.data;
1464 if (((uu->flags & UU_HEADER) || data && (data->flags & UU_HEADERMUST)) && uu->meth.headerf)
1465 {
1466 if ((*uu->meth.headerf)(uu))
1467 return -1;
1468 headerpath = 1;
1469 }
1470 else
1471 {
1472 headerpath = 0;
1473 if (data && (data->flags & UU_DEFAULT) && (c = sfgetc(uu->ip)) != EOF)
1474 {
1475 sfungetc(uu->ip, c);
1476 for (mp = methods; ((Uudata_t*)mp->data)->flags & UU_LENGTH; mp++)
1477 {
1478 m = uu_map((Uudata_t*)mp->data, map);
1479 if (m[c] > 0 && m[c] < (UUIN * UUCHUNK + 1))
1480 break;
1481 }
1482 if (mp > methods)
1483 {
1484 uu->meth = *mp;
1485 if (uu->disc->errorf)
1486 (*uu->disc->errorf)(uu, uu->disc, 1, "assuming %s encoding", mp->name);
1487 }
1488 }
1489 }
1490 if (!uu->op)
1491 {
1492 if (!uu->path && headerpath)
1493 {
1494 if (uu->disc->errorf)
1495 (*uu->disc->errorf)(uu, uu->disc, 2, "%s format header has no output file name", uu->meth.name);
1496 return -1;
1497 }
1498 if (!uu->path || !*uu->path || streq(uu->path, "-") || streq(uu->path, "/dev/stdout"))
1499 uu->op = sfstdout;
1500 else
1501 {
1502 if (headerpath && (uu->flags & UU_LOCAL))
1503 {
1504 for (s = uu->path; *s; s++)
1505 if (isspace(*s) || iscntrl(*s) || !isprint(*s) || *s == '/' || *s == '\\')
1506 *s = '_';
1507 s = uu->path;
1508 if (isalpha(s[0]) && s[1] == ':')
1509 s[1] = '_';
1510 }
1511 if (!(uu->op = sfopen(NiL, uu->path, "w")))
1512 {
1513 if (uu->disc->errorf)
1514 (*uu->disc->errorf)(uu, uu->disc, 2, "%s: cannot write", uu->path);
1515 return -1;
1516 }
1517 uu->flags |= UU_CLOSEOUT;
1518 }
1519 }
1520 return uuop(uu, uu->meth.decodef);
1521 }
1522