ae75f9d4e5
- 755 -> 644
562 lines
12 KiB
C
562 lines
12 KiB
C
/* 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 <sys/types.h>
|
|
#include <stdarg.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
#include <sys/stat.h>
|
|
#include <stdio.h>
|
|
|
|
#ifdef GEMDOS
|
|
#include <osbind.h>
|
|
#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, "<stdin>");
|
|
} 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
|
|
}
|