/* #ifndef lint
static char sccsid[] = "@(#)uudecode.c  5.3-1 (Berkeley) 4/10/85";
#endif */

/* Modification history:
 *   Date	Author		Modification
 * ========	===============	===============================================
 * 88-01-25	R.W.Bowers (RWB@Trng-E.Prime.COM) PLink: (MA*Rick) GEnie: (RWB)
 *				Cleaned-up AMIGA port
 *				Added reporting of files as they are decoded.
 * yy-mm-dd	bcn (Bryce Nesbitt,ucbvax!cogsci!bryce)
 *				Modified  to fix some very misleading error
 *				messages on the Amiga port, to fix a bug that
 *				prevented decoding certain files, to work even
 *				if trailing spaces have been removed from a
 *				file, to check the filesize (if present), to
 *				add some error checking, and to loop for
 *				multiple decodes from a single file.  Also
 *				kludged around a missing string function in
 *				Aztec C 
 * yy-mm-dd	fnf (Fred Fish,well!fnf)
 *				Modified to use Keith Pyle's suggestion for
 *				compatibility
 * yy-mm-dd	ajr (Alan J Rosenthatl,flaps@utcsri.UUCP)
 *				Modified to use checksums
 * yy-mm-dd	Mark Horton
 *				Original coding.
 */

/*
 * uudecode [input]
 *
 * Decode a file encoded with uuencode.
 */

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

#ifdef AMIGA
#define AMIGA_LATTICE    /* Set for Amiga Lattice C */
#define MCH_AMIGA
#define MPU68000
#endif

#ifdef unix
#include <pwd.h>
#include <sys/types.h>
#include <sys/stat.h>
#endif

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

main(argc, argv)
char **argv;
{
FILE *in, *out;
int through_loop=0; 		/* Dejavu indicator */
int mode;			/* file's mode (from header) */
long filesize;			/* theoretical file size (from header) */
char dest[128];
char buf[80];

#ifdef AMIGA_LATTICE
extern int  Enable_Abort;
   Enable_Abort=1;
#endif

    /* A filename can be specified to be uudecoded, or nothing can
       be specified, and the input will come from STDIN */

   switch (argc)
      {
      case 1:
         in=stdin;
         break;

      case 2:
         if ((in = fopen(argv[1], "r")) == NULL)
            {
            fprintf(stderr, "ERROR: can't find %s\n", argv[1]);
            fprintf(stderr, "USAGE: uudecode [infile]\n");
            exit(10);
            }
         break;

      default:
         fprintf(stderr, "USAGE: uudecode [infile]\n");
         exit(11);
         break;
      } /* switch (argc) */

    /* Loop through file, searching for headers.  Decode anything with a
       header, complain if there where no headers. */

   for (;;)		/* search file for header line */
      {
      for (;;)
         {
         if (fgets(buf, sizeof buf, in) == NULL)
            {
            if (!through_loop)
               {
               fprintf(stderr, "ERROR: no `begin' line!\n");
               exit(12);
               }
            else
               {
               exit(0);
               }
            }
         if (strncmp(buf, "begin ", 6) == 0)
            break;
         } /* for (;;) */
      sscanf(buf, "begin %o %s", &mode, dest);

#ifdef unix
      /* handle ~user/file format */
      if (dest[0] == '~')
         {
char *sl;
struct passwd *getpwnam();
char *index();
struct passwd *user;
char dnbuf[100];

         sl = index(dest, '/');
         if (sl == NULL)
            {
            fprintf(stderr, "Illegal ~user\n");
            exit(13);
            }
         *sl++ = 0;
         user = getpwnam(dest+1);
         if (user == NULL)
            {
            fprintf(stderr, "No such user as %s\n", dest);
            exit(14);
            }
         strcpy(dnbuf, user->pw_dir);
         strcat(dnbuf, "/");
         strcat(dnbuf, sl);
         strcpy(dest, dnbuf);
         } /* if (dest[0] == '~') */
#endif

      /* create output file */
      if ((out = fopen(dest, "w")) == NULL)
         {
         fprintf(stderr, "ERROR: can't open output file %s\n", dest);
         exit(15);
         }
      else
         fprintf(stdout, "Begin File: %s, Mode: %o ... ", dest, mode);
#ifdef unix
      chmod(dest, mode);
#endif

      decode(in, out, dest);

      if (fgets(buf, sizeof buf, in) == NULL || strncmp(buf,"end",3))
         {       		/* don't be overly picky about newline ^ */
         fprintf(stderr, "ERROR: no `end' line\n");
         exit(16);
         }

      if (!(fgets(buf,sizeof buf,in) == NULL || strncmp(buf,"size ",3)))
         {
         sscanf(buf, "size %ld", &filesize);
         if (ftell(out) != filesize)
            {
            fprintf(stderr,
                    "ERROR: file should have been %ld bytes long but was %ld.\n",
                    filesize, ftell(out));
            exit(17);
            }
         else
            fprintf(stdout, "Size: %ld bytes.\n", filesize);
         }
      through_loop = 1;
      }   /* do (;;) */
}   /* main */

