[comp.sys.cbm] UUENCODER source code in C

scott@max.u.washington.edu (02/21/90)

This is the C source code and comments for UUENCODER.
 
Sincerely,
Scott K. Stephen
 
==============================================================================
 
Modified to compile with Microsoft C v3.00 and CI86 only.
Much easier to use than uue [remote-f-n] <local-f-n >output-f-n.
Both are inverse of the other.
Gregory Hicks
==============================================================================
Modified once more to compile with Microsoft C V3.00.
The CI86 code is still there, surrounded with #ifdef CI86...#endif
 
Usage is:
        uuencode [input [output] ]
                if output not specified, write to stdout
                if input not specified, get input from stdin
        uudecode [input]
                if input not specified, get input from stdin
 
WARNING: Current restriction on VAX/VMS:  ASCII text files get
all CR/LF combinations converted to LF only during UUENCODE.
This should not be a major problem, as ASCII text files should
not need to be "encoded".
 
Also note that the return status is not consistent with VAX/VMS
usage, although the error messages should be OK.
 
Gary Stebbins, DEC, 8/31/86
==============================================================================
The files described in the following paragraphs have been modified
for and compiled under Computer Innovations C86.  Note that when
uuencoding a binary file, you must not feed the file to uuencode
through stdin--for some reason, this causes the file to only be
partially encoded (I suspect that this happens when uuencode runs
into a ^Z in the input--I have played around with putting stdin into
'raw' mode, which failed to successfully correct the problem).  So
when uuencoding binary files, use the full two-argument form.  Note
that uudecode is unaffected.
 
Brant Cheikes
Department of Computer and Information Science
University of Pennsylvania
ARPA:  brant@linc.cis.upenn.edu
CSNET: brant%upenn-linc@upenn.csnet
==============================================================================
 
uudecode and uuencode are easily implemented under MSDOS as well.  Here
are the sources for Microsoft C v3.0, but if you have another kind of C
compiler, there should be perhaps only 1 change -- the output file of
uudecode and the input file of uuencode must be in binary format.
(ie.  binary files, like .EXE files may have byte patterns that are the
same as ^Z, which signals end-of-file in non-binary (text) mode).
 
If you need more info, write back.  Note that the included files are
*not* in "shar" format -- you will have to use an editor to cut the
files out.
 
        Don Kneller
UUCP:   ...ucbvax!ucsfcgl!kneller
ARPA:   kneller@ucsf-cgl.ARPA
BITNET: kneller@ucsfcgl.BITNET
 
/*
 * uudecode [input]
 *
 * if input not specified, get input from stdin
 *
 * Create the specified file, decoding as you go.
 * Used with uuencode.
 *
 * Modified for use with Microsoft C and VAX/VMS.
 * Define CI86 symbol to use with CI86.
 */
#include <stdio.h>
#ifndef CI86
#include <sys/types.h>
#include <sys/stat.h>
#endif
 
/* single character decode */
#define DEC(c)  (((c) - ' ') & 077)
 
main(argc, argv)
char **argv;
{
        FILE *in, *out;
#ifndef CI86
        struct stat sbuf;
#endif
        int mode;
        char dest[128];
        char buf[80];
 
        /* optional input arg */
        if (argc > 1) {
                if ((in = fopen(argv[1], "r")) == NULL) {
                        perror(argv[1]);
                        exit(1);
                }
                argv++; argc--;
        } else
                in = stdin;
 
        if (argc != 1) {
                fprintf(stderr, "Usage: uudecode [infile]\n");
                exit(2);
        }
 
        /* search for header line */
        for (;;) {
                if (fgets(buf, sizeof buf, in) == NULL) {
                        fprintf(stderr, "No begin line\n");
                        exit(3);
                }
                if (strncmp(buf, "begin ", 6) == 0)
                        break;
        }
        sscanf(buf, "begin %o %s", &mode, dest);
 
        /* create output file */
        /* binary output file */
        out = fopen(dest, "wb");
        if (out == NULL) {
                perror(dest);
                exit(4);
        }
#ifndef CI86
        chmod(dest, mode);
#endif
 
        decode(in, out);
 
        if (fgets(buf, sizeof buf, in) == NULL || strcmp(buf, "end\n")) {
                fprintf(stderr, "No end line\n");
                exit(5);
        }
        exit(0);
}
 
