/* Written by Mark Horton */
/* Modified by ajr (Alan J Rosenthatl,flaps@utcsri.UUCP) to use checksums */
/* Modified by fnf (Fred Fish,well!fnf) to use Keith Pyle's suggestion for
   compatibility */
/* Modified by bcn (Bryce Nesbitt,ucbvax!cogsci!bryce) to fix a very
   misleading error message on the Amiga port, enable CTRL-C for LATTICE,
   and add a transparant file size trailer for later check. */
/* Modified by Ueli Kaufmann (Ueli_Kaufmann@augs1.adsp.sub.org) for use
   in xprascii.library */


#include <ctype.h>

LONG AtoL(UBYTE *);
extern void sscanf(UBYTE *, UBYTE *, ...);


#define SUMSIZE 64  /* 6 bits */
/* ENC is the basic 1 character encode function to make a char printing */
/* Each output character represents 6 bits of input */
#define ENC(c) ((c) ? ((c) & '\077') + ' ': '`')

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

VOID __stdargs upderr(struct XPR_IO *io, UBYTE *err, ...);



VOID uuencode_header(UBYTE *name, UBYTE *buf)
{
	sprintf(buf, "\nbegin 644 %s\n", name);
}


VOID uuencode_footer(UBYTE *buf, ULONG size)
{
	sprintf(buf, "end\nsize %ld\n", size);
}


STATIC UBYTE outdec(UBYTE *buf_source, UBYTE *buf_dest)
{
	buf_dest[0] = ENC( (buf_source[0] >> 2));
	buf_dest[1] = ENC(((buf_source[0] << 4) & '\060' | (buf_source[1] >> 4) & '\017'));
	buf_dest[2] = ENC(((buf_source[1] << 2) & '\074' | (buf_source[2] >> 6) & '\003'));
	buf_dest[3] = ENC(( buf_source[2] & '\077'));

	return((UBYTE)((buf_source[0] + buf_source[1] + buf_source[2]) % SUMSIZE));
}


VOID uuencode(UBYTE *buf_source, UBYTE *buf_dest, UBYTE *temp, UBYTE len)
{
	UBYTE checksum, i, j;

	checksum = 0;
	for(i=0, j=0; i<len; i+=3, j+=4)
		checksum = (checksum + outdec(&buf_source[i], &temp[j])) % SUMSIZE;
	temp[j] = '\0';

	sprintf(buf_dest, "%lc%s%lc\n", ENC(len), temp, ENC(checksum));
}










struct my_fgets_data {
	ULONG inbuf;
	ULONG bufpos;
};




STATIC UBYTE *my_fgets(struct XPR_IO *io, UBYTE *buf_dest, ULONG maxlen, UBYTE *buf_source, VOID *fp_source, struct my_fgets_data *myfgd)
{
	ULONG len, limit;
	UBYTE *ptr;

	VOID kprintf(UBYTE *, ...);


	if(myfgd->inbuf < maxlen)
	{
		if(myfgd->inbuf)
			movmem(buf_source + myfgd->bufpos, buf_source, 1024 - myfgd->bufpos);

		myfgd->inbuf += io->xpr_fread(buf_source + myfgd->inbuf, 1, 1024 - myfgd->inbuf, fp_source);

		myfgd->bufpos = 0;

		buf_source[myfgd->inbuf] = '\0';
	}

	if(myfgd->inbuf == 0)
		return(NULL);

	ptr = buf_source + myfgd->bufpos;
	len = 0;
	for(;;)
	{
		len++;
		if(ptr[0] == '\n'  ||  ptr[0] == '\r'  ||  ptr[0] == '\0')
		{
			if(ptr[1] == '\n'  ||  ptr[1] == '\r')
				len++;

			break;		
		}

		ptr++;
	}


	if(len == 0)
		return(NULL);

	limit = (myfgd->inbuf > maxlen) ? maxlen : myfgd->inbuf;

	if(len > limit)
		len = limit;

	memcpy(buf_dest, buf_source + myfgd->bufpos, len);

	buf_dest[len] = '\0';
	myfgd->inbuf  -= len;
	myfgd->bufpos += len;

	return(buf_dest);
}