/*
 * Copy from in to out, decoding as you go.
 * If a return or newline is encountered too early in a line, it is
 * assumed that means that some editor has truncated trailing spaces.
 */
decode(in, out, dest)
FILE *in;
FILE *out;
char *dest;
{
char buf[80];
char *bp;
int nosum=0;
#ifndef unix
extern errno;
#endif
register int j;
register int n;
int checksum, line;

   for (line = 1; ; line++)	/* for each input line */
      {
      if (fgets(buf, sizeof buf, in) == NULL)
         {
         fprintf(stderr, "ERROR: input ended unexpectedly!\n");
         exit(18);
         }

/* Pad end of lines in case some editor truncated trailing spaces */

      for (n=0;n<79;n++)  	/* search for first \r, \n or \000 */
         {
         if (buf[n]=='\r'||buf[n]=='\n'||buf[n]=='\000')
            break;
         }
      for (;n<79;n++)     	/* when found, fill rest of line with space */
         {
         buf[n]=' ';
         }
      buf[79]=0;    		/* terminate new string */

      checksum = 0;
      n = DEC(buf[0]);
      if (n <= 0)
         break; 		/* 0 bytes on a line??Must be the last line */

      bp = &buf[1];

/* FOUR input characters go into each THREE output charcters */

      while (n >= 4)
         {
         j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4; putc(j, out); checksum += j;
         j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2; putc(j, out); checksum += j;
         j = DEC(bp[2]) << 6 | DEC(bp[3]);   putc(j, out); checksum += j;
         checksum = checksum % SUMSIZE;
         bp += 4;
         n -= 3;
         }

      j = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4;
      checksum += j;
      if (n >= 1)
         putc(j, out);
      j = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2;
      checksum += j;
      if (n >= 2)
         putc(j, out);
      j = DEC(bp[2]) << 6 | DEC(bp[3]);
      checksum += j;
      if (n >= 3)
         putc(j, out);
      checksum = checksum % SUMSIZE;
      bp += 4;
      n -= 3;

#ifndef unix
 /* Error checking under UNIX??? You must be kidding... */
 /* Check if an error occured while writing to that last line */
      if (errno)
         {
         fprintf(stderr, "ERROR: error writing to %s\n",dest);
         exit(19);
         }
#endif

/* The line has been decoded; now check that sum */

      nosum |= !isspace(*bp);
      if (nosum)			/* Is there a checksum at all?? */
         {
         if (checksum != DEC(*bp))	/* Does that checksum match? */
            {
            fprintf(stderr, "ERROR: checksum mismatch decoding %s, line %d.\n",
                    dest, line);
            }
         }/* sum */
      }/* line */
}   /* function */

#ifdef unix
/*
 * Return the ptr in sp at which the character c appears;
 * 0 if not found
 */
char *
index(sp, c)
register char *sp, c;
{
   do
      {
      if (*sp == c)
         return(sp);
      }
      while (*sp++);

   return(0);
}
#endif unix
