/* uud - bulletproof version of uudecode */ /* * Uud -- decode a uuencoded file back to binary form. * * From the Berkeley original, modified by MSD, RDR, JPHD & WLS. * The Atari GEMDOS version compiled with MWC 2.x. * The MSDOS version with TurboC. * The Unix version with cc. * this version is made: 25 Nov 1988. * Jan 2 1990: Change system definition and change MSDOS to open the output * file for write binary do cr/lf replacement. */ #define UNIX 1 /* define one of: UNIX (Minix too!), MSDOS, or GEMDOS */ #ifdef GEMDOS #define SYSNAME "gemdos" #define SMALL 1 #endif #ifdef MSDOS #define SYSNAME "msdos" #define SMALL 1 #endif #ifdef UNIX #define SYSNAME "unix" #endif #include #include #include #include #include #include #include #ifdef GEMDOS #include #define Error(n) { Bconin(2); exit(n); } #else #define Error(n) exit(n) #endif #ifdef UNIX #define WRITE "w" #else #define WRITE "wb" /* for both MSDOS and GEMDOS! */ #endif #define loop while (1) #define NCHARS 256 #define LINELEN 256 #define FILELEN 64 #define NORMLEN 60 /* allows for 80 encoded chars per line */ #define SEQMAX 'z' #define SEQMIN 'a' char seqc; int first, secnd, check, numl; FILE *in, *out; char *pos; char ifname[FILELEN], ofname[FILELEN]; char *source = NULL, *target = NULL; char blank, part = '\0'; int partn, lens; int debug = 0, nochk = 0, onedone = 0; int chtbl[NCHARS], cdlen[NORMLEN + 3]; _PROTOTYPE(int main, (int argc, char **argv)); _PROTOTYPE(char *getnword, (char *str, int n)); _PROTOTYPE(void gettable, (void)); _PROTOTYPE(void decode, (void)); _PROTOTYPE(void getfile, (char *buf)); _PROTOTYPE(void format, (char *fp, ...)); _PROTOTYPE(void doprnt, (char *fp, char *ap)); _PROTOTYPE(void puti, (unsigned int i, unsigned int r)); _PROTOTYPE(void outc, (int c)); int main(argc, argv) int argc; char *argv[]; { int mode; register int i, j; char *curarg; char dest[FILELEN], buf[LINELEN]; while ((curarg = argv[1]) != NULL && curarg[0] == '-') { if (((curarg[1] == 'd') || (curarg[1] == 'D')) && (curarg[2] == '\0')) { debug = 1; } else if (((curarg[1] == 'n') || (curarg[1] == 'N')) && (curarg[2] == '\0')) { nochk = 1; } else if (((curarg[1] == 't') || (curarg[1] == 'T')) && (curarg[2] == '\0')) { argv++; argc--; if (argc < 2) { format("uud: Missing target directory.\n"); Error(15); } target = argv[1]; if (debug) format("Target dir = %s\n",target); } else if (((curarg[1] == 's') || (curarg[1] == 'S')) && (curarg[2] == '\0')) { argv++; argc--; if (argc < 2) { format("uud: Missing source directory.\n"); Error(15); } source = argv[1]; if (debug) format("Source dir = %s\n",source); } else if (curarg[1] != '\0') { format("Usage: uud [-n] [-d] [-s dir] [-t dir] [input-file]\n"); Error(1); } else break; argv++; argc--; } if (curarg == NULL || ((curarg[0] == '-') && (curarg[1] == '\0'))) { in = stdin; strcpy(ifname, ""); } else { if (source != NULL) { strcpy(ifname, source); strcat(ifname, curarg); } else strcpy(ifname, curarg); if ((in = fopen(ifname, "r")) == NULL) { format("uud: Can't open %s\n", ifname); Error(2); } numl = 0; } /* * Set up the default translation table. */ for (i = 0; i < ' '; i++) chtbl[i] = -1; for (i = ' ', j = 0; i < ' ' + 64; i++, j++) chtbl[i] = j; for (i = ' ' + 64; i < NCHARS; i++) chtbl[i] = -1; chtbl['`'] = chtbl[' ']; /* common mutation */ chtbl['~'] = chtbl['^']; /* an other common mutation */ blank = ' '; /* * set up the line length table, to avoid computing lotsa * and / ... */ cdlen[0] = 1; for (i = 1, j = 5; i <= NORMLEN; i += 3, j += 4) cdlen[i] = (cdlen[i + 1] = (cdlen[i + 2] = j)); /* * search for header or translation table line. */ loop { /* master loop for multiple decodes in one file */ partn = 'a'; loop { if (fgets(buf, sizeof buf, in) == NULL) { if (onedone) { if (debug) format("End of file.\n"); exit(0); } else { format("uud: No begin line.\n"); Error(3); } } numl++; if (strncmp(buf, "table", (size_t)5) == 0) { gettable(); continue; } if (strncmp(buf, "begin", (size_t)5) == 0) { break; } } lens = strlen(buf); if (lens) buf[--lens] = '\0'; #ifdef SMALL if ((pos = getnword(buf, 3))) { strcpy(dest, pos); } else #else if(sscanf(buf,"begin%o%s", &mode, dest) != 2) #endif { format("uud: Missing filename in begin line.\n"); Error(10); } if (target != NULL) { strcpy(ofname, target); strcat(ofname, dest); } else strcpy(ofname, dest); if((out = fopen(ofname, WRITE)) == NULL) { format("uud: Cannot open output file: %s\n", ofname); Error(4); } if (debug) format("Begin uudecoding: %s\n", ofname); seqc = SEQMAX; check = nochk ? 0 : 1; first = 1; secnd = 0; decode(); fclose(out); #ifdef UNIX chmod(ofname, mode); #endif onedone = 1; if (debug) format("End uudecoding: %s\n", ofname); } /* master loop for multiple decodes in one file */ } /* * Bring back a pointer to the start of the nth word. */ char *getnword(str, n) register char *str; register int n; { while((*str == '\t') || (*str == ' ')) str++; if (! *str) return NULL; while(--n) { while ((*str != '\t') && (*str != ' ') && (*str)) str++; if (! *str) return NULL; while((*str == '\t') || (*str == ' ')) str++; if (! *str) return NULL; } return str; } /* * Install the table in memory for later use. */ void gettable() { char buf[LINELEN]; register int c, n = 0; register char *cpt; for (c = 0; c < NCHARS; c++) chtbl[c] = -1; again: if (fgets(buf, sizeof buf, in) == NULL) { format("uud: EOF while in translation table.\n"); Error(5); } numl++; if (strncmp(buf, "begin", (size_t)5) == 0) { format("uud: Incomplete translation table.\n"); Error(6); } cpt = buf + strlen(buf) - 1; *cpt = ' '; while (*(cpt) == ' ') { *cpt = 0; cpt--; } cpt = buf; while (c = *cpt) { if (chtbl[c] != -1) { format("uud: Duplicate char in translation table.\n"); Error(7); } if (n == 0) blank = c; chtbl[c] = n++; if (n >= 64) return; cpt++; } goto again; } /* * copy from in to out, decoding as you go along. */ void decode() { char buf[LINELEN], outl[LINELEN]; register char *bp, *ut; register int *trtbl = chtbl; register int n, c, rlen; register unsigned int len; loop { if (fgets(buf, sizeof buf, in) == NULL) { format("uud: EOF before end.\n"); fclose(out); Error(8); } numl++; len = strlen(buf); if (len) buf[--len] = '\0'; /* * Is it an unprotected empty line before the end line ? */ if (len == 0) continue; /* * Get the binary line length. */ n = trtbl[*buf]; if (n >= 0) goto decod; /* * end of uuencoded file ? */ if (strncmp(buf, "end", (size_t)3) == 0) return; /* * end of current file ? : get next one. */ if (strncmp(buf, "include", (size_t)7) == 0) { getfile(buf); continue; } format("uud: Bad prefix line %d in file: %s\n",numl, ifname); if (debug) format("Bad line =%s\n",buf); Error(11); /* * Sequence checking ? */ decod: rlen = cdlen[n]; /* * Is it the empty line before the end line ? */ if (n == 0) continue; /* * Pad with blanks. */ for (bp = &buf[c = len]; c < rlen; c++, bp++) *bp = blank; /* * Verify if asked for. */ if (debug) { for (len = 0, bp = buf; len < rlen; len++) { if (trtbl[*bp] < 0) { format( "Non uuencoded char <%c>, line %d in file: %s\n", *bp, numl, ifname); format("Bad line =%s\n",buf); Error(16); } bp++; } } /* * All this just to check for uuencodes that append a 'z' to each line.... */ if (secnd && check) { secnd = 0; if (buf[rlen] == SEQMAX) { check = 0; if (debug) format("Sequence check turned off (2).\n"); } else if (debug) format("Sequence check on (2).\n"); } else if (first && check) { first = 0; secnd = 1; if (buf[rlen] != SEQMAX) { check = 0; if (debug) format("No sequence check (1).\n"); } else if (debug) format("Sequence check on (1).\n"); } /* * There we check. */ if (check) { if (buf[rlen] != seqc) { format("uud: Wrong sequence line %d in %s\n", numl, ifname); if (debug) format( "Sequence char is <%c> instead of <%c>.\n", buf[rlen], seqc); Error(18); } seqc--; if (seqc < SEQMIN) seqc = SEQMAX; } /* * output a group of 3 bytes (4 input characters). * the input chars are pointed to by p, they are to * be output to file f.n is used to tell us not to * output all of them at the end of the file. */ ut = outl; len = n; bp = &buf[1]; while (n > 0) { *(ut++) = trtbl[*bp] << 2 | trtbl[bp[1]] >> 4; n--; if (n) { *(ut++) = (trtbl[bp[1]] << 4) | (trtbl[bp[2]] >> 2); n--; } if (n) { *(ut++) = trtbl[bp[2]] << 6 | trtbl[bp[3]]; n--; } bp += 4; } if ((n = fwrite(outl, (size_t)1, (size_t)len, out)) <= 0) { format("uud: Error on writing decoded file.\n"); Error(18); } } } /* * Find the next needed file, if existing, otherwise try further * on next file. */ void getfile(buf) register char *buf; { if ((pos = getnword(buf, 2)) == NULL) { format("uud: Missing include file name.\n"); Error(17); } else if (source != NULL) { strcpy(ifname, source); strcat(ifname, pos); } else strcpy(ifname, pos); #ifdef GEMDOS if (Fattrib(ifname, 0, 0) < 0) #else if (access(ifname, 04)) #endif { if (debug) { format("Cant find: %s\n", ifname); format("Continuing to read same file.\n"); } } else { if (freopen(ifname, "r", in) == in) { numl = 0; if (debug) format("Reading next section from: %s\n", ifname); } else { format("uud: Freopen abort: %s\n", ifname); Error(9); } } loop { if (fgets(buf, LINELEN, in) == NULL) { format("uud: No begin line after include: %s\n", ifname); Error(12); } numl++; if (strncmp(buf, "table", (size_t)5) == 0) { gettable(); continue; } if (strncmp(buf, "begin", (size_t)5) == 0) break; } lens = strlen(buf); if (lens) buf[--lens] = '\0'; /* * Check the part suffix. */ if ((pos = getnword(buf, 3)) == NULL ) { format("uud: Missing part name, in included file: %s\n", ifname); Error(13); } else { part = *pos; partn++; if (partn > 'z') partn = 'a'; if (part != partn) { format("uud: Part suffix mismatch: <%c> instead of <%c>.\n", part, partn); Error(14); } if (debug) format("Reading part %c\n", *pos); } } /* * Printf style formatting. (Borrowed from MicroEmacs by Dave Conroy.) * A lot smaller than the full fledged printf. */ #ifdef __STDC__ void format(char *fp, ...) { va_list args; va_start (args, fp); doprnt(fp, (char *)&args); va_end(args); } #else /* VARARGS1 */ void format(fp, args) char *fp; { doprnt(fp, (char *)&args); } #endif void doprnt(fp, ap) register char *fp; register char *ap; { register int c, k; register char *s; while ((c = *fp++) != '\0') { if (c != '%') outc(c); else { c = *fp++; switch (c) { case 'd': puti(*(int *)ap, 10); ap += sizeof(int); break; case 's': s = *(char **)ap; while ((k = *s++) != '\0') outc(k); ap += sizeof(char *); break; case 'c': outc(*(int *)ap); ap += sizeof(int); break; default: outc(c); } } } } /* * Put integer, in radix "r". */ void puti(i, r) register unsigned int i; register unsigned int r; { register unsigned int q, s; if ((q = i / r) != 0) puti(q, r); s = i % r; if (s <= 9) outc(s + '0'); else outc(s - 10 + 'A'); } void outc(c) register char c; { #ifdef GEMDOS if (c == '\n') Bconout(2, '\r'); Bconout(2, c); #else putchar(c); #endif }