source: trunk/minix/commands/simple/uud.c@ 15

Last change on this file since 15 was 9, checked in by Mattia Monga, 14 years ago

Minix 3.1.2a

File size: 11.6 KB
RevLine 
[9]1/* uud - bulletproof version of uudecode */
2
3/*
4 * Uud -- decode a uuencoded file back to binary form.
5 *
6 * From the Berkeley original, modified by MSD, RDR, JPHD & WLS.
7 * The Atari GEMDOS version compiled with MWC 2.x.
8 * The MSDOS version with TurboC.
9 * The Unix version with cc.
10 * this version is made: 25 Nov 1988.
11 * Jan 2 1990: Change system definition and change MSDOS to open the output
12 * file for write binary do cr/lf replacement.
13 */
14
15#define UNIX 1 /* define one of: UNIX (Minix too!), MSDOS, or GEMDOS */
16
17#ifdef GEMDOS
18#define SYSNAME "gemdos"
19#define SMALL 1
20#endif
21#ifdef MSDOS
22#define SYSNAME "msdos"
23#define SMALL 1
24#endif
25#ifdef UNIX
26#define SYSNAME "unix"
27#endif
28
29#include <sys/types.h>
30#include <stdarg.h>
31#include <string.h>
32#include <stdlib.h>
33#include <unistd.h>
34#include <sys/stat.h>
35#include <stdio.h>
36
37#ifdef GEMDOS
38#include <osbind.h>
39#define Error(n) { Bconin(2); exit(n); }
40#else
41#define Error(n) exit(n)
42#endif
43#ifdef UNIX
44#define WRITE "w"
45#else
46#define WRITE "wb" /* for both MSDOS and GEMDOS! */
47#endif
48
49#define loop while (1)
50
51#define NCHARS 256
52#define LINELEN 256
53#define FILELEN 64
54#define NORMLEN 60 /* allows for 80 encoded chars per line */
55
56#define SEQMAX 'z'
57#define SEQMIN 'a'
58char seqc;
59int first, secnd, check, numl;
60
61FILE *in, *out;
62char *pos;
63char ifname[FILELEN], ofname[FILELEN];
64char *source = NULL, *target = NULL;
65char blank, part = '\0';
66int partn, lens;
67int debug = 0, nochk = 0, onedone = 0;
68int chtbl[NCHARS], cdlen[NORMLEN + 3];
69
70_PROTOTYPE(int main, (int argc, char **argv));
71_PROTOTYPE(char *getnword, (char *str, int n));
72_PROTOTYPE(void gettable, (void));
73_PROTOTYPE(void decode, (void));
74_PROTOTYPE(void getfile, (char *buf));
75_PROTOTYPE(void format, (char *fp, ...));
76_PROTOTYPE(void doprnt, (char *fp, char *ap));
77_PROTOTYPE(void puti, (unsigned int i, unsigned int r));
78_PROTOTYPE(void outc, (int c));
79
80int main(argc, argv) int argc; char *argv[];
81{
82 int mode;
83 register int i, j;
84 char *curarg;
85 char dest[FILELEN], buf[LINELEN];
86
87 while ((curarg = argv[1]) != NULL && curarg[0] == '-') {
88 if (((curarg[1] == 'd') || (curarg[1] == 'D')) &&
89 (curarg[2] == '\0')) {
90 debug = 1;
91 } else if (((curarg[1] == 'n') || (curarg[1] == 'N')) &&
92 (curarg[2] == '\0')) {
93 nochk = 1;
94 } else if (((curarg[1] == 't') || (curarg[1] == 'T')) &&
95 (curarg[2] == '\0')) {
96 argv++;
97 argc--;
98 if (argc < 2) {
99 format("uud: Missing target directory.\n");
100 Error(15);
101 }
102 target = argv[1];
103 if (debug)
104 format("Target dir = %s\n",target);
105 } else if (((curarg[1] == 's') || (curarg[1] == 'S')) &&
106 (curarg[2] == '\0')) {
107 argv++;
108 argc--;
109 if (argc < 2) {
110 format("uud: Missing source directory.\n");
111 Error(15);
112 }
113 source = argv[1];
114 if (debug)
115 format("Source dir = %s\n",source);
116 } else if (curarg[1] != '\0') {
117 format("Usage: uud [-n] [-d] [-s dir] [-t dir] [input-file]\n");
118 Error(1);
119 } else
120 break;
121 argv++;
122 argc--;
123 }
124
125 if (curarg == NULL || ((curarg[0] == '-') && (curarg[1] == '\0'))) {
126 in = stdin;
127 strcpy(ifname, "<stdin>");
128 } else {
129 if (source != NULL) {
130 strcpy(ifname, source);
131 strcat(ifname, curarg);
132 } else
133 strcpy(ifname, curarg);
134 if ((in = fopen(ifname, "r")) == NULL) {
135 format("uud: Can't open %s\n", ifname);
136 Error(2);
137 }
138 numl = 0;
139 }
140
141/*
142 * Set up the default translation table.
143 */
144 for (i = 0; i < ' '; i++) chtbl[i] = -1;
145 for (i = ' ', j = 0; i < ' ' + 64; i++, j++) chtbl[i] = j;
146 for (i = ' ' + 64; i < NCHARS; i++) chtbl[i] = -1;
147 chtbl['`'] = chtbl[' ']; /* common mutation */
148 chtbl['~'] = chtbl['^']; /* an other common mutation */
149 blank = ' ';
150/*
151 * set up the line length table, to avoid computing lotsa * and / ...
152 */
153 cdlen[0] = 1;
154 for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4)
155 cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j));
156/*
157 * search for header or translation table line.
158 */
159 loop { /* master loop for multiple decodes in one file */
160 partn = 'a';
161 loop {
162 if (fgets(buf, sizeof buf, in) == NULL) {
163 if (onedone) {
164 if (debug) format("End of file.\n");
165 exit(0);
166 } else {
167 format("uud: No begin line.\n");
168 Error(3);
169 }
170 }
171 numl++;
172 if (strncmp(buf, "table", (size_t)5) == 0) {
173 gettable();
174 continue;
175 }
176 if (strncmp(buf, "begin", (size_t)5) == 0) {
177 break;
178 }
179 }
180 lens = strlen(buf);
181 if (lens) buf[--lens] = '\0';
182#ifdef SMALL
183 if ((pos = getnword(buf, 3))) {
184 strcpy(dest, pos);
185 } else
186#else
187 if(sscanf(buf,"begin%o%s", &mode, dest) != 2)
188#endif
189 {
190 format("uud: Missing filename in begin line.\n");
191 Error(10);
192 }
193
194 if (target != NULL) {
195 strcpy(ofname, target);
196 strcat(ofname, dest);
197 } else
198 strcpy(ofname, dest);
199
200 if((out = fopen(ofname, WRITE)) == NULL) {
201 format("uud: Cannot open output file: %s\n", ofname);
202 Error(4);
203 }
204 if (debug) format("Begin uudecoding: %s\n", ofname);
205 seqc = SEQMAX;
206 check = nochk ? 0 : 1;
207 first = 1;
208 secnd = 0;
209 decode();
210 fclose(out);
211#ifdef UNIX
212 chmod(ofname, mode);
213#endif
214 onedone = 1;
215 if (debug) format("End uudecoding: %s\n", ofname);
216 } /* master loop for multiple decodes in one file */
217}
218
219/*
220 * Bring back a pointer to the start of the nth word.
221 */
222char *getnword(str, n) register char *str; register int n;
223{
224 while((*str == '\t') || (*str == ' ')) str++;
225 if (! *str) return NULL;
226 while(--n) {
227 while ((*str != '\t') && (*str != ' ') && (*str)) str++;
228 if (! *str) return NULL;
229 while((*str == '\t') || (*str == ' ')) str++;
230 if (! *str) return NULL;
231 }
232 return str;
233}
234
235/*
236 * Install the table in memory for later use.
237 */
238void gettable()
239{
240 char buf[LINELEN];
241 register int c, n = 0;
242 register char *cpt;
243
244 for (c = 0; c < NCHARS; c++) chtbl[c] = -1;
245
246again: if (fgets(buf, sizeof buf, in) == NULL) {
247 format("uud: EOF while in translation table.\n");
248 Error(5);
249 }
250 numl++;
251 if (strncmp(buf, "begin", (size_t)5) == 0) {
252 format("uud: Incomplete translation table.\n");
253 Error(6);
254 }
255 cpt = buf + strlen(buf) - 1;
256 *cpt = ' ';
257 while (*(cpt) == ' ') {
258 *cpt = 0;
259 cpt--;
260 }
261 cpt = buf;
262 while (c = *cpt) {
263 if (chtbl[c] != -1) {
264 format("uud: Duplicate char in translation table.\n");
265 Error(7);
266 }
267 if (n == 0) blank = c;
268 chtbl[c] = n++;
269 if (n >= 64) return;
270 cpt++;
271 }
272 goto again;
273}
274
275/*
276 * copy from in to out, decoding as you go along.
277 */
278
279void decode()
280{
281 char buf[LINELEN], outl[LINELEN];
282 register char *bp, *ut;
283 register int *trtbl = chtbl;
284 register int n, c, rlen;
285 register unsigned int len;
286
287 loop {
288 if (fgets(buf, sizeof buf, in) == NULL) {
289 format("uud: EOF before end.\n");
290 fclose(out);
291 Error(8);
292 }
293 numl++;
294 len = strlen(buf);
295 if (len) buf[--len] = '\0';
296/*
297 * Is it an unprotected empty line before the end line ?
298 */
299 if (len == 0) continue;
300/*
301 * Get the binary line length.
302 */
303 n = trtbl[*buf];
304 if (n >= 0) goto decod;
305/*
306 * end of uuencoded file ?
307 */
308 if (strncmp(buf, "end", (size_t)3) == 0) return;
309/*
310 * end of current file ? : get next one.
311 */
312 if (strncmp(buf, "include", (size_t)7) == 0) {
313 getfile(buf);
314 continue;
315 }
316 format("uud: Bad prefix line %d in file: %s\n",numl, ifname);
317 if (debug) format("Bad line =%s\n",buf);
318 Error(11);
319/*
320 * Sequence checking ?
321 */
322decod: rlen = cdlen[n];
323/*
324 * Is it the empty line before the end line ?
325 */
326 if (n == 0) continue;
327/*
328 * Pad with blanks.
329 */
330 for (bp = &buf[c = len];
331 c < rlen; c++, bp++) *bp = blank;
332/*
333 * Verify if asked for.
334 */
335 if (debug) {
336 for (len = 0, bp = buf; len < rlen; len++) {
337 if (trtbl[*bp] < 0) {
338 format(
339 "Non uuencoded char <%c>, line %d in file: %s\n", *bp, numl, ifname);
340 format("Bad line =%s\n",buf);
341 Error(16);
342 }
343 bp++;
344 }
345 }
346/*
347 * All this just to check for uuencodes that append a 'z' to each line....
348 */
349 if (secnd && check) {
350 secnd = 0;
351 if (buf[rlen] == SEQMAX) {
352 check = 0;
353 if (debug) format("Sequence check turned off (2).\n");
354 } else
355 if (debug) format("Sequence check on (2).\n");
356 } else if (first && check) {
357 first = 0;
358 secnd = 1;
359 if (buf[rlen] != SEQMAX) {
360 check = 0;
361 if (debug) format("No sequence check (1).\n");
362 } else
363 if (debug) format("Sequence check on (1).\n");
364 }
365/*
366 * There we check.
367 */
368 if (check) {
369 if (buf[rlen] != seqc) {
370 format("uud: Wrong sequence line %d in %s\n",
371 numl, ifname);
372 if (debug)
373 format(
374 "Sequence char is <%c> instead of <%c>.\n", buf[rlen], seqc);
375 Error(18);
376 }
377 seqc--;
378 if (seqc < SEQMIN) seqc = SEQMAX;
379 }
380/*
381 * output a group of 3 bytes (4 input characters).
382 * the input chars are pointed to by p, they are to
383 * be output to file f.n is used to tell us not to
384 * output all of them at the end of the file.
385 */
386 ut = outl;
387 len = n;
388 bp = &buf[1];
389 while (n > 0) {
390 *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4;
391 n--;
392 if (n) {
393 *(ut++) = (trtbl[bp[1]] << 4) |
394 (trtbl[bp[2]] >> 2);
395 n--;
396 }
397 if (n) {
398 *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]];
399 n--;
400 }
401 bp += 4;
402 }
403 if ((n = fwrite(outl, (size_t)1, (size_t)len, out)) <= 0) {
404 format("uud: Error on writing decoded file.\n");
405 Error(18);
406 }
407 }
408}
409
410/*
411 * Find the next needed file, if existing, otherwise try further
412 * on next file.
413 */
414void getfile(buf) register char *buf;
415{
416 if ((pos = getnword(buf, 2)) == NULL) {
417 format("uud: Missing include file name.\n");
418 Error(17);
419 } else
420 if (source != NULL) {
421 strcpy(ifname, source);
422 strcat(ifname, pos);
423 } else
424 strcpy(ifname, pos);
425#ifdef GEMDOS
426 if (Fattrib(ifname, 0, 0) < 0)
427#else
428 if (access(ifname, 04))
429#endif
430 {
431 if (debug) {
432 format("Cant find: %s\n", ifname);
433 format("Continuing to read same file.\n");
434 }
435 }
436 else {
437 if (freopen(ifname, "r", in) == in) {
438 numl = 0;
439 if (debug)
440 format("Reading next section from: %s\n", ifname);
441 } else {
442 format("uud: Freopen abort: %s\n", ifname);
443 Error(9);
444 }
445 }
446 loop {
447 if (fgets(buf, LINELEN, in) == NULL) {
448 format("uud: No begin line after include: %s\n", ifname);
449 Error(12);
450 }
451 numl++;
452 if (strncmp(buf, "table", (size_t)5) == 0) {
453 gettable();
454 continue;
455 }
456 if (strncmp(buf, "begin", (size_t)5) == 0) break;
457 }
458 lens = strlen(buf);
459 if (lens) buf[--lens] = '\0';
460/*
461 * Check the part suffix.
462 */
463 if ((pos = getnword(buf, 3)) == NULL ) {
464 format("uud: Missing part name, in included file: %s\n", ifname);
465 Error(13);
466 } else {
467 part = *pos;
468 partn++;
469 if (partn > 'z') partn = 'a';
470 if (part != partn) {
471 format("uud: Part suffix mismatch: <%c> instead of <%c>.\n",
472 part, partn);
473 Error(14);
474 }
475 if (debug) format("Reading part %c\n", *pos);
476 }
477}
478
479/*
480 * Printf style formatting. (Borrowed from MicroEmacs by Dave Conroy.)
481 * A lot smaller than the full fledged printf.
482 */
483#ifdef __STDC__
484void format(char *fp, ...)
485{
486 va_list args;
487
488 va_start (args, fp);
489 doprnt(fp, (char *)&args);
490 va_end(args);
491}
492#else
493/* VARARGS1 */
494void format(fp, args) char *fp;
495{
496 doprnt(fp, (char *)&args);
497}
498#endif
499
500void doprnt(fp, ap)
501register char *fp;
502register char *ap;
503{
504 register int c, k;
505 register char *s;
506
507 while ((c = *fp++) != '\0') {
508 if (c != '%')
509 outc(c);
510 else {
511 c = *fp++;
512 switch (c) {
513 case 'd':
514 puti(*(int *)ap, 10);
515 ap += sizeof(int);
516 break;
517
518 case 's':
519 s = *(char **)ap;
520 while ((k = *s++) != '\0')
521 outc(k);
522 ap += sizeof(char *);
523 break;
524
525 case 'c':
526 outc(*(int *)ap);
527 ap += sizeof(int);
528 break;
529
530 default:
531 outc(c);
532 }
533 }
534 }
535}
536
537/*
538 * Put integer, in radix "r".
539 */
540void puti(i, r)
541register unsigned int i;
542register unsigned int r;
543{
544 register unsigned int q, s;
545
546 if ((q = i / r) != 0)
547 puti(q, r);
548 s = i % r;
549 if (s <= 9)
550 outc(s + '0');
551 else
552 outc(s - 10 + 'A');
553}
554void outc(c) register char c;
555{
556#ifdef GEMDOS
557 if (c == '\n') Bconout(2, '\r');
558 Bconout(2, c);
559#else
560 putchar(c);
561#endif
562}
Note: See TracBrowser for help on using the repository browser.