/*************************************************************************
dialogue.c      -       Collection of dialogue routines for use with
			multi threaded interface

Programmer      -       E.J.Pugh, FIAP

Copyright       -       (C) Edward James Pugh, 12-Jan-1990
**************************************************************************/


#include <stdlib.h>
#include "ibmpc.h"

extern Window_t *CURWINDOW;
extern char DATE_SEPARATOR; /* in screen.c */

DateType_s DATETYPE = ENG; /* default */
int daysin[12] = { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31};

/* global variables for internal use */
static int moves, col, mvs, posn, tstate, tposn, width, no, mod,
year, month, day, p1, p2, p3, nyear, newposn;
static char *source, *target;
static double number;


#define DATA   ((Dialogue_t *)CURWINDOW->Contents)
#define DECIMALS DATA->Decimals
#define SIGNED DATA->Signed
#define COUNT  DATA->Count
#define BUFFER DATA->Buffer
#define BUFLEN DATA->BufLen
#define LAST   DATA->Last
#define BPOSN  DATA->BufPosn
#define OFFSET DATA->Offset
#define SCALAR DATA->Scalar
#define SHOWR(x)  PutString(CURWINDOW, &BUFFER[x], TRUE, (DTYPE == DATE_D))
#define SHOW(x)   PutString(CURWINDOW, &BUFFER[x], FALSE, (DTYPE == DATE_D))
#define KEY event->Input
#define ACCEPT  DATA->Accept
#define LIST    DATA->List
#define STATE   DATA->State
#define LPOSN   DATA->LPosn
#define ITEM    ((char *)LPOSN->item)
#define DTYPE   CURWINDOW->CType
#define SHOWITEM ClearWindow(); PutString(CURWINDOW, ITEM, TRUE, \
(DTYPE == DATE_D))
#define ISLEAP(y)   ((y > 0) && (y % 400 == 0) || ((y % 4 == 0) && \
(y % 100 !=0)))
#define ISDIGIT(x) ((x >= '0') && (x <= '9'))
#define DIGIT(x)   ((int)(x - '0'))
#define CHAR(x) ((char)(x + '0'))
#define ISDSEP(x) ((x == ' ') || (x == '.') || (x == '-') || (x == '/') || \
(x == ':'))

/* function prototypes */
static void DoEntry(char ch);
static BOOLEAN DoEsc(void);
static void DoBack(void);
static BOOLEAN DoRet(void);
static void DoCtrlRight(void);
static void DoCtrlLeft(void);
static void DoDelete(void);
static void GoEnd(void);
static void GoHome(void);
static void GoRight(void);
static void GoLeft(void);
static void ShowItem(void);
static BOOLEAN IsValid(char ch);
static void AdjStateL(void);
static void Insert(char ch);

/*************************************************************************
NewDialogue     -       New Dialogue

Description     -       Creates a Dialogue_t  description
			from the information supplied

Parameters      -       NewDialogue(type, length, accept, signed,
				decimals, list, scalar)
			WObj_s type; STRING_D, INT_D, NUMBER_D, or DATE_D
			int length; of buffer required (including '\0')
			int (*accept)(Dialogue_t *); to acceptance routine;
			BOOLEAN signed; TRUE or FALSE
			int decimals; number of places required
			llist_t *list; for scalar type
			BOOLEAN scalar; TRUE if no input allowed, else FALSE

Returns         -       A void pointer to the new structure if
			successful, else NULL
***************************************************************************/
void *NewDialogue(WObj_s type, int length, int(*accept)(Dialogue_t *ptr),
	BOOLEAN signtype, int decimals, llist_t *list, BOOLEAN scalar)
{
Dialogue_t *ptr;

if(ptr = (Dialogue_t *)malloc(sizeof(Dialogue_t)))
	{
	if(ptr->Buffer = (char *)malloc(length))
		{
		ptr->Buffer[0] = '\0';
		ptr->BufLen = length;
		ptr->Accept = accept;
		ptr->Count = ptr->BufPosn = ptr->Offset = 0;
		ptr->State = list ? 0 : 1 ;
		ptr->Last = -1;
		ptr->List = list;
		ptr->LPosn = list ? list->first : NULL;
		ptr->Signed = signtype;
		ptr->Decimals = decimals;
		ptr->Scalar = ptr->LPosn ? scalar : FALSE;
		}
	else
		{
		free(ptr);
		ptr = NULL;
		}
	}
return((void *)ptr);
}

