[comp.sources.misc] Uuencode pair w/backwards compatible checksums & BITNET compatibility

bryce@hoser.berkeley.edu (Bryce Nesbitt) (09/09/87)

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of shell archive."
# Contents:  uuencode.1 uuencode.c uudecode.c
# Wrapped by bryce@hoser.berkeley.edu on Mon Sep  7 22:27:18 1987
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f uuencode.1 -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"uuencode.1\"
else
echo shar: Extracting \"uuencode.1\" \(2661 characters\)
sed "s/^X//" >uuencode.1 <<'END_OF_uuencode.1'
X.TH UUENCODE 1 "1 Sep 1987"
X.UC 4
X.SH NAME
Xuuencode,uudecode \- encode/decode a binary file for transmission via
X(plain text) mail
X.SH SYNOPSIS
X.B uuencode
X[source] remotedest |
X.B mail
Xsys1!sys2!..!decode
X.br
X.B uuencode
X>outfile.uue source remotedest
X.br
X.B uuencode
X>outfile.uue <source remotedest
X.br
X.B uudecode
X[file]
X.SH DESCRIPTION
X.I Uuencode
Xand
X.I uudecode
Xare used to send a binary file via uucp (or other) mail.  This combination can
Xbe used over indirect mail links even when
X.IR uusend (1C)
Xor other direct binary transmission methods are not available.
X.PP
X.I Uuencode
Xtakes the named source file (default standard input) and produces an encoded
Xversion on the standard output.  The encoding uses only printing ASCII
Xcharacters, and includes the mode of the file and the
X.I remotedest
Xfor recreation on the remote system.
X.PP
X.I Uudecode
Xreads an encoded file, strips off any leading lines added by mailers,
Xand recreates the original file with the specified mode and name.  If
Xthere are other uuencoded files in the same input, they will be decoded
Xas well.
X.PP
XThe intent is that all mail to the user ``decode'' should be filtered
Xthrough the uudecode program.  This way the file is created automatically
Xwithout human intervention.  This is possible on the uucp network by either
Xusing
X.I sendmail
Xor by making
X.I rmail
Xbe a link to
X.I Mail
Xinstead of
X.I mail.
XIn each case, an alias must be created in a master file to get
Xthe automatic invocation of uudecode.
X.PP
XIf these facilities are not available, the file can be uudecoded manually.
X.PP
XThe encode file has an ordinary text form and can be edited by any text editor
Xto change the mode or remote name.
X.PP
XThis version of uuencode automatically adds checksums to each line, and
Xan overall file size to the end.  Old versions of uudecode will ignore this
Xextra information, new versions will check it and complain if it is in
Xerror.
X.PP
XCare has been taken with these versions to compensate for most of the nasty
Xthings editors and networks can do to files.  In particular, lines that
Xhave had trailing spaces truncated are handled, as are files that have
Xpassed through
X.I BITNET
Xhosts.
X.SH SEE\ ALSO
Xuuencode(5), uusend(1C), uucp(1C), uux(1C), mail(1)
X.SH AUTHOR
XMark Horton
X.br
XChecksums and Amiga port by Alan J. Rosenthal
X.br
XFile compatability added back in by Fred Fish
X.br
XFile size check and various refinements by Bryce Nesbitt
X.SH BUGS
XThe file is expanded by 35% (3 bytes become 4, plus control information)
Xcausing it to take longer to transmit.
X.PP
XThe user on the remote system who is invoking
X.I uudecode
X(often
X.I uucp)
Xmust have write permission on the specified file
END_OF_uuencode.1
if test 2661 -ne `wc -c <uuencode.1`; then
    echo shar: \"uuencode.1\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f uuencode.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"uuencode.c\"