/*
 * copy from in to out, decoding as you go along.
 */
decode(in, out)
FILE *in;
FILE *out;
{
        char buf[80];
        char *bp;
        int n;
 
        for (;;) {
                /* for each input line */
                if (fgets(buf, sizeof buf, in) == NULL) {
                        fprintf(stderr, "Short file\n");
                        exit(10);
                }
                n = DEC(buf[0]);
                if (n <= 0)
                        break;
 
                bp = &buf[1];
                while (n > 0) {
                        outdec(bp, out, n);
                        bp += 4;
                        n -= 3;
                }
        }
}
 
/*
 * 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, f, n)
char *p;
FILE *f;
{
        int c1, c2, c3;
 
        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, f);
        if (n >= 2)
                putc(c2, f);
        if (n >= 3)
                putc(c3, f);
}
 
 
/* fr: like read but stdio */
int
fr(fd, buf, cnt)
FILE *fd;
char *buf;
int cnt;
{
        int c, i;
 
        for (i=0; i<cnt; i++) {
                c = getc(fd);
                if (c == EOF)
                        return(i);
                buf[i] = c;
        }
        return (cnt);
}
 
/*
 * Return the ptr in sp at which the character c appears;
 * NULL if not found
 */
 
#define NULL    0
 
char *
index(sp, c)
register char *sp, c;
{
        do {
                if (*sp == c)
                        return(sp);
        } while (*sp++);
        return(NULL);
}
-----------------------------------------------
/*
 * uuencode [input [output] ]
 *
 * if output not specified, output to stdout
 * if input not specified, input from stdin
 *
 * Encode a file so it can be mailed to a remote system.
 *
 * Modified for use with Microsoft C and VAX/VMS.
 * Define CI86 symbol to use with CI86.
 */
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
 
/* ENC is the basic 1 character encoding function to make a char printing */
#define ENC(c) (((c) & 077) + ' ')
 
main(argc, argv)
char **argv;
{
        FILE *in, *out;
        struct stat sbuf;
        int mode;
 
        /* if 3 arguments, then output file specified */
        if (argc > 2) {
                if ((out = fopen(argv[2], "w")) == NULL) {
                        perror(argv[2]);
                        exit(3);
                }
                argc--;
        }
        else
                out = stdout;
        if (argc > 1) {
                /* Use binary mode */
                if ((in = fopen(argv[1], "rb")) == NULL) {
                        perror(argv[1]);
                        exit(1);
                }
                argc--;
        } else
                in = stdin;
 
        if (argc != 1) {
                fprintf(stderr,"Usage: uuencode [infile [outfile] ]\n");
                exit(2);
        }
 
        /* figure out the input file mode */
        fstat(fileno(in), &sbuf);
        mode = sbuf.st_mode & 0777;
        fprintf(out,"begin %o %s\n", mode, argv[1]);
 
        encode(in, out);
 
        fprintf(out,"end\n");
        exit(0);
}
 
/*
 * copy from in to out, encoding as you go along.
 */
encode(in, out)
FILE *in;
FILE *out;
{
        char buf[80];
        int i, n;
 
        for (;;) {
                /* 1 (up to) 45 character line */
                n = fr(in, buf, 45);
                putc(ENC(n), out);
 
                for (i=0; i<n; i += 3)
                        outdec(&buf[i], out);
 
                putc('\n', out);
                if (n <= 0)
                        break;
        }
}
 
/*
 * output one group of 3 bytes, pointed at by p, on file f.
 */
outdec(p, f)
char *p;
FILE *f;
{
        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;
        putc(ENC(c1), f);
        putc(ENC(c2), f);
        putc(ENC(c3), f);
        putc(ENC(c4), f);
}
 
/* fr: like read but stdio */
int
fr(fd, buf, cnt)
FILE *fd;
char *buf;
int cnt;
{
        int c, i;
 
        for (i=0; i<cnt; i++) {
                c = getc(fd);
                if (c == EOF)
                        return(i);
                buf[i] = c;
        }
        return (cnt);
}