/**************************************************************************
DoDialogue      -       Do Dialogue

Description     -       Gathers a string of the required type
			into the given window

Parameters      -       DoDialogue(window, mode)
			Window_t *window;
			unsigned int mode; ALLOW_MENU_BAR |
				ALLOW_ALTER_WINDOW

Returns         -       A pointer to the terminating event
**************************************************************************/
Event_t *DoDialogue(Window_t *window, unsigned int mode)
{
Event_t *event;

CURWINDOW = window;
DecDBounds(CURWINDOW);
ResCurs(&CURWINDOW->CursPosn);
if(InsertOn())
	SmallCurs();
else
	LargeCurs();
if(!STATE)
	{
	SHOWITEM;
	}
for(;;)
	{
	SaveCurs(&window->CursPosn);
	IncDBounds(window);
	event = GetEvent(mode | REPORT_EXT_KEY | REPORT_NULL_EVENT);
	DecDBounds(window);
	ResCurs(&window->CursPosn);
	if(InsertOn())
		SmallCurs();
	else
		LargeCurs();
	if(!event)
		continue;
	CURWINDOW = window;
	switch(event->Event)
		{
		case DO_DIALOGUE:
			{
			if(event->Window == window)
				break;
			else
				goto end;
			}
		break;
		case EXT_KEY_EVENT:
			{
			if(event->Class == GREY_KEY)
				{
				switch(event->Key)
					{
			case LEFT:
				{
				if(STATE == -1)
					goto end;
				else
					GoLeft();
				}
			break;
			case RIGHT:
				{
				if(STATE == -1)
					goto end;
				else
					GoRight();
				}
			break;
			case HOME:
				{
				if(STATE == -1)
					goto end;
				else
					GoHome();
				}
			break;
			case END:
				{
				if(STATE == -1)
					goto end;
				else
					GoEnd();
				}
			break;
			case DEL:
				{
				if(STATE == -1)
					goto end;
				else
					DoDelete();
				}
			break;
			case CTRL_LEFT:
				{
				if(STATE == -1)
					goto end;
				else
					DoCtrlLeft();
				}
			break;
			case CTRL_RIGHT:
				{
				if(STATE == -1)
					goto end;
				else
					DoCtrlRight();
				}
			break;
			case SHIFT_TAB:
			case UP:
			case PGUP:
			case DOWN:
			case PGDN:
			case CTRL_END:
			case CTRL_PGDN:
			case CTRL_HOME:
			case CTRL_PGUP:
				goto end;
			break;
					} // end switch
				} // end if Class == GREY_KEY
			}
		break;
		case ASCII_KEY:
			{
			switch(event->Input)
				{
			case '\r':
				{
				if(DoRet())
					{
					event->Event = EXT_KEY_EVENT;
					event->Class = GREY_KEY;
					event->Key = RIGHT;
					goto end;
					}
				}
			break;
			case '\t':
				goto end;
			break;
			case '\b':
				{
				if(STATE == -1)
					goto end;
				else
					DoBack();
				}
			break;
			case '\033':
				{
				if(DoEsc())
					{
					event->Event = EXT_KEY_EVENT;
					event->Class = GREY_KEY;
					event->Key = LEFT;
					goto end;
					}
				}
			break;
			default:
			{
			DoEntry(KEY);
			}
			break;
				} // end switch
			}
		break;
		case ALTER_WINDOW:
		break;
		case DO_BUTTON:
		case DO_CHECK_BOX:
		case DO_SWITCH:
		case CLOSE_WINDOW:
		case CHANGE_WINDOW:
		case DO_LINE:
		case NEW_PROCESS:
		case EXIT_PROGRAM:
			goto end;
		break;
		} // end switch
	} // end continuous loop
end:
SaveCurs(&window->CursPosn);
CursOff();
IncDBounds(window);
return(event);
}

/*************************************************************************
GoLeft          -       Go Left

Description     -       Moves the dialogue cursor left one column

Parameters      -       GoLeft()

Returns         -       NOTHING
*************************************************************************/
static void GoLeft(void)
{
if(!STATE)
	{
	/* back to previous item */
	LPOSN = LPOSN->prev ? LPOSN->prev : LIST->last;
	SHOWITEM;
	}
else if(STATE > 1)
	{
	AdjStateL();
	if(CursL())
		--BPOSN;
	else
		{
		if(OFFSET)
			{
			-- OFFSET;
			SHOWR(OFFSET);
			--BPOSN;
			}
		}
	}
}

