/*
** $VER: FSCode.c (22.5.95) by Flavio Stanchina
** Loc. Montevaccino, 39
** 38040 Trento (Italy)
** 2:333/408.9@fidonet.org
*/

#include <exec/types.h>
#include <exec/alerts.h>
#include <exec/memory.h>
#include <dos/dos.h>
#include <dos/dosasl.h>
#include <dos/dosextens.h>
#include <dos/rdargs.h>
#include <dos/stdio.h>

#include <clib/exec_protos.h>
#include <clib/utility_protos.h>
#include <clib/dos_protos.h>

#include <ctype.h>
#include <string.h>

#if defined(__SASC)
#define _USEOLDEXEC_
#include <proto/exec.h>
#include <proto/utility.h>
#include <proto/dos.h>
#endif

#include "FSCode.h"
#include "FSCode_rev.h"
#include "CRC32.h"

/***** Strings *****/
TEXT StartFmt[] = "!start %s\n";
TEXT MultiFmt[] = "!mstrt %ld/%ld %s\n";
TEXT   EndFmt[] = "!end %ld %lx\n\n";
TEXT  NameFmt[] = "%s%ld";

TEXT Template[] = "FILE/A,TO,E=ENCODE/S,M=MULTI/S,L=LINES/N" VERSTAG " by Flavio Stanchina";

LONG FSCode(VOID)
{
	struct RDArgs  *rda;
	struct FSData  *fsd;
	struct Process *pr;
	ULONG tmp;

	if (fsd = AllocMem(sizeof(struct FSData), MEMF_CLEAR))
	{
		/* Open libraries (and make sure we're on OS 2.04 or greater) */
		if ((DOSBase = (struct DosLibrary *)OpenLibrary("dos.library", 37)) == NULL)
		{
			Alert(AT_Recovery | AG_OpenLib | AO_DOSLib);
			return RETURN_FAIL + 0;
		}

		if ((UtilityBase = OpenLibrary("utility.library", 37)) == NULL)
		{
			Alert(AT_Recovery | AG_OpenLib | AO_UtilityLib);
			return RETURN_FAIL + 1;
		}

		pr = (struct Process *)FindTask(NULL);
		if (pr->pr_CES == 0)
		{
			struct CommandLineInterface *cli = Cli();

			if (cli && cli->cli_StandardOutput)
				fsd->StdErr = cli->cli_StandardOutput;
			else fsd->StdErr = pr->pr_COS;
		}
		else fsd->StdErr = pr->pr_CES;

		if (rda = ReadArgs(Template, (LONG *)fsd, NULL))
		{
			/* Set up CRC table */
			CRC32_init();

			/* Encode or decode? */
			if (fsd->Encode)
			{
				if (fsd->In = Open(fsd->File, MODE_OLDFILE))
				{
					if ((fsd->RealSize = FileSizeFH(fsd, fsd->In)) > 0)
					{
						if (fsd->Multi)
						{
							fsd->Lines = 100;

							if (fsd->pLines)
							{
								if (*fsd->pLines < 2)
									FPuts(fsd->StdErr, "too few LINES -- using default value\n");
								else fsd->Lines = *fsd->pLines;
							}

							// fsd->Parts = (((fsd->RealSize - 1) / 60) + fsd->Lines - 1) / fsd->Lines;
							tmp = UDivMod32(fsd->RealSize - 1, 60);
							fsd->Parts = UDivMod32(tmp + fsd->Lines - 1, fsd->Lines);
							if (fsd->Parts <= 1) fsd->Multi = 0; // easier
						}

						switch (Encode(fsd))
						{
							case -3:
								MyPrintFault(fsd, ERROR_BREAK, NULL);
								break;
						}
					}
					else FPrintf(fsd->StdErr, "file \"%s\" is empty\n", fsd->File);

					MyClose(fsd, fsd->In);
				}
				else MyPrintFault(fsd, IoErr(), fsd->File);
			}
			else 
			{
				if (fsd->pLines)
					FPuts(fsd->StdErr, "LINES is meaningless in decode mode -- ignored\n");

				if (OpenPart(fsd))
				{
					switch(Decode(fsd))
					{
						case '!':
							FPuts(fsd->StdErr, "illegal '!' -- file corrupt\n");
							break;
						case -1:
							FPuts(fsd->StdErr, "unexpected EOF\n");
							break;
						case -2:
							MyPrintFault(fsd, IoErr(), fsd->To);
							break;
						case -3:
							MyPrintFault(fsd, ERROR_BREAK, NULL);
							break;
					}

					ClosePart(fsd, 0); // flush the output file
					// NOTE: This is meaningful only in case of errors, 'cause if
					// all went ok the file has already been closed when the !end
					// line has been encountered.
				}

				/* Did we decode anything at all? */
				if (fsd->NumFound == 0)
					FPuts(fsd->StdErr, "no start line\n");

				/* Make sure files are closed. Should hit these only in case of errors. */
				if (fsd->In ) MyClose(fsd, fsd->In );
				if (fsd->Out) MyClose(fsd, fsd->Out);
			}

free:
			FreeArgs(rda);
		}
		else MyPrintFault(fsd, IoErr(), NULL);

		CloseLibrary((struct Library *)DOSBase);
		CloseLibrary((struct Library *)UtilityBase);

		FreeMem(fsd, sizeof(struct FSData));
	}
	else Alert(AT_Recovery | AG_NoMemory);

	return RETURN_OK;
}

