[net.micro.atari16] Source for new version of uuen/decode

RDROYA01@ULKYVX.BITNET (Robert Royar) (10/29/86)

$ share readme uudecode.c uuencode.c
#       This is a shell archive.
#       Remove everything above and including the cut line.
#       Then run the rest of the file through sh.
#----cut here-----cut here-----cut here-----cut here----#
#!/bin/sh
# shar:    Shell Archiver
#       Run the following text with /bin/sh to create:
#       readme
#       uudecode.c
#       uuencode.c
# This archive created: Wed Oct 29 14:20:25 1986
cat << \SHAR_EOF > readme

I'm opening myself for flames by posting this to the net, but I've had
over twenty requests for this simple program, so I'm posting it in
three parts.  This part contains source for the two uuen/decode
programs.  Part two contains the uudecode program in the "new"
uuencoded form.  And the last part contains uuencode in the encoded
form.  The new form is compatible with the old version as long as you
delete the character tables at the beginning of each uuencoded file
(they're the group of letters before the "begin XXX FILENAME.TTP"),
delete the "include UUDECODB.UUE" line from the end of UUDECODA.UUE,
and delete the "begin part b" line from UUDECODB.UUE.  Then concat
UUDECODA.UUE and UUDECODB.UUE and run through your own UUDECODE
program.
SHAR_EOF
cat << \SHAR_EOF > uudecode.c
/*
 * Uudecode -- decode a uuencoded file back to binary form.
 *
 * Slightly modified from a version posted to net.sources once;
 * suitable for compilation on an IBM PC.
 *
 * modified for Lattice C on the ST - 11.05.86 by MSD
 * modified for Alcyon on the ST -    10-24-86 by RDR
 *
 */


#include <stdio.h>

#define USAGE "Usage: UUDECODE file\n"

/* single character decode */
#define DEC(c)  (((c) - ' ') & 077)

FILE  *out;
extern FILE *freopen(), *fopenb();
extern char *rindex(), *index(), *strcpy();
char chtbl[80]; /* read from input files */
char ifname[80];
int part = 'a';
int err = NO;

main(argc, argv)
int argc;
char *argv[];
{

        int mode;
        register char dest[128];
        register char buf[80];
        register int i;
        register int c;

        if (argc != 2) {
                fprintf(stderr,USAGE);
                exit(1);
        }
        if(freopen(argv[1], "r",stdin)!=stdin) {
                fprintf(stderr,"ABORT: cannot free stdin for reading\n");
                        exit(1);
                }
        makename(argv[1]);      /* get a filename for later */
        gettable();
        /* search for header line */
        for (;;) {
                if (fgets(buf, sizeof buf, stdin) == NULL) {
                        fprintf(stderr, "ABORT: no begin line\n");
                        exit(1);
                        }
                if (strncmp(buf, "begin", 5) == 0)
                        break;
                }
        sscanf(buf, "begin %o %s", &mode, dest);
        if((out=fopenb(dest, "w"))==NULL) {  /* create output file */
                fprintf(stderr,"ABORT: cannot open %s for output\n");
                exit(1);
        }
        decode();
        if(err) {
                /* I know, why bother, but this might prevent an
                 * accidental clicking on a bad command file
                 */
                fclose(out);
                unlink(dest);
        }
        else
                fclose(out);
        if (fgets(buf, sizeof buf, stdin) == NULL || strcmp(buf, "end\n")) {
                fprintf(stderr, "Warning: no end line\n");
                exit(1);
        }
        exit(0);
}

/* Is it a feature of C in general thet strcpy(name,argv[1]) will not
 * work, or is it just ALCYON?  It seems to work under CP/M-68K until
 * you check where the copied string really is, always at &00000000!
 */
makename(name)
char name[];
{
        strcpy(ifname,name);
}

/* read through file looking for the beginning of the character
 * table.  Then install the table in memory for later use.
 */
gettable()
{
        register char buf[80];
        register int c;
        register char *cpt;

        cpt = &chtbl[0];
        for (c=0;c<=66;c++)
                *cpt++ = '\0';
        for (;;) {
                if (fgets(buf, sizeof buf, stdin) == NULL) {
                        fprintf(stderr, "ABORT: no table line\n");
                        exit(1);
                        }
                if (strncmp(buf, "table", 5) == 0)
                        break;
                }
        cpt = &chtbl[0];
        for(;;) {
                c = getchar();
                if (c == '\n')
                        continue;
                if (index(chtbl,c)) {
                        fprintf(stderr,"ABORT: duplicate in character \
table:\nchar: \\%o\nfile: %s\n",c,ifname);
                        exit(1);
                }
                *cpt++ = c;
                if (c == '_') {
                        *cpt = '\0';
                        break;
                }
        }
}

/*
 * copy from stdin to out, decoding as you go along.
 */

decode()
{
        char buf[80];
        register char *bp;
        register int n;
        register char ch;

        for (;;) {
                if (fgets(buf, sizeof buf, stdin) == NULL) {
                        fprintf(stderr, "ABORT: short file %s\n",ifname);
                        break;
                }
                /* end of current file; get next one */
                if (strncmp(buf, "include", 7)==0) {
                        getfile(buf);
                        continue;
                }
                n = DEC(buf[0]);
                if (n <= 0)
                        break;
                bp = &buf[1];
                chkch(&buf[1]);
                while (n > 0) {
                        outdec(bp, n);
                        bp += 4;
                        n -= 3;
                }
        }
}

/* you may need to rename the filenames at the ends of each part
 * if the encoder encoded them on directories and specified drives.
 */
getfile(buf)
register char *buf;
{
        sscanf(buf,"include %s",ifname);
        if (freopen(ifname,"r",stdin)!=stdin) {
                fprintf(stderr,"ABORT: cannot reassign $%X to %s\n",
                stdin,ifname);
                exit(1);
        }
        gettable();
        for (;;) {
                if (fgets(buf, 80, stdin) == NULL) {
                        fprintf(stderr,"ABORT: no begin line in %s\n",ifname);
                        exit(1);
                        }
                if (strncmp(buf, "begin", 5) == 0)
                        break;
        }
        return;
}

/*
 * 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.
 */
outdec(p,n)
register char *p;
register int n;
{
        register int c1, c2, c3;

        if (err)
                return;
        c1 = DEC(*p) << 2 | DEC(p[1]) >> 4;
        c2 = DEC(p[1]) << 4 | DEC(p[2]) >> 2;
        c3 = DEC(p[2]) << 6 | DEC(p[3]);
        if (n >= 1)
                putc(c1, out);
        if (n >= 2)
                putc(c2, out);
        if (n >= 3)
                putc(c3, out);
}

/* This routine returns the character that matches the native
 * character set.
 */
chkch(buf)
register char *buf;
{
        register char *t1, *t2;
        register char c;
        register int offset;
        register long filepos;
        long ftell();

        t1 = &chtbl[0];
        while(c = *buf) {
                if (t2=index(chtbl, c)) {
                        offset = (int)t2 - (int)t1;
                        *buf++ = (char)(' ' + offset);
                }
                else if (c == part)     /* should be a line terminator */
                        *buf++ = c;
                else if (c == '\n')
                        *buf++ = c;
                else {
                        filepos = ftell(stdin);
                        /* filepos is the end of the faulty line */
                        fprintf(stderr,"Warning: suspect character\n\
char: \\%o\nfile: %s\noffset: %D\nOutput file will not be completed\n",
                        c,ifname,filepos);
                        *buf++ = c;
                        err = YES;      /* file will not be closed */
                }
        }
        return;
}
SHAR_EOF
cat << \SHAR_EOF > uuencode.c
/*
 *
 * Uuencode -- encode a file so that it's printable ascii, short lines
 *
 * Slightly modified from a version posted to net.sources a while back,
 * and suitable for compilation on the IBM PC
 *
 * modified for Lattice C on the ST - 11.05.85 by MSD
 * modified for ALCYON on the ST -    10-24-86 by RDR
 *
 */


#include <stdio.h>
#include <ctype.h>

#define USAGE "Usage: UUENCODE file [>outfile]\n"

/* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) (((c) & 077) + ' ')

extern FILE  *freopen(), *fopenb();
FILE *fp;
char ofname[80];
extern char *rindex();
int part = 'a';

main(argc, argv)
int argc; char *argv[];
{
        if (argc < 2) {
                fprintf(stderr, USAGE);
                exit(2);
                }
        makename(argv[1]);
        if ((fp=fopenb(argv[1], "r"))==NULL) {  /* binary input !!! */
                fprintf(stderr,"Cannot open %s\n",argv[1]);
                exit(1);
        }
        if(freopen(ofname, "w", stdout)!=stdout) {
                fprintf(stderr,"Cannot reassign stdout\n");
                exit(1);
                }
        maketable();
        printf("begin %o %s\n", 0777, argv[1]);
        encode();
        printf("end\n");
        exit(0);
}