/*************************************************************************
GoRight         -       Go Right

Description     -       Moves the dialogue cursor right one column

Parameters      -       GoRight()

Returns         -       NOTHING
*************************************************************************/
static void GoRight(void)
{
if(!STATE)
	{
	/* to next item */
	LPOSN = LPOSN->next ? LPOSN->next : LIST->first;
	SHOWITEM;
	}
else
	{
	if((BPOSN <= LAST) && (BPOSN < BUFLEN) && IsValid(BUFFER[BPOSN]))
		{
		if(CursR())
			; /* do nothing */
		else
			{
			++OFFSET;
			BegLine();
			SHOW(OFFSET);
			}
		++BPOSN;
		if(BPOSN > LAST)
			PutChar(' ', CURWINDOW->Attrib);
		}
	}
}

/*************************************************************************
GoHome          -       Go Home

Description     -       Moves the dialogue cursor to the start of the
			buffer

Parameters      -       GoHome()

Returns         -       NOTHING
**************************************************************************/
static void GoHome(void)
{
if(!STATE)
	{
	LPOSN = LIST->first;
	SHOWITEM;
	}
else
	{
	STATE = 1;
	BPOSN = OFFSET = 0;
	BegLine();
	SHOWR(BPOSN);
	}
}

/*************************************************************************
GoEnd           -       Go End

Description     -       Moves the dialogue cursor to the end of the
			buffer's contents

Parameters      -       GoEnd()

Returns         -       NOTHING
**************************************************************************/
static void GoEnd(void)
{
if(!STATE)
	{
	LPOSN = LIST->last;
	SHOWITEM;
	}
else if(DTYPE == STRING_D)
	{
	moves = LAST - BPOSN + 1;
	col = (int)CURWINDOW->CursPosn.Col;
	mvs = CURWINDOW->Bounds.BCol - col;

	if(mvs >= moves)
		{
		CURWINDOW->CursPosn.Col += (unsigned char)moves;
		ResCurs(&CURWINDOW->CursPosn);
		}
	else
		{
		OFFSET += (moves - mvs);
		BegLine();
		SHOW(OFFSET);
		PutChar(' ', CURWINDOW->Attrib);
		}
	BPOSN += moves;
	if(BPOSN > 0)
		STATE = 2;
	}
else /* number and date types */
	{
	while(IsValid(BUFFER[BPOSN]))
		{
		++ BPOSN;
		if(CursR())
			; /* do nothing */
		else
			{
			++ OFFSET;
			BegLine();
			SHOW(OFFSET);
			}
		}
	}
}

/**************************************************************************
DoDelete        -       Do Delete

Description     -       Deletes the character at the dialogue cursor
			location

Parameters      -       DoDelete()

Returns         -       NOTHING
***************************************************************************/
static void DoDelete(void)
{
if(!STATE)
	return;
if(COUNT && (BPOSN <= LAST))
	{
	target = &BUFFER[BPOSN];
	source = (char *)(target + 1);

	while(*source)
		*target ++ = *source ++;
	BUFFER[LAST] = ' ';
	if(CursR())
		{
		CursL();
		SHOWR(BPOSN);
		}
	else
		PutChar(BUFFER[BPOSN], CURWINDOW->Attrib);
	BUFFER[LAST] = '\0';
	--LAST;
	--COUNT;
	/* no state to update */
	}
}

/**************************************************************************
DoCtrlLeft      -       Do Control Left

Description     -       Moves the dialogue cursor to the beginning of the
			previous word

Parameters      -       DoCtrlLeft()

Returns         -       NOTHING
***************************************************************************/
static void DoCtrlLeft(void)
{
if(!STATE)
	{
	/* show previous item */
	LPOSN = LPOSN->prev ? LPOSN->prev : LIST->last;
	SHOWITEM;
	return;
	}
posn = BPOSN;
if(!BPOSN)
	return; /* nowhere to go */
if(DTYPE == STRING_D)
	{
	if(BUFFER[posn] == ' ')
		{
		/* back over spaces */
		space_first:
		while((posn >= 0) && (BUFFER[posn] == ' '))
			--posn;
		/* back over letters */
		while((posn >= 0) && (BUFFER[posn] != ' '))
			-- posn;
		}
	else
		{
		/* is letter first in word ? */
		if(BUFFER[posn - 1] == ' ')
			{
			--posn;
			goto space_first;
			}
		/* back over letters */
		while((posn >= 0) && (BUFFER[posn] != ' '))
			-- posn;
		}
	++posn; /* back to target */
	if(posn)
		STATE = 2;
	else
		STATE = 1;
	}
else
	{
	/* date or number types */
	if(STATE > 1)
		{
		tstate = STATE;
		tposn = BPOSN;

		while(tstate == STATE)
			{
			AdjStateL();
			-- BPOSN;
			}
		posn = BPOSN;
		BPOSN = tposn;
		}
	}
/* reposition cursor on letter */
if(OFFSET > posn)
	{
	BPOSN = OFFSET = posn;
	BegLine();
	SHOWR(OFFSET);
	}
else
	while(BPOSN > posn)
		{
		CursL();
		--BPOSN;
		}
}

