/************************************
 *												*
 *	Module: Command	            *
 *												*
 *	Command-Interpreter for     *
 *	Editor.											*
 ************************************/

/*#FOLD:	Includes	*/
/************
 *
 * Includes:
 *
 ************/

#include <stdio.h>
#include <exec/types.h>
#include <intuition/intuition.h>
#include "src/Editor.h"
/*#ENDFD*/
/*#FOLD:	Defines */
/***********
 *
 * Defines:
 *
 ***********/

#define TAB 9
#define LF 10
#define CR 13

#define UC(c) (((c >= 'a')	&&	(c	<=	'z'))? (c &	0xDF)	: c)
#define DIGIT(c) ((c >= '0') && (c <= '9'))
#define SKIPBLANKS(str) while (*str == ' ') str++;
/*#ENDFD*/
/*#FOLD:	External Functions */
/**********************
 *
 * External Functions:
 *
 **********************/

BOOL cursorLeft(),cursorRight(),cursorUp(),cursorDown(),cursorHome();
BOOL cursorEnd(),deleteChar(),deleteLine(),backspaceChar(),insertLine();
BOOL saveLine();
void printAll(),printFlags(),fclose(),Insert(),strncpy(),printNumLines();
void restoreZlinenptr(),SetWindowTitles(),initFolding(),printYpos();
void DisplayBeep(),saveIfCursorMoved(),CopyMem();
struct Zline *newZline(),*prevLine(),*nextLine();
FILE *fopen();
int fwrite(),getc();
UBYTE *getLastWord();
ULONG fseek();
UWORD recalcTopOfWindow();
/*#ENDFD*/
/*#FOLD:	External Variables	*/
/*********************
 *
 * External Variables:
 *
 *********************/ 

extern struct Editor *actualEditor;
extern struct MsgPort *edUserPort;
/*#ENDFD*/
/*#FOLD:	Globale Variables	*/
/*********************
 *
 * Globale Variables:
 *
 *********************/ 

struct jmpEntry
{
	UBYTE c;
	UBYTE *(*fkt)();
};
/*#ENDFD*/

/***************
 *	       *
 * Functions:  *
 *	       *
 ***************/

/*#FOLD: saveASCII */
/*********************************
 *
 * saveASCII(name)
 *
 * Save text under name to
 * Diskette .
 *
 * name ^ File name.
 *
 *********************************/

BOOL saveASCII(name)
UBYTE			  *name;
{
	register FILE *file;
	register struct Zline *z;

	if	(actualEditor->bufypos)
		if	(!	saveLine(actualEditor->actual))
		{
			DisplayBeep(NULL);
			return (FALSE);
		}

	if	(file	= fopen(name,"w"))
	{
		z = actualEditor->zlines.head;
		while (z->succ)
		{
			if (z->len)
				if (fwrite(z + 1,(int)z->len,1,file) != 1)
				{
					/* Error! */
					fclose (file);
					return (FALSE);
				}

			z = z->succ;
		}

		actualEditor->changed = 0;
		printFlags();
		fclose (file);
		return (TRUE);
	}
	else
		return (FALSE);
}
/*#ENDFD*/
/*#FOLD: loadASCII */
/***********************************
 *
 * loadASCII(name)
 *
 * Load text from disk. The text
 * is inserted after the actual line
 * in the text.
 *
 * name ^ File name.
 *
 ***********************************/

