/*
 * $Id: uudecode.c,v 36.9 1994/09/28 15:03:59 zodiac Rel zodiac $
 *
 * uudecode 36.9 -- Copyright (c) 1994 Ralph Seichter.
 *
 * Decodes binary files encoded with uuencode. This tool is pure.
 *
 * This tool is FREELY DISTRIBUTABLE. Copying and spreading is encouraged,
 * as long as you don't charge anyone for giving him/her this tool. Please
 * note that the Copyright is mine, this is *NOT* public domain software!
 * Permission is hereby granted to include the complete (!) archive in all
 * non-profit public domain software series like Fred Fish, SaarAG, etc.
 *
 * $Log: uudecode.c,v $
 * Revision 36.9  1994/09/28  15:03:59  zodiac
 * Files with IBM-style EOL sequences (CR/LF pairs) are now
 * processed correctly. This was suggested by Patrick Ohly.
 *
 * Revision 36.8  1994/09/28  14:18:47  zodiac
 * First release with 'pure' executables.
 *
 */

#define	VERSION		"\0$VER: uudecode 36.9 (28.9.94)"
#define	TEMPLATE		"FROM/A/M,PATH=TO/K,NC=NOCHECKSUM/S,QUIET/S"

#define	NO_DATA_FOUND	-3
#define	BAD_CHECKSUM	-4
#define	LINESIZE		128
#define	SIXBIT		0x40				/* 6 bit = 0x40 */

enum { ARG_FROM, ARG_PATH, ARG_NOCHKSUM, ARG_QUIET, ARG_TOTAL };

#define	DECODE(c) ((c - 0x20) & 0x3F)		/* decode one single byte */
#define	ISEOL(c) (c == '\n' || c == '\r')	/* check for LF or CR */



/* function prototypes ******************************************************/

LONG entryPoint (VOID);
LONG decodeFile (struct DosLibrary *, struct Library *, STRPTR *, BPTR, BOOL);



/* the main program *********************************************************/

LONG __saveds entryPoint (VOID)
{
	LONG rc = RETURN_FAIL;
	struct DosLibrary *DOSBase;

	/* uudecode will *NOT* run without dos.library V36 or better! */
	if (DOSBase = (struct DosLibrary *)OpenLibrary (DOSNAME, 36))
	{
		struct Library *UtilityBase;

		if (UtilityBase = OpenLibrary ("utility.library", 37))
		{
			struct RDArgs *rda;
			STRPTR arg[ARG_TOTAL];

			rc = RETURN_ERROR;
			memset (arg, 0, sizeof (arg));
			if (rda = ReadArgs (TEMPLATE, (LONG *)arg, NULL))
			{
				LONG i, err;
				BPTR in;
				STRPTR *name = (STRPTR *)arg[ARG_FROM];
				static UBYTE ver[] = VERSION;

				if (!arg[ARG_QUIET])
					Printf ("%s Copyright \xA9 Ralph Seichter\n", &ver[7]);
				for (err = 0, i = 0; err == 0 && (name[i]); ++i)
					if (in = Open (name[i], MODE_OLDFILE))
					{
						BPTR oldPath, path = NULL;

						if (arg[ARG_PATH] && (path = Lock (arg[ARG_PATH], SHARED_LOCK)))
							oldPath = CurrentDir (path);
						if (!arg[ARG_QUIET])
							Printf ("Processing input file %s\n", name[i]);
						err = decodeFile (DOSBase, UtilityBase, arg, in, (BOOL)arg[ARG_NOCHKSUM]);
						if (path)
						{
							CurrentDir (oldPath);
							UnLock (path);
						}
						Close (in);
					}
					else
						break;
				if (err <= NO_DATA_FOUND)
					rc = RETURN_WARN;
				else if (err > 0 || (err = IoErr ()) > 0)
					PrintFault (err, NULL);
				else
					rc = RETURN_OK;
				FreeArgs (rda);
			}
			CloseLibrary (UtilityBase);
		}
		CloseLibrary ((struct Library *)DOSBase);
	}
	return (rc);
}



/* decode a complete file ***************************************************/