/*************************************************************************
DoCtrlRight     -       Do Control Right

Description     -       Moves the dialogue cursor to the beginning of
			the next word

Parameters      -       DoCtrlRight()

Returns         -       NOTHING
**************************************************************************/
static void DoCtrlRight(void)
{
if(!STATE)
	{
	/* to next item */
	LPOSN = LPOSN->next ? LPOSN->next : LIST->first;
	SHOWITEM;
	return;
	}
posn = BPOSN;
width = (int)CURWINDOW->Bounds.BCol - (int)CURWINDOW->Bounds.TCol;

if(BPOSN >= LAST)
	return; /* nowhere to go */
if(DTYPE == STRING_D)
	{
	if(BUFFER[posn] != ' ')
		{
		/* over letters */
		while((posn <= LAST) && (BUFFER[posn] != ' '))
			++posn;
		/* over spaces */
		after_letters:
		while((posn <= LAST) && (BUFFER[posn] == ' '))
			++ posn;
		}
	else
		goto after_letters;
	/* reposition cursor on next letter */
	width -= (BPOSN - OFFSET);
	if((posn - BPOSN) > width)
		{
		moves = posn - BPOSN;

		OFFSET += moves;
		BPOSN = OFFSET;
		ClearWindow();
		SHOWR(OFFSET);
		}
	while(BPOSN < posn)
		{
		CursR();
		++BPOSN;
		}
	if(posn)
		STATE = 2;
	else
		STATE =1;
	}
else
	{
	/* number or date types */
	tstate = STATE;

	while( (tstate == STATE) && IsValid(BUFFER[BPOSN]) )
		{
		++BPOSN;
		if(CursR())
			; /* do nothing */
		else
			{
			++OFFSET;
			BegLine();
			SHOW(OFFSET);
			}
		}
	}
}

/************************************************************************
DoRet           -       Do Return

Description     -       Handles user entry of '\r' in the dialogue box

Parameters      -       DoRet()

Returns         -       TRUE if the control routine can now exit, else
			FALSE
*************************************************************************/
static BOOLEAN DoRet(void)
{
if(STATE == 0)
	{
	source = ITEM;
	target = &BUFFER[0];
	COUNT = 0;
	while(*source)
		{
		++ COUNT;
		*target ++ = *source ++;
		}
	LAST = COUNT - 1;
	BPOSN = OFFSET = 0;
	BUFFER[COUNT] = '\0';
	STATE = 1;
	if(!SCALAR)
		{
		ClearWindow();
		SHOWR(BPOSN);
		GoEnd();
		return(FALSE);
		}
	}

GoEnd();

if(DTYPE == STRING_D)
	{
	if(STATE < 2)
		return(FALSE);
	}
else if(DTYPE == DATE_D)
	{
	if(STATE < 9)
		return(FALSE);
	}
else if((DTYPE == INT_D) || (DTYPE == NUMBER_D))
	{
	if(STATE < 3)
		return(FALSE);
	}

GoHome();

/* now we can see if what we have is acceptable */
if((posn = ACCEPT(DATA)) < 0)
	{
	if((DTYPE == DATE_D) || (DTYPE == STRING_D))
		; /* do nothing */
	else
		{
		/* number types */
		ShowDNumber();
		}
	if(LIST)
		{
		STATE = COUNT = BPOSN = OFFSET = 0;
		LAST = -1;
		BUFFER[0] = '\0';
		}
	else
		STATE = -1;
	return(TRUE);
	}
/* so it was not accepted */
/* reposition cursor on error */
mod = (int)CURWINDOW->Bounds.BCol - (int)CURWINDOW->Bounds.TCol + 1;
OFFSET = ((posn + 1) / mod) * mod;
BPOSN = OFFSET;
ClearWindow();
SHOWR(OFFSET);
while(BPOSN < posn)
	{
	CursR();
	++BPOSN;
	}
return(FALSE);
}