BOOL loadASCII(name)
UBYTE			  *name;
{
	UBYTE buf[MAXWIDTH];
	register UBYTE *ptr,*tab;
	int c;
	UWORD rm = actualEditor->rm;
	register FILE *file;
	register UWORD len;
	register struct Zline *z,*zn;

	if (file = fopen(name,"r"))
	{
		/* display name in title line: */
		strncpy(actualEditor->filename,name,
				  sizeof(actualEditor->filename));
		SetWindowTitles(actualEditor->window,actualEditor->filename,-1L);

		if	(z	= ZLINESPTR(actualEditor->wdy))
		{
			actualEditor->changed =	1;
			printFlags();
		}

		while	(!feof(file))
		{
			tab = actualEditor->tabstring;
			ptr = buf;
			len = 0;

			/* read one line: */
			while (!feof(file) && (len < rm))
			{
				if ((c = getc(file)) == EOF)
					break;
				else
					*ptr++ = (UBYTE)c;

				if ((c == TAB) && (actualEditor->tabs))
					do
					{
						tab++;
						len++;
					} while ((*tab) && (len < rm));
				else
				{
					tab++;
					len++;

					if (c == LF)
						break;
					else if (c == CR)
						if (!feof(file) && (len < rm))
						{
							if ((*ptr = (UBYTE)getc(file)) == LF)
								ptr++;
							else
								/* set file pointer back: */
								fseek(file,-1L,1);

							break;
						}
				}	/* of if (TAB) */
			}		/* of while */

			if (len = ptr - buf)
			{
				/* Wordwrap? */
				if (!actualEditor->skipblanks)
					if ((len >= rm) && (c != CR) && (c != LF))
					{
						tab = getLastWord(buf,len);
						fseek(file,(long)tab - ptr,1);
						len = (ptr = tab) - buf;
					}

				/* save line now: */
				actualEditor->lineptr =	z;	/* Garbage-Collection	*/
				if (zn = newZline(len))
				{
					z = actualEditor->lineptr;
					zn->len = len;
					CopyMem(buf,zn	+ 1,(ULONG)len);
					actualEditor->num_lines++;
					Insert(&actualEditor->zlines,zn,z);
					z = zn;
					actualEditor->lineptr =	NULL;
				}
				else
				{
					actualEditor->lineptr =	NULL;
					fclose (file);
					return (FALSE);
				}
			}
		}

		/* calculate fold level: */
		initFolding();

		/* redisplay window: */
		if ((ZLINESPTR(0) == NULL) && (actualEditor->zlines.head->succ))
		{
			ZLINESPTR(0) = actualEditor->zlines.head;
			ZLINESNR(0)  = 1;
		}
		restoreZlinenptr();
		printAll();
		printNumLines();

		fclose (file);
		return (TRUE);
	}
	else
		return (FALSE);
}
/*#ENDFD*/

/*************************************************
 *
 * command-Functions:
 *
 * This processes a function group
 *
 * All functions return a pointer to 
 * the next command, if no error
 * is encountered, otherwise a negative pointer
 * to the faulty character is returned.
 *
 * Returna a NULL, so this 
 * serves as break criteria.
 *
 * A -1 ends the program.
 *
 *************************************************/