else
echo shar: Extracting \"uuencode.c\" \(3245 characters\)
sed "s/^X//" >uuencode.c <<'END_OF_uuencode.c'
X/* #ifndef lint
Xstatic char sccsid[] = "@(#)uuencode.c  5.3-1 (Berkeley) 9/1/87";
X#endif */
X
X/* Written by Mark Horton */
X/* Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums */
X/* Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
X   compatibility */
X/* Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to enable CTRL-C for
X   Amiga Lattice C and add a transparant file size trailer for later check. */
X
X/*
X * uuencode >outfile [infile] name
X *
X * Encode a file so it can be mailed to a remote system.  This version
X * transparantly adds line checksums and a file size for sanity checks.
X *
X */
X
X#include <stdio.h>
X
X#ifdef	AMIGA			/* Amiga Lattice C */
X#define AMIGA_LATTICE
X#define MCH_AMIGA
X#define MPU68000
X#endif
X
X#ifdef unix
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X#define SUMSIZE 64  /* 6 bits */
X/* ENC is the basic 1 character encode function to make a char printing */
X/* Each output character represents 6 bits of input */
X#define ENC(c) ((c) ? ((c) & 077) + ' ': '`')
Xlong	totalsize=0;	/* Used to count the file size because ftell() does
X			   not return sane results for pipes */
X
Xmain(argc, argv)
Xchar **argv;
X{
X    FILE *in;
X    int mode;
X#ifdef unix
X    struct stat sbuf;
X#endif
X#ifdef AMIGA_LATTICE
X    extern int Enable_Abort;	/* Enable CTRL-C for Lattice */
X    Enable_Abort=1;
X#endif
X
X	/* optional 1st argument */
X	if (argc > 2) {
X		if ((in = fopen(argv[1], "r")) == NULL) {
X			fprintf(stderr, "ERROR: can't find %s\n", argv[1]);
X			fprintf(stderr, "USAGE: uuencode >outfile [infile] name\n");
X			exit(10);
X		}
X		argv++; argc--;
X	} else
X		in = stdin;
X
X	if (argc != 2) {
X		fprintf(stderr, "USAGE: uuencode >outfile [infile] name\n");
X		exit(11);
X	}
X
X#ifdef unix
X	/* figure out the input file mode */
X	fstat(fileno(in), &sbuf);
X	mode = sbuf.st_mode & 0777;
X#else
X	mode = 0644;	    /* Default permissions */
X#endif
X
X	printf("\nbegin %o %s\n", mode, argv[1]);
X
X	encode(in, stdout);
X
X	printf("end\n");
X	printf("size %ld\n",totalsize);
X	exit(0);
X}
X
X/*
X * copy from in to out, encoding as you go along.
X */
Xencode(in, out)
XFILE *in;
XFILE *out;
X{
X#ifndef unix
Xextern errno;
X#endif
X	char buf[80];
X	int i, n, checksum;
X
X	for (;;) {
X		/* 1 (up to) 45 character line */
X		n = fr(in, buf, 45);
X		putc(ENC(n), out);
X
X		checksum = 0;
X		for (i=0; i<n; i += 3)
X		    checksum = (checksum+outdec(&buf[i], out)) % SUMSIZE;
X
X		putc(ENC(checksum), out);
X		putc('\n', out);
X
X#ifndef unix
X		/* Error checking under UNIX?? You must be kidding! */
X		if (errno) {
X		    fprintf(stderr, "ERROR: error writing to output\n");
X			exit(12);
X		    }
X#endif
X		if (n <= 0)
X			break;
X	}
X}
X
X/*
X * output one group of 3 bytes, pointed at by p, on file f.
X * return the checksum increment.
X */
Xint outdec(p, f)
Xchar *p;
XFILE *f;
X{
X	int c1, c2, c3, c4;
X
X	c1 = *p >> 2;
X	c2 = (*p << 4) & 060 | (p[1] >> 4) & 017;
X	c3 = (p[1] << 2) & 074 | (p[2] >> 6) & 03;
X	c4 = p[2] & 077;
X	putc(ENC(c1), f);
X	putc(ENC(c2), f);
X	putc(ENC(c3), f);
X	putc(ENC(c4), f);
X
X	return((p[0]+p[1]+p[2]) % SUMSIZE);
X}
X
X/* fr: like read but stdio */
Xint
Xfr(fd, buf, cnt)
XFILE *fd;
Xchar *buf;
Xint cnt;
X{
X	int c, i;
X
X	for (i=0; i<cnt; i++) {
X		c = getc(fd);
X		if (c == EOF)
X			return(i);
X		totalsize++;
X		buf[i] = c;
X	}
X	return (cnt);
X}
END_OF_uuencode.c
if test 3245 -ne `wc -c <uuencode.c`; then
    echo shar: \"uuencode.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
if test -f uudecode.c -a "${1}" != "-c" ; then 
  echo shar: Will not over-write existing file \"uudecode.c\"