/************************************************************************
ShowDNumber     -       Show Dialogue Number

Description     -       Shows the number as represented by the
			buffer in its final format using the CURWINDOW and
			casting CURWINDOW->Contents to Dialogue_t *.

Parameters      -       ShowNumber()

Returns         -       NOTHING
*************************************************************************/
void ShowDNumber(void)
{
width = (int)CURWINDOW->Bounds.BCol - (int)CURWINDOW->Bounds.TCol + 1;

if(DTYPE == INT_D)
	{
	no = atoi(BUFFER);
	ClearWindow();
	if(SIGNED)
		wprintf(CURWINDOW, TRUE, "\n%*d", width, no);
	else
		wprintf(CURWINDOW, TRUE,"\n%*u", width, no);
	}
else
	{
	number = atof(BUFFER);
	ClearWindow();
	wprintf(CURWINDOW, TRUE, "\n%*.*lf", width, DECIMALS, number);
	}
}

/************************************************************************
DoBack          -       Do Back Space

Description     -       Deletes the character to the left of the
			dialogue cursor position

Parameters      -       DoBack()

Returns         -       NOTHING
*************************************************************************/
static void DoBack(void)
{
if(!STATE)
	{
	/* show previous item */
	LPOSN = LPOSN->prev ? LPOSN->prev : LIST->last;
	SHOWITEM;
	return;
	}
if(COUNT && BPOSN)
	{
	target = &BUFFER[BPOSN - 1];
	source = &BUFFER[BPOSN];

	AdjStateL(); /* to previous state */
	while(*source)
		*target ++ = *source ++;
	BUFFER[LAST] = ' ';
	--BPOSN;
	--COUNT;
	if(CursL())
		SHOWR(BPOSN);
	else
		{
		if(OFFSET)
			{
			--OFFSET;
			SHOWR(OFFSET);
			}
		else
			SHOWR(BPOSN);
		}
	BUFFER[LAST] = '\0';
	--LAST;
	}
}

/*************************************************************************
DoEsc           -       Do Escape

Description     -       Handles an ESC entry in the dialogue box

Parameters      -       DoEsc()

Returns         -       TRUE if the calling function should be exited,
			else FALSE
*************************************************************************/
static BOOLEAN DoEsc(void)
{
if(STATE == -1)
	return(TRUE);
if(STATE == 0)
	return(FALSE);
/* so we have a positive state */
ClearWindow();
COUNT = BPOSN = OFFSET = 0;
LAST = -1;
BUFFER[0] = '\0';
if(LIST)
	{
	STATE = 0;
	SHOWITEM;
	}
else
	STATE = 1;
return(FALSE);
}

/*************************************************************************
Insert          -       Insert

Description     -       Inserts the supplied character into the buffer
			For use with Date validation routine

Parameters      -       Insert(ch)
			char ch;

Returns         -       NOTHING
*************************************************************************/
static void Insert(char ch)
{
/* insert within the string */
source = &BUFFER[LAST + 1];
target = (char *) (source + 1);

if(COUNT == 8)
	{
	-- COUNT;
	--LAST;
	}
while(source >= &BUFFER[BPOSN])
	*target -- = *source --;
BUFFER[BPOSN] = ch;
++COUNT;
++LAST;
BUFFER[LAST + 1] = '\0';
SHOWR(BPOSN);
++BPOSN;
if(CursR())
	; /* do nothing */
else
	{
	++OFFSET;
	BegLine();
	SHOW(OFFSET);
	if(BPOSN > LAST)
		PutChar(' ', CURWINDOW->Attrib);
	}
}