/*#FOLD: commandASCII */
UBYTE *commandASCII(str)
register UBYTE		 *str;
{
	UBYTE *error = 1L - str,buf[80];
	register UBYTE *name;

	switch (UC(*str))
	{
		case 'L':
			str++;
			SKIPBLANKS(str)
			if (*str == '"')
			{
				/* take file name from command line */
				str++;
				name = buf;
				while ((*name = *str) && (*str != '"')
						&& (name < buf + sizeof(buf) - 1))
				{
					name++;
					str++;
				}

				*name = 0;
				if (*str == '"')
					str++;

				name = buf;
			}
			else
				/* use file name form load: */
				name = actualEditor->filename;

			if (!loadASCII(name))
				return (error);

			break;
		case 'S':
			str++;
			SKIPBLANKS(str)
			if (*str == '"')
			{
				/* get file name from command line */
				str++;
				name = buf;
				while ((*name = *str) && (*str != '"')
						&& (name < buf + sizeof(buf) - 1))
				{
					name++;
					str++;
				}

				*name = 0;
				if (*str == '"')
					str++;

				name = buf;
			}
			else
				/* use file name from load: */
				name = actualEditor->filename;

			if (!saveASCII(name))
				return (error);

			break;

		default:
			return (NULL - str);
	}

	return (str);
}
/*#ENDFD*/
/*#FOLD: commandCursor */
UBYTE *commandCursor(str)
register UBYTE		  *str;
{
	switch (UC(*str))
	{
		case 'R':
			if (!cursorRight())
				return (NULL);
			break;
		case 'L':
			if (!cursorLeft())
				return (NULL);
			break;
		case 'U':
			if (!cursorUp())
				return (NULL);
			break;
		case 'D':
			if (!cursorDown())
				return (NULL);
			break;
		case 'E':
			if (!cursorEnd())
				return (NULL);
			break;
		case 'S':
		case 'H':
			if (!cursorHome())
				return (NULL);
			break;
		case 'N':
			if (!cursorDown())
				return (NULL);
			if (!cursorHome())
				return (NULL);
			break;
		case 'P':
			if (!cursorUp())
				return (NULL);
			if (!cursorHome())
				return (NULL);
			break;
		case 'T':
			if (!cursorTop())
				return (NULL);
			break;
		case 'B':
			if (!cursorBottom())
				return (NULL);
			break;

		default:
			return (NULL - str);
	}

	return (str + 1);
}
/*#ENDFD*/
/*#FOLD: commandDelete */
UBYTE *commandDelete(str)
register UBYTE		  *str;
{
	switch (UC(*str))
	{
		case 'L':
			if (! deleteLine())
				return (NULL);
			break;
		case 'C':
			if (! deleteChar())
				return (NULL);
			break;
		case 'B':
			if (! backspaceChar())
				return (NULL);
			break;

		default:
			return (NULL - str);
	}

	return (str + 1);
}
/*#ENDFD*/
/*#FOLD: commandLine */
UBYTE *commandLine(str)
register UBYTE		*str;
{
	switch (UC(*str))
	{
		case 'S':
			if (! insertLine((UBYTE) 0))
				return (1 - str);
			break;

		default:
			return (NULL - str);
	}

	return (str + 1);
}
/*#ENDFD*/
/*#FOLD: commandQuit */
UBYTE *commandQuit(str)
register UBYTE		*str;
{
	return (-1L);
}
/*#ENDFD*/
/*#FOLD: commandSet */
UBYTE *commandSet(str)
register UBYTE	  *str;
{
	switch (*str)
	{
		case 't':
			actualEditor->tabs = 0;
			printAll();
			printFlags();
			break;
		case 'T':
			actualEditor->tabs = 1;
			printAll();
			printFlags();
			break;
		case 'i':
		case 'I':
			actualEditor->insert = 1;
			printFlags();
			break;
		case 'o':
		case 'O':
			actualEditor->insert = 0;
			printFlags();
			break;
		case 'b':
			actualEditor->skipblanks= 0;
			printFlags();
			break;
		case 'B':
			actualEditor->skipblanks= 1;
			printFlags();
			break;
		case 'a':
			actualEditor->autoindent= 0;
			printFlags();
			break;
		case 'A':
			actualEditor->autoindent= 1;
			printFlags();
			break;
		case 'r':
		case 'R':
		{
			register UWORD rm;

			if (*++str == '=')
			{
				str++;
				if	(DIGIT(*str))
				{
					rm = 0;
					while (DIGIT(*str))
					{
						rm = 10*rm + (*str - '0');
						str++;
					}

					if (rm > MAXWIDTH)
						rm = MAXWIDTH;
					else if (rm < 10)
						rm = 10;
				}
				else
					/* Fehler */
					return (NULL - str);
			}
			else
				rm = MAXWIDTH;

			actualEditor->rm = rm;
			--str;
			break;
		}

		default:
			return (NULL - str);
	}

	return (str + 1);
}
/*#ENDFD*/
/*#FOLD: commandExit */
UBYTE *commandExit(str)
register UBYTE		*str;
{
	if (actualEditor->changed)
		if (saveASCII(actualEditor->filename))
			return (-1L);
		else
			return (NULL - str);
	else
		return (-1L);
}
/*#ENDFD*/
/*#FOLD:	commandBracket	*/
UBYTE	*executeCommand();		/* declaration due to recursion! */