LONG decodeFile (struct DosLibrary *DOSBase, struct Library *UtilityBase, STRPTR *arg, BPTR in, BOOL ignoreChecksum)
{
	BOOL done, gotBegin = FALSE, badChecksum = FALSE;
	LONG line = 0;
	UBYTE buf[LINESIZE], outbuf[LINESIZE];
	static UBYTE error[] = "\7\23331;42m ERROR \2330m -- ";
	static UBYTE warning[] = "\23331;42m WARNING \2330m -- ";
	STRPTR s, t;

	do
	{
		done = TRUE;

		/* find the next `begin' line */
		while (s = FGets (in, buf, LINESIZE - 1))
		{
			++line;
			if (0 == Strnicmp (buf, "begin ", 6))
			{
				gotBegin = TRUE;
				/* skip over `begin xyz ' to find the file name. */
				t = s = FilePart (&buf[10]);
				while (!ISEOL(*t))
					++t;
				*t = 0;
				break;
			}
		}

		/* is there a file to decode? */
		if (s)
		{
			BPTR out;

			if (out = Open (s, MODE_NEWFILE))
			{
				LONG expected = ~0, size = 0;

				if (!arg[ARG_QUIET])
					Printf ("Line %ld: decoding file %s\n", line, s);
				memset (buf, 0, LINESIZE);
				while (s = FGets (in, buf, LINESIZE - 1))
				{
					++line;
					if (0 == Strnicmp (buf, "end", 3) && ISEOL(buf[3]))
					{
						if (FGets (in, buf, LINESIZE - 1))
						{
							++line;
							if (0 == Strnicmp (buf, "size ", 5))
								StrToLong (&buf[5], &expected);
						}
						break;
					}
					else
					{
						LONG l, c, checksum = 0;

						t = outbuf;
						c = *s++;
						l = DECODE(c);
						if (c != '\n' && l > 0)
						{
							/* decode one line. four input bytes will
								result in three output bytes. */
							while (l >= 4)
							{
								c = DECODE(s[0]) << 2 | DECODE(s[1]) >> 4;
								checksum += c;
								*t++ = c;

								c = DECODE(s[1]) << 4 | DECODE(s[2]) >> 2;
								checksum += c;
								*t++ = c;

								c = DECODE(s[2]) << 6 | DECODE(s[3]);
								checksum += c;
								*t++ = c;

								checksum = checksum % SIXBIT;
								s += 4;
								l -= 3;
							}
							c = DECODE(s[0]) << 2 | DECODE(s[1]) >> 4;
							checksum += c;
							if (l >= 1)
								*t++ = c;
							c = DECODE(s[1]) << 4 | DECODE(s[2]) >> 2;
							checksum += c;
							if (l >= 2)
								*t++ = c;
							c = DECODE(s[2]) << 6 | DECODE(s[3]);
							checksum += c;
							if (l >= 3)
								*t++ = c;
							checksum = checksum % SIXBIT;
							s += 4;
							if (!ignoreChecksum && *s >= 0x20 && *s <= 0x60 && checksum != DECODE(*s))
							{
								badChecksum = TRUE;
								if (!arg[ARG_QUIET])
									Printf ("Line %ld: %sbad checksum!\n", line, error);
								break;
							}
							l = t - outbuf;
							if (1 == FWrite (out, outbuf, l, 1))
								size += l;
							else
								break;
						}
					}
				}
				if (s)
				{
					done = FALSE;
					if (!arg[ARG_QUIET])
					{
						if (expected != ~0 && expected != size)
							Printf ("Line %ld: %sfile size mismatch!\n%ld bytes expected, ", line, warning, expected);
						Printf ("%ld bytes written.\n", size);
					}
				}
				else if (!arg[ARG_QUIET])
					Printf ("Line %ld: %sunexpected end of file!\n", line, error);
				Close (out);
			}
		}
	}
	while (!done);
	if (badChecksum)
		return (BAD_CHECKSUM);
	else if (gotBegin)
		return (IoErr ());
	else
	{
		if (!arg[ARG_QUIET])
			Printf ("%sno encoded data found.\n", warning);
		return (NO_DATA_FOUND);
	}
}

/* EOF */