/*************************************************************************
IsValid         -       Is Valid

Description     -       Ensures the given character is valid with
			regard to the current dialogue type and its
			internal STATE which is updated as required

Parameters      -       IsValid(ch)
			char ch;

Returns         -       TRUE if character is valid, else FALSE
*************************************************************************/
static BOOLEAN IsValid(char ch)
{
switch(DTYPE)
	{
	case STRING_D:
		{
		STATE = 2;
		return(TRUE); /* everything is valid */
		}
	break;
	case INT_D:
	case NUMBER_D:
		{
		switch(STATE)
			{
			case 1:
				{
				// nothing read
				if(SIGNED && ( (ch == '+') || (ch == '-') ))
					{
					STATE = 2;
					return(TRUE); // unary
					}
				if((DTYPE == NUMBER_D) && (ch == '.'))
					{
					STATE = 4;
					return(TRUE); // dec point
					}
				if(ISDIGIT(ch))
					{
					STATE = 3;
					return(TRUE); // digit
					}
				}
			break;
			case 2:
				{
				// unary just read
				if(ISDIGIT(ch))
					{
					STATE = 3;
					return(TRUE); // digit
					}
				if((DTYPE == NUMBER_D) && (ch == '.'))
					{
					STATE = 4;
					return(TRUE); // dec point
					}
				}
			break;
			case 3:
				{
				// integer being read
				if((DTYPE == NUMBER_D) && (ch == '.'))
					{
					STATE = 4;
					return(TRUE); // dec point
					}
				if(ISDIGIT(ch))
					return(TRUE); // digit
				}
			break;
			case 4:
				{
				// decimal point just read
				if(ISDIGIT(ch))
					{
					STATE = 5;
					return(TRUE); // digit
					}
				}
			break;
			case 5:
				{
				// decimal part being read
				if(ISDIGIT(ch))
					return(TRUE); // digit
				}
			break;
			}
		}
	break;
	case DATE_D:
		{
		switch(STATE)
		{
		case 1:
			{
			// nothing read
			if(ISDIGIT(ch))
				{
				STATE = 2;
				return(TRUE);
				}
			if((ch == DATE_SEPARATOR) || (ch == ':'))
				{
				switch(DATETYPE)
				{
				case JAP:
					{
					year = SysYear();
					year %= 100;
					Insert(CHAR(year / 10));
					Insert(CHAR(year % 10));
					}
				break;
				case USA:
					{
					month = SysMonth();
					Insert(CHAR(month / 10));
					Insert(CHAR(month % 10));
					}
				break;
				case ENG:
					{
					day = SysDay();
					Insert(CHAR(day / 10));
					Insert(CHAR(day % 10));
					}
				break;
				}
				STATE = 4;
				return(TRUE);
				}
				}
			break;
			case 2:
				{
				// p1 being read - 2nd digit
				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					p1 = DIGIT(BUFFER[0]);

					if( (DATETYPE == ENG) ||
					(DATETYPE == USA))
						if(p1 == 0)
							return(FALSE);
					DoBack();
					Insert('0');
					Insert(CHAR(p1));
					STATE = 4;
					return(TRUE);
					}
				if(ISDIGIT(ch))
					{
					p1 = (DIGIT(BUFFER[0]) * 10)
					+ DIGIT(ch);

					switch(DATETYPE)
						{
						case ENG:
						if( (p1 == 0) ||
						(p1 > 31))
							return(FALSE);
						break;
						case USA:
						if( (p1 == 0) ||
						(p1 > 12))
							return(FALSE);
						break;
						case JAP:
						break;
						}
					STATE = 3;
					return(TRUE); // for now
					} // end isdigit
				}
			break;
			case 3:
				{
				/* p1 separator awaited */
				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					STATE = 4;
					return(TRUE);
					}
				else
					return(FALSE);
				}
			break;
			case 4:
				{
				// p1 separator just read
				if(ISDIGIT(ch))
					{
					STATE = 5;
					return(TRUE);
					}
				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					switch(DATETYPE)
						{
						case JAP:
						case ENG:
						{
						month = SysMonth();
						Insert(CHAR(month / 10));
						Insert(CHAR(month % 10));
						}
						break;
						case USA:
						{
						day = SysDay();
						Insert(CHAR(day / 10));
						Insert(CHAR(day % 10));
						}
						break;
						}
					STATE = 7;
					return(TRUE);
					}
				}
			break;
			case 5:
				{
				// p2 being read - 2nd digit
				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					p1 = (DIGIT(BUFFER[0]) * 10) +
					DIGIT(BUFFER[1]);
					p2 = DIGIT(BUFFER[3]);

					if(p2 == 0)
						return(FALSE);
					if(DATETYPE == ENG)
						{
						if((p2 == 2) && (p1 == 29))
							goto accept;
						if(p1 > daysin[p2 - 1])
							return(FALSE);
						}
					accept:
					DoBack();
					Insert('0');
					Insert(CHAR(p2));
					STATE = 7;
					return(TRUE);
					}
				if(ISDIGIT(ch))
					{
					p2 = (DIGIT(BUFFER[3]) * 10 )
					+ DIGIT(ch);
					p1 = (DIGIT(BUFFER[0]) * 10) +
					(DIGIT(BUFFER[1]));
					switch(DATETYPE)
					{
					case ENG:
					{
					if((p2 == 0) || (p2 > 12))
						return(FALSE);
					if((p2 == 2) && (p1 == 29))
						break;
					if(p1 > daysin[p2 - 1])
						return(FALSE);
					}
					break;
					case USA:
					{
					if((p2 == 0) || (p2 > 31))
						return(FALSE);
					if((p1 == 2) && (p2 == 29))
						{
						STATE = 6;
						return(TRUE);
						}
					if(p2 > daysin[p1 - 1])
						return(FALSE);
					}
					break;
					case JAP:
					{
					if((p2 == 0) || (p2 > 12))
						return(FALSE);
					}
					break;
					} // end switch
					STATE = 6;
					return(TRUE);
					} // end isdigit
				}
			break;
			case 6:
				{
				/* p2 separator awaited */
				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					STATE = 7;
					return(TRUE);
					}
				else
					return(FALSE);
				}
			break;
			case 7:
				{
				// p2 separator just read
				if(ISDIGIT(ch))
					{
					STATE = 8;
					return(TRUE);
					}
				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					p1 = (DIGIT(BUFFER[0]) * 10) +
					(DIGIT(BUFFER[1]));
					p2 = (DIGIT(BUFFER[3]) * 10 )
					+ DIGIT(BUFFER[4]);
					year = SysYear();
					nyear = year;
					year /= 100;
					year *= 100;
					nyear %= 100;
					switch(DATETYPE)
					{
					case ENG:
					{
					if((p1 == 29) && (p2 == 2))
						if(!ISLEAP(SysYear()))
							return(FALSE);
					Insert(CHAR(nyear / 10));
					Insert(CHAR(nyear % 10));
					}
					break;
					case USA:
					{
					if((p1 == 2) && (p2 == 29))
						if(!ISLEAP(SysYear()))
							return(FALSE);
					Insert(CHAR(nyear / 10));
					Insert(CHAR(nyear % 10));
					}
					break;
					case JAP:
					{
					day = SysDay();
					year += p1;

					if((day == 29) && (p2 == 2))
						if(!ISLEAP(year))
							return(FALSE);
					Insert(CHAR(day / 10));
					Insert(CHAR(day % 10));
					}
					break;
					}
					STATE = 9;
					return(FALSE);
					}
				}
			break;
			case 8:
				{
				// p3 being read - 2nd digit
				p1 = (DIGIT(BUFFER[0]) * 10) +
				(DIGIT(BUFFER[1]));
				p2 = (DIGIT(BUFFER[3]) * 10) +
				(DIGIT(BUFFER[4]));
				year = SysYear();
				year /= 10;
				year *= 10;

				if((ch == DATE_SEPARATOR) || (ch == ':'))
					{
					p3 = DIGIT(BUFFER[6]);
					switch(DATETYPE)
					{
					case ENG:
						{
						if((p1 == 29) && (p2 == 2))
							{
							year += p3;
							if(!ISLEAP(year))
							return(FALSE);
							}
						}
					break;
					case USA:
						{
						if((p1 == 2) && (p2 == 29))
							{
							year += p3;
							if(!ISLEAP(year))
							return(FALSE);
							}
						}
					break;
					case JAP:
						{
						if(p3 == 0)
							return(FALSE);
						}
					break;
					}
					DoBack();
					Insert('0');
					Insert(CHAR(p3));
					STATE = 9;
					return(FALSE);
					}
				if(ISDIGIT(ch))
					{
					p3 = (DIGIT(BUFFER[6]) * 10 )
					+ DIGIT(ch);

					switch(DATETYPE)
					{
					case ENG:
					{
					if((p2 == 2) && (p1 == 29))
						{
						int year = SysYear();
						year /= 100;
						year *= 100;
						year += p3;
						if(!ISLEAP(year))
							return(FALSE);
						}
					}
					break;
					case JAP:
					{
					year = SysYear();
					year /= 100;
					year *= 100;
					year += p1;

					if((p2 == 2) && (p3 == 29) &&
						(!ISLEAP(year)))
						return(FALSE);
					if((p3 == 0) || (p3 > 31))
						return(FALSE);
					if(p3 > daysin[p2 - 1])
						return(FALSE);
					}
					break;
					case USA:
					{
					if((p1 == 2) && (p2 == 29))
						{
						year = SysYear();
						year /= 100;
						year *= 100;
						year += p3;
						if(!ISLEAP(year))
						return(FALSE);
						}
					}
					break;
					} // end switch
					STATE = 9;
					return(TRUE);
					}
				}
			break;
			}
		}
	break;
	}