/* create ASCII table so a mailer can screw it up and the decode
 * program can restore the error.
 */
maketable()
{
        register int i;
        register int j;

        puts("table");
        for(i=' ',j=0;i<'`';j++) {
                if (j==12){ /* just an arbitrarily short line */
                        putchar('\n');
                        j=0;
                }
                else
                        fputc(i++,stdout);
        }
        putchar('\n');
}

/* I include this in all of my programs to take the guess work out of
 * filenames.
 */
makename(name)
char name[];
{
        register char *ptr;

        strcpy(ofname,name);
        /* I think index is neat; just look for a character, and
         * bomb it.  Voila, you have a substring, no mess.
         */
        if(ptr=rindex(ofname,'.')) {
                *ptr = '\0';
                *--ptr = toupper(part);/*makes the include FILE look better*/
        }
        else /* i.e. make the last character in the first name = part */
                ofname[strlen(ofname)-1] = toupper(part);
        strcat(ofname,".UUE");
        return;
}

/*
 * copy from stdin to stdout, encoding as you go along.
 */
encode()
{
        char buf[80];
        char file[80];
        register int i, n;
        register int lines;
        lines = 6;

        strcpy(file,ofname);
        for (;;) {
                n = fr(buf, 45);
                putchar(ENC(n));
                for (i=0; i<n; i += 3)
                      outdec(&buf[i]);
                putchar(part);
                putchar('\n');
                ++lines;
                if (lines >300) {
                        ++part;
                        makename(file);
                        printf("include %s\n",ofname);
                        if(freopen(ofname, "w", stdout)!=stdout) {
                                fprintf(stderr,"Cannot reassign stdout\n");
                                exit(1);
                        }
                        maketable();
                        printf("begin part %c\n",part);
                        lines = 6;
                }
                if (n <= 0)
                        break;
        }
}

/*
 * output one group of 3 bytes, pointed at by p, on file f.
 */
outdec(p)
register char *p;
{
        register int c1, c2, c3, c4;

        c1 = *p >> 2;
        c2 = (*p << 4) & 060 | (p[1] >> 4) & 017;
        c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
        c4 = p[2] & 077;
        putchar(ENC(c1));
        putchar(ENC(c2));
        putchar(ENC(c3));
        putchar(ENC(c4));
}

/* fr: like read but stdio */
int fr(buf, cnt)
register char *buf;
register int cnt;
{
        register int c, i;
        for (i = 0; i < cnt; i++) {
                c = fgetc(fp);
                if (feof(fp))
                        return(i);
                buf[i] = c;
        }
        return (cnt);
}
SHAR_EOF
#       End of shell archive
exit 0
%NONAME-W-NOMSG, Message number 00000000