UBYTE	*commandBracket(str)
register	UBYTE			*str;
{
	register	UBYTE	*ptr;

	if	(ptr = executeCommand(str))
	{
		if	(ptr != -1L)
			if	(ptr >= 0x80000000)
				/*	Pointer behind command line '}':	*/
				ptr =	1L	- ptr;
			else
				/*	pointer to error: */
				ptr =	NULL - ptr;
	}
	else
	{
		/* search for ending '}':	*/
		ptr =	str;
		while	(*ptr	&&	(*ptr	!=	'}'))
		{
			if	(*ptr	==	'"')
				do
				{
					ptr++;
				} while (*ptr && (*ptr != '"'));

			ptr++;
		}

		if	(*ptr	==	0)
			ptr =	NULL;
		else
			ptr++;
	}

	return (ptr);
}
/*#ENDFD*/

/*#FOLD:	messageWaiting	*/
/*********************************
 *
 * messageWaiting:
 *
 * Test if there is a message
 * in the UserPort.
 *
 * Returns TRUE if yes.
 *
 *********************************/

BOOL messageWaiting()
{
	register struct IntuiMessage *im;

	im = (struct IntuiMessage *)edUserPort->mp_MsgList.lh_Head;
	while (im->ExecMessage.mn_Node.ln_Succ)
	{
		if ((im->Class == RAWKEY)
		&& !(im->Code & IECODE_UP_PREFIX)) return (TRUE);
		if (im->Class == NEWSIZE) return (TRUE);
		im = (struct IntuiMessage *)im->ExecMessage.mn_Node.ln_Succ;
	}

	return (FALSE);
}
/*#ENDFD*/
/*#FOLD: Functions table */
struct jmpEntry jmpTable[] =
{
	'A',&commandASCII,
	'C',&commandCursor,
	'D',&commandDelete,
	'L',&commandLine,
	'Q',&commandQuit,
	'S',&commandSet,
	'X',&commandExit,
	'{',&commandBracket
};
/*#ENDFD*/
/*#FOLD:	executeCommand	*/
/****************************************
 *
 * executeCommand(str)
 *
 * Executes the commands to which str,
 * points. The end is marked by
 * a Null byte.
 *
 * The function returns the following:
 * 0:	 no error
 * -1:	 Program end
 *	else: pointer to error in string.
 *	or:	 negative pointer to '}'
 *
 ****************************************/

UBYTE *executeCommand(str)
register UBYTE			*str;
{
	register UBYTE *ptr,c;
	register UWORD count,n;
	register UBYTE *(*fkt)();

	while (!messageWaiting() && *str)
	{
		SKIPBLANKS(str)

		if	(*str	==	'}')
			return (NULL -	str);

		if (DIGIT(*str))
		{
			count = 0;
			while (DIGIT(*str))
			{
				count = 10*count + (*str - '0');
				str++;
			}

			if	(count == 0)
				count	= 1;
		}
		else
			count = 1;

		/* search function, the command executes: */
		c = UC(*str);
		fkt = NULL;
		for (n = 0; n < sizeof(jmpTable)/sizeof(struct jmpEntry); n++)
			if (c == jmpTable[n].c)
			{
				fkt = jmpTable[n].fkt;
				break;
			}

		if (fkt)
			while (!messageWaiting() && count--)
			{
				if (ptr = (*fkt)(str + 1))
				{
					if (ptr >= 0x80000000)
						if (ptr == -1L)
							/* Program end */
							return (ptr);
						else
							/* Error encountered: */
							return (NULL - ptr);
					else
						/* If count == 0: Next command, */
						/* else repeat */
						if (count == 0)
							str = ptr;
				}
				else
					/* break */
					return (NULL);
			}
		else
			/* Unknown command */
			return (str);

		saveIfCursorMoved();

		SKIPBLANKS(str)

		if (*str == ';')
			str++;
		else if (*str == '}')
			return (NULL -	str);
		else if (*str)
			return (str);
	}

	return (NULL);
}
/*#ENDFD*/