/***** Restituisce la lunghezza di un file già aperto *****/
LONG FileSizeFH(struct FSData *fsd, BPTR fh)
{
	struct FileInfoBlock *fib;
	LONG size = -1;

	if (fib = AllocDosObject(DOS_FIB, NULL))
	{
		if (ExamineFH(fh, fib))
			size = fib->fib_Size;
		FreeDosObject(DOS_FIB, fib);
	}

	return size;
}

/***** Print fault on given file *****/
VOID MyPrintFault(struct FSData *fsd, LONG code, STRPTR header)
{
	static TEXT buffer[256];

	Fault(code, header, buffer, 256);
	FPrintf(fsd->StdErr, "\a%s\n", buffer);
	Flush(fsd->StdErr);
}

/***** Close file and check for errors *****/
BOOL MyClose(struct FSData *fsd, BPTR fh)
{
	BOOL rc;

	if ((rc = Close(fh)) == 0)
		MyPrintFault(fsd, IoErr(), "error closing file");

	return rc;
}

/***** Get a decimal number from a string *****/
LONG GetDec(STRPTR *line)
{
	LONG l = 0;

	while (!isdigit(*(*line))) (*line)++;
	while ( isdigit(*(*line))) l = (l * 10) + (*(*line)++ - '0');

	return l;
}

/***** Get a hexadecimal number from a string *****/
static LONG h2l(LONG c)
{
	if (c >= 'a') return c - 'a' + 10; // 'f' > 'F'
	if (c >= 'A') return c - 'A' + 10; // 'F' < 'f'
	return c - '0';
}

LONG GetHex(STRPTR *line)
{
	LONG l = 0;

	while (!isxdigit(*(*line))) (*line)++;
	while ( isxdigit(*(*line))) l = (l * 16) + h2l(*(*line)++);

	return l;
}

/***** Remove leading spaces and trailing control characters *****/
VOID TrimLine(STRPTR *line)
{
	register i;

	while (isspace(*(*line))) (*line)++;
	for (i = 0; (*line)[i] & 0x60; i++);
	(*line)[i] = '\0';
}

VOID SPrintf(STRPTR buf, STRPTR fmt, ...)
{
	RawDoFmt(fmt, (APTR)(&fmt + 1), stuff, buf);
}

/***** EOF *****/