else
echo shar: Extracting \"uudecode.c\" \(6418 characters\)
sed "s/^X//" >uudecode.c <<'END_OF_uudecode.c'
X/* #ifndef lint
Xstatic char sccsid[] = "@(#)uudecode.c  5.3-1 (Berkeley) 9/1/87";
X#endif */
X
X/* Written by Mark Horton */
X/* Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums */
X/* Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
X   compatibility */
X/* Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a misleading
X   error message on the Amiga port, to fix a bug that prevented decoding
X   certain files, to work even if trailing spaces have been removed from a
X   file, to check the filesize (if present), to add some error checking, to
X   loop for multiple decodes from a single file, and to handle common
X   BITNET mangling.  Also kludged around a missing string function in Aztec
X   C */
X
X/*
X * uudecode [input]
X *
X * Decode a file encoded with uuencode.  WIll extract multiple encoded
X * modules from a single file.	Can deal with most mangled files, including
X * BITNET.
X */
X
X#include <stdio.h>
X#include <ctype.h>
X
X#ifdef AMIGA
X#define AMIGA_LATTICE	    /* Set for Amiga Lattice C */
X#define MCH_AMIGA
X#define MPU68000
X#endif
X
X#ifdef unix
X#include <pwd.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#endif
X
X#define SUMSIZE 64
X#define DEC(c)	(((c) - ' ') & 077)    /* single character decode */
X
Xmain(argc, argv)
Xchar **argv;
X{
XFILE	*in, *out;
Xint	through_loop=0; /* Dejavu indicator */
Xint	mode;		/* file's mode (from header) */
Xlong	filesize;	/* theoretical file size (from header) */
Xchar	dest[128];
Xchar	buf[80];
X
X#ifdef AMIGA_LATTICE
Xextern	int Enable_Abort;
X	Enable_Abort=1;
X#endif
X
X    /* A filename can be specified to be uudecoded, or nothing can
X    be specified, and the input will come from STDIN */
X
X    switch (argc)
X	{
X	case 1:
X	in=stdin;
X	break;
X
X	case 2:
X	if ((in = fopen(argv[1], "r")) == NULL)
X	    {
X	    fprintf(stderr, "ERROR: can't find %s\n", argv[1]);
X	    fprintf(stderr, "USAGE: uudecode [infile]\n");
X	    exit(10);
X	    }
X	break;
X
X	default:
X	fprintf(stderr, "USAGE: uudecode [infile]\n");
X	exit(11);
X	break;
X	}
X
X    /* Loop through file, searching for headers.  Decode anything with a
X       header, complain if there where no headers. */
X
Xfor (;;)
X{
X    /* search file for header line */
X    for (;;)
X	{
X	if (fgets(buf, sizeof buf, in) == NULL)
X	    {
X	    if (!through_loop)
X		{
X		fprintf(stderr, "ERROR: no `begin' line!\n");
X		exit(12);
X		}
X	    else
X		{
X		exit(0);
X		}
X	    }
X	if (strncmp(buf, "begin ", 6) == 0)
X	    break;
X	}
X    sscanf(buf, "begin %o %s", &mode, dest);
X
X#ifdef unix
X    /* handle ~user/file format */
X    if (dest[0] == '~')
X	{
X	char *sl;
X	struct passwd *getpwnam();
X	char *index();
X	struct passwd *user;
X	char dnbuf[100];
X
X	sl = index(dest, '/');
X	if (sl == NULL)
X	    {
X	    fprintf(stderr, "Illegal ~user\n");
X		exit(13);
X	    }
X	*sl++ = 0;
X	user = getpwnam(dest+1);
X	if (user == NULL)
X	    {
X	    fprintf(stderr, "No such user as %s\n", dest);
X	    exit(14);
X	    }
X	strcpy(dnbuf, user->pw_dir);
X	strcat(dnbuf, "/");
X	strcat(dnbuf, sl);
X	strcpy(dest, dnbuf);
X	}
X#endif
X
X    /* create output file */
X    if ((out = fopen(dest, "w")) == NULL)
X	{
X	fprintf(stderr, "ERROR: can't open output file %s\n", dest);
X	exit(15);
X	}
X#ifdef unix
X    chmod(dest, mode);
X#endif
X
X    decode(in, out, dest);
X
X    if (fgets(buf, sizeof buf, in) == NULL || strncmp(buf,"end",3))
X	{	       /* don't be overly picky about newline ^ */
X	fprintf(stderr, "ERROR: no `end' line\n");
X	exit(16);
X	}
X
X    if (!(fgets(buf,sizeof buf,in) == NULL || strncmp(buf,"size ",3)))
X	{
X	sscanf(buf, "size %ld", &filesize);
X	if (ftell(out) != filesize)
X	    {
X	    fprintf(stderr, "ERROR: file should have been %ld bytes long but was %ld.\n", filesize, ftell(out));
X	    exit(17);
X	    }
X	}
X    through_loop = 1;
X}   /* forever */
X}   /* main */
X
X/*
X * Copy from in to out, decoding as you go.
X * If a return or newline is encountered too early in a line, it is
X * assumed that means that some editor has truncated trailing spaces.
X */
Xdecode(in, out, dest)
XFILE *in;
XFILE *out;
Xchar *dest;
X{
Xchar buf[81];
Xchar *bp;
Xint nosum=0;
X#ifndef unix
Xextern errno;
X#endif
Xregister int j;
Xregister int n;
Xint checksum, line;
X
X    for (line = 1; ; line++)	/* for each input line */
X	{
X	if (fgets(buf, sizeof buf, in) == NULL)
X	    {
X	    fprintf(stderr, "ERROR: input ended unexpectedly!\n");
X	    exit(18);
X	    }
X
X	/* Pad end of lines in case some editor truncated trailing
X	   spaces */
X
X	for (n=0;n<79;n++)  /* search for first \r, \n or \000 */
X	    {
X	    if (buf[n]=='\176')     /* If BITNET made a twiddle, */
X		buf[n]='\136';     /* we make a caret           */
X	    if (buf[n]=='\r'||buf[n]=='\n'||buf[n]=='\000')
X		break;
X	    }
X	for (;n<79;n++)     /* when found, fill rest of line with space */
X	    {
X	    buf[n]=' ';
X	    }
X	buf[79]=0;	    /* terminate new string */
X
X	checksum = 0;
X	n = DEC(buf[0]);
X	if (n <= 0)
X	    break;	/* 0 bytes on a line??	Must be the last line */
X
X	bp = &buf[1];
X
X	/* FOUR input characters go into each THREE output charcters */
X
X	while (n >= 4)
X	    {
X	    j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4; putc(j, out); checksum += j;
X	    j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2; putc(j, out); checksum += j;
X	    j = DEC(bp[2]) << 6 | DEC(bp[3]);	   putc(j, out); checksum += j;
X	    checksum = checksum % SUMSIZE;
X	    bp += 4;
X	    n -= 3;
X	    }
X
X	    j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4;
X		checksum += j;
X		if (n >= 1)
X		    putc(j, out);
X	    j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2;
X		checksum += j;
X		if (n >= 2)
X		    putc(j, out);
X	    j = DEC(bp[2]) << 6 | DEC(bp[3]);
X		checksum += j;
X		if (n >= 3)
X		    putc(j, out);
X	    checksum = checksum % SUMSIZE;
X	    bp += 4;
X	    n -= 3;
X
X#ifndef unix
X	 /* Error checking under UNIX??? You must be kidding... */
X	 /* Check if an error occured while writing to that last line */
X	if (errno)
X	    {
X	    fprintf(stderr, "ERROR: error writing to %s\n",dest);
X	    exit(19);
X	    }
X#endif
X
X	/* The line has been decoded; now check that sum */
X
X	nosum |= !isspace(*bp);
X	if (nosum)			/* Is there a checksum at all?? */
X	    {
X	    if (checksum != DEC(*bp))	/* Does that checksum match? */
X		{
X		fprintf(stderr, "ERROR: checksum mismatch decoding %s, line %d.\n",dest, line);
X		}
X	    }	/* sum */
X    }	/* line */
X}   /* function */
X
X#ifdef unix
X/*
X * Return the ptr in sp at which the character c appears;
X * 0 if not found
X */
Xchar *
Xindex(sp, c)
Xregister char *sp, c;
X{
X    do
X	{
X	if (*sp == c)
X	    return(sp);
X	}
X    while (*sp++);
X
X    return(0);
X}
X#endif unix
X
END_OF_uudecode.c
if test 6418 -ne `wc -c <uudecode.c`; then
    echo shar: \"uudecode.c\" unpacked with wrong size!
fi
# end of overwriting check
fi
echo shar: End of shell archive.
exit 0