return(FALSE);
}

/************************************************************************
AdjStateL       -       Adjust State Left

Description     -       Adjusts the internal STATE value in respect
			of moving back one place in the buffer and
			performs any necessary clean ups

Parameters      -       AdjStateL()

Returns         -       NOTHING
**************************************************************************/
static void AdjStateL(void)
{
newposn = BPOSN - 1;

if(newposn < 1)
	{
	STATE = 1;
	return;
	}
switch(DTYPE)
	{
	case STRING_D:
	break;
	case INT_D:
	case NUMBER_D:
		{
		if(BUFFER[newposn] == '.')
			STATE = 3;
		switch(BUFFER[newposn - 1])
			{
			case '+':
			case '-':
				STATE = 2;
			break;
			case '.':
				STATE = 4;
			break;
			default: /* its a digit */
			break;
			}
		}
	break;
	case DATE_D:
		{
		--STATE;
		}
	break;
	}
}

/*************************************************************************
DoEntry         -       Do Entry

Description     -       Handles the entry of character into the
			dialogue box

Parameters      -       DoEntry(ch)
			char ch; for entry

Returns         -       NOTHING
**************************************************************************/
static void DoEntry(char ch)
{
if(SCALAR)
	return;
if(STATE == 0)
	{
	if(ch == ' ')
		{
		/* to next item */
		LPOSN = LPOSN->next ? LPOSN->next : LIST->first;
		SHOWITEM;
		return;
		}
	else
		{
		ClearWindow();
		BUFFER[0] = '\0';
		BPOSN = OFFSET = COUNT = 0;
		LAST = -1;
		STATE = 1;
		}
	}
if(STATE == -1)
	{
	ClearWindow();
	STATE = 1;
	LAST = -1;
	BPOSN = COUNT = OFFSET = 0;
	BUFFER[0] = '\0';
	}
if((DTYPE == DATE_D) && (ISDSEP(ch)))
	ch = DATE_SEPARATOR;
if(!IsValid(ch))
	return;
if(InsertOn())
	{
	if(COUNT < (BUFLEN - 1))
		{
		if(BPOSN > LAST)
			{
			/* insert at end of string */
			BUFFER[BPOSN] = ch;
			++BPOSN;
			++LAST;
			++COUNT;
			BUFFER[BPOSN] = '\0';
			PutChar(ch, CURWINDOW->Attrib);
			if(CursR())
				; /* do nothing */
			else
				{
				++OFFSET;
				BegLine();
				SHOW(OFFSET);
				if(BPOSN > LAST)
					PutChar(' ', CURWINDOW->Attrib);
				}
			}
		else
			{
			/* BPOSN <= LAST */
			/* insert within the string */
			source = &BUFFER[LAST + 1];
			target = (char *) (source + 1);

			while(source >= &BUFFER[BPOSN])
				*target -- = *source --;
			BUFFER[BPOSN] = ch;
			++COUNT;
			++LAST;
			SHOWR(BPOSN);
			++BPOSN;
			if(CursR())
				; /* do nothing */
			else
				{
				++OFFSET;
				BegLine();
				SHOW(OFFSET);
				if(BPOSN > LAST)
					PutChar(' ', CURWINDOW->Attrib);
				}
			}
		} // end if
	else if(BPOSN <= LAST)
		goto over_type;
	} // end InsertOn
else
	{
	/* insert is off */
	if(BPOSN > LAST)
		{
		if(COUNT < (BUFLEN - 1))
			{
			BUFFER[BPOSN] = ch;
			++BPOSN;
			++LAST;
			++COUNT;
			BUFFER[BPOSN] = '\0';
			PutChar(ch, CURWINDOW->Attrib);
			if(CursR())
				; /* do nothing */
			else
				{
				++OFFSET;
				BegLine();
				SHOW(OFFSET);
				if(BPOSN > LAST)
				PutChar(' ', CURWINDOW->Attrib);
				}
			}
		}
	else
		{
		/* BPOSN <= LAST */
		/* over write existing entry */
		over_type:
		BUFFER[BPOSN ++] = ch;
		PutChar(ch, CURWINDOW->Attrib);
		if(CursR())
			; /* do nothing */
		else
			{
			++OFFSET;
			BegLine();
			SHOW(OFFSET);
			if(BPOSN > LAST)
				PutChar(' ', CURWINDOW->Attrib);
			}
		}
	} // end insert is off
}