STATIC VOID uudecode(struct XPR_IO *io, UBYTE *fileName, UBYTE *buf_source, UBYTE *buf_dest, VOID *fp_source, VOID *fp_dest, struct my_fgets_data *myfgd)
{
	ULONG line;
	LONG n;
	BOOL nosum=FALSE;
	UBYTE *buf_destptr, *bp, c, checksum;

	for(line=0; ; line++)	/* for each input line */
	{
		if((buf_destptr = my_fgets(io, buf_dest, 80, buf_source, fp_source, myfgd)) == NULL)
		{
			upderr(io, "Input ended unexpectedly!");
			return;
		}

		for(n=0; n<79; n++)	/* search for first \r, \n or \0 */
		{
			if(buf_destptr[n]=='\r' || buf_destptr[n]=='\n' || buf_destptr[n]=='\0')
				break;
		}

		for(; n<79; n++)		/* when found, fill rest of line with space */
			buf_destptr[n] = ' ';

		buf_destptr[79] = '\0';		/* terminate new string */

		checksum = 0;
		n = DEC(buf_destptr[0]);

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

		bp = &buf_destptr[1];


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

		while(n >= 4)
		{
			c = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4;
			checksum += c;
			*buf_destptr++ = c;

			c = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2;
			checksum += c;
			*buf_destptr++ = c;

			c = DEC(bp[2]) << 6 | DEC(bp[3]);
			checksum += c;
			*buf_destptr++ = c;

			checksum = checksum % SUMSIZE;
			bp += 4;
			n -= 3;
		}

		c = DEC(bp[0]) << 2 | DEC(bp[1]) >> 4;
		checksum += c;
		if(n >= 1)
			*buf_destptr++ = c;

		c = DEC(bp[1]) << 4 | DEC(bp[2]) >> 2;
		checksum += c;
		if(n >= 2)
			*buf_destptr++ = c;

		c = DEC(bp[2]) << 6 | DEC(bp[3]);
		checksum += c;
		if(n >= 3)
			*buf_destptr++ = c;

		checksum = checksum % SUMSIZE;
		bp += 4;
		n -= 3;

      io->xpr_fwrite(buf_dest, 1, buf_destptr - buf_dest, fp_dest);

	/* 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? */
				upderr(io, "checksum mismatch decoding %s, line %ld.", fileName, line);
		}
	}
}


BOOL ProcessUUeFile(struct XPR_IO *io, UBYTE *buf_source, UBYTE *buf_dest, VOID *fp_source)
{
	struct XPR_UPDATE xpru;
	struct my_fgets_data myfgd;
	VOID *fp_dest=0;
	LONG fileMode;		/* ignored.. */
	BOOL thruLoop=FALSE;	/* Dejavu indicator */
	BOOL success;
	UBYTE *buf_destptr, fileName[34], msgBuf[80];

	success = TRUE;

	myfgd.inbuf = 0;
	myfgd.bufpos= 0;

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


	for(;;)
	{
		xpru.xpru_updatemask = XPRU_MSG | XPRU_FILENAME;
		xpru.xpru_msg        = "";
		xpru.xpru_filename   = "";
		io->xpr_update(&xpru);


		for(;;)		/* search file for header line */
		{
			if((buf_destptr = my_fgets(io, buf_dest, 80, buf_source, fp_source, &myfgd)) == NULL)
			{
				if(thruLoop == FALSE)
					upderr(io, "No `begin' line!");
				return(FALSE);
			}

			if(strncmp(buf_destptr, "begin ", 6) == 0)
				break;
		}

		sscanf(buf_destptr, "begin %o %s", &fileMode, fileName);

/*
**		UBYTE *tmpptr;
**
**	/*	sscanf()-replacement	*/
**
**		tmpptr = buf_destptr;		/* search file for file name */
**		while(iscntrl(tmpptr[1]) == FALSE)
**			tmpptr++;
**		tmpptr[1] = '\0';
**
**		while(isspace(tmpptr[-1]) == FALSE)
**			tmpptr--;
**
**		strcpy(fileName, tmpptr);
*/

		if((fp_dest = io->xpr_fopen(fileName, "w")) == NULL)		/* create output file */
		{
			upderr(io, "Cannot open output file `%s'", fileName);
			success = FALSE;
			break;
		}
		else
			success = TRUE;


		xpru.xpru_updatemask = XPRU_MSG | XPRU_FILENAME;
		sprintf(msgBuf, "Starting uudecode %s", io->xpr_filename);
		xpru.xpru_msg        = msgBuf;
		xpru.xpru_filename   = fileName;
		io->xpr_update(&xpru);

		uudecode(io, fileName, buf_source, buf_dest, fp_source, fp_dest, &myfgd);

		io->xpr_fclose(fp_dest);
		fp_dest = NULL;

		if((buf_destptr = my_fgets(io, buf_dest, 80, buf_source, fp_source, &myfgd)) == NULL  ||
								strncmp(buf_destptr, "end", 3))
		{
			success = FALSE;
			upderr(io, "No `end' line");	/* don't be overly picky about newline ^ */
			break;
		}

		if(!((buf_destptr = my_fgets(io, buf_dest, 80, buf_source, fp_source, &myfgd)) == NULL  ||
									strncmp(buf_destptr, "size ", 3)))
		{
			ULONG total, fileSize;

			total = io->xpr_finfo(fileName, 1);

/*
**			sscanf()-replacement
**			filesize = AtoL(&buf_destptr[5]);
*/

			sscanf(buf_destptr, "size %ld", &fileSize);
			if(total != fileSize)
			{
				success = FALSE;
				upderr(io, "Filesize error (should=%ld  is=%ld)", fileSize, total);
				break;
			}
		}
		thruLoop = TRUE;
	}

	if(fp_dest != NULL)
		io->xpr_fclose(fp_dest);

	return(success);
}


/* end of source-code */
