/******************************************************************************

	ReadCS()

This function reads a command string from the terminal.  It returns to it's
caller when the command buffer pointed to by CBfBeg contains a command string.
In addition to echoing the characters typed by the user,  this function
handles the following special things:

	1. DEL		(delete last character)
	2. ^G*		(retype command string)
	3. ^G<SP>	(retype command line)
	4. ^G^G		(delete command string)
	5. ^U		(delete command line)
	6. ^Z^Z^Z	(exit TECO-C)
	7. LF		(immediate mode: do a 1L1T command)
	8. BS		(immediate mode: do a -1L1T)
	9. *q		(immediate mode: store last command string in q)
	10. HELP	(immediate mode: access HELP subsystem)
	11. /		(immediate mode: display explanation of last error)
	12. ?		(immediate mode: display erroneous command string)

*****************************************************************************/

#include "ZPort.h"		/* define portability identifiers */
#include "DefTeco.h"		/* define general identifiers */
#include "DefChars.h"		/* define identifiers for characters */
#include "ChrMacs.h"		/* define character processing macros */
#include "DefError.h"		/* define identifiers for error messages */
#include "DefScren.h"		/* define identifiers for screen i/o */

#define PRV_CTG		'\1'	/* previous character was control-G */
#define PRV_DEF		'\2'	/* previous character wasn't ^Z, ^G or ESC */
#define PRV_ESC		'\3'	/* previous character was escape */
#define PRV_Z1		'\4'	/* previous character was one control-Z */
#define PRV_Z2		'\5'	/* previous character was two control-Z */

extern	BOOLEAN	ChkHlp();	/* check for a HELP command */
extern	char	FrstCh();	/* input 1st command string character */
extern	VOID	InpDel();	/* handle a delete character */
extern	LONG	Ln2Chr();	/* convert line offset to character offset */
extern	VOID	ScrnOp();	/* do a screen operation */
extern	VOID	TypBuf();	/* type a buffer */
extern	VOID	ZAbort();	/* cleanup and exit TECO-C */
extern	char	ZChIn();	/* input a character from the terminal */
extern	VOID	ZDspBf();	/* output a buffer to the terminal */
extern	VOID	ZDspCh();	/* output a character to the terminal */

EXTERN	char	*CBfBeg;	/* beginning of command buffer */
EXTERN	char	*CBfEnd;	/* end of command buffer */
EXTERN	char	*CBfPtr;	/* pointer into command buffer */
EXTERN	LONG	CRCnt;		/* count of CRETRNs in command string */
EXTERN	char	*CStEnd;	/* pointer to last char of command string */
EXTERN	WORD	EtFlag;		/* ET mode control flag */
EXTERN	WORD	EvFlag;		/* EV mode control flag */
EXTERN	char	*GapBeg;	/* beginning of edit buffer gap */
EXTERN	char	*GapEnd;	/* end of edit buffer gap */
EXTERN	BOOLEAN	GotCtC;		/* YES if user just hit a CTRL_C */
EXTERN	WORD	LstErr;		/* number of last error message */


VOID ReadCS()
{
	LOCAL	BOOLEAN	BadSeq;		/* bad escape sequence indicator */
	static	char	ctrstr[3] = "^x";
	LOCAL	LONG	HowFar;
	static	char	*Prompt = "\012\015*";
	LOCAL	BYTE	PrvChr;		/* previous character flags */
	LOCAL	char	TmpChr;		/* temporary character */
	LOCAL	char	*TmpPtr;	/* temporary pointer */


CRCnt = 0L;			/* count of carriage-returns is 0 */
PrvChr = PRV_DEF;		/* no previous characters */
CBfPtr = CBfBeg;		/* command buffer is empty */
*CBfPtr = FrstCh();		/* handle 1st char until not *, / or ? */
LstErr = ERR_XXX;		/* last error message is undefined */

FOREVER {
	if (GotCtC)				/* if got a control-C */
		{
		GotCtC = NO;
		ctrstr[1] = 'C';
		ZDspBf(ctrstr, 2);		/* display "^C" */
		CBfPtr = CBfBeg - 1L;
		ZDspBf(Prompt, 3);		/* prompt */
		CRCnt = 0L;			/* carriage-return count = 0 */
		PrvChr = PRV_DEF;
		}
	else if (*CBfPtr == ESCAPE)
		if (EtFlag & ET_VT200)		/* if special vt200 mode */
			{
			*CBfPtr = ZChIn();	/* get a character */
			BadSeq = NO;		/* initialize BadSeq */
			if (*CBfPtr != '[')
				BadSeq = YES;
			else
				{
				*CBfPtr = ZChIn();
				switch (*CBfPtr) {
				case '2':		/* f9 - f16 */
					*CBfPtr = ZChIn();
					if (*CBfPtr == '4')
						{
						TmpChr = ZChIn();
						if (TmpChr != '~')
							BadSeq = YES;
						else
							*CBfPtr = BAKSPC;
						}
					else if (*CBfPtr == '5')
						{
						TmpChr = ZChIn();
						if (TmpChr != '~')
							BadSeq = YES;
						else
							*CBfPtr = LINEFD;
						}
					else
						BadSeq = YES;
					break;
#if NO
				case 'A':		/* up arrow */
				case 'B':		/* down arrow */
				case 'C':		/* right arrow */
				case 'D':		/* left arrow */
#endif
				default:
					BadSeq = YES;
				}
				}
			if (BadSeq)
				{
ZDspBf("\015\012\012\tInvalid escape sequence.\015\012\012", 31);
ZDspBf("\tThe 16384 bit of the ET flag is set,  which means\015\012", 52);
ZDspBf("\tthat you are in VT200 mode.  In this mode,  the\015\012", 50);
ZDspBf("\tescape character is not used to terminate commands.\015\012", 54);
ZDspBf("\tIt is used to introduce escape sequences.  This\015\012", 50);
ZDspBf("\tallows the function keys to take on meanings.  The\015\012", 53);
ZDspBf("\taccent grave (~) character is the command terminator.\015\012", 56);
ZDspBf("\tIf you want to turn off VT200 mode,  say 16384,0ET``\015\012", 55);
ZDspBf("\tNote that the recognition of accent grave as a\015\012", 49);
ZDspBf("\tcommand terminator is controlled by the 8192 bit\015\012", 51);
ZDspBf("\tof the ET flag,  seperate from the VT200 bit.\015\012\012", 49);
ZDspBf("\tThere may be a part of the unrecognized escape\015\012", 49);
ZDspBf("\tsequence in the command string.  The last line of\015\012", 52);
ZDspBf("\tthe command string is shown to help you recover.\015\012", 51);
				PrvChr = PRV_CTG;
				CBfPtr++;
				*CBfPtr = ' ';
				}
			}
		else				/* else not vt200 mode */
			{
			ZDspCh('$');
			if (PrvChr == PRV_ESC)
				{
				ZDspBf("\015\012", 2);
				CStEnd = CBfPtr;
				return;
				}
			PrvChr = PRV_ESC;
			}
	switch (ChrMsk[*CBfPtr] & '\17') {		/* use bottom 4 bits */
		case RCS_LWR:
			if ((EtFlag & ET_READ_LOWER) == 0)
				*CBfPtr &= '\137';	/* convert to upper */
		case RCS_DEF:
			ZDspCh(*CBfPtr);
			PrvChr = PRV_DEF;
			break;
		case RCS_GRV:
			ZDspCh('`');
			if (EtFlag & ET_ACCENT_GRAVE)
				{
				*CBfPtr = ESCAPE;
				if (PrvChr == PRV_ESC)
					{
					ZDspBf("\015\012", 2);
					CStEnd = CBfPtr;
					return;
					}
				PrvChr = PRV_ESC;
				}
			else
				PrvChr = PRV_DEF;
			break;
		case RCS_SP:
			if (PrvChr == PRV_CTG)
				{
				CBfPtr--;		/* remove space */
				TmpPtr = CBfPtr;
				while (TmpPtr > CBfBeg)
					{
					TmpPtr--;
					if (IsEOL(*TmpPtr))
						{
						++TmpPtr;
						break;
						}
					}
				ZDspBf("\015\012", 2);
				if (TmpPtr == CBfBeg)
					ZDspCh('*');
				TypBuf(TmpPtr, CBfPtr);
				CBfPtr--;		/* remove ^G */
				}
			else
				ZDspCh(SPACE);
			PrvChr = PRV_DEF;
			break;
		case RCS_DEL:
			InpDel();
			PrvChr = PRV_DEF;
			break;
		case RCS_CR:
			ZDspCh(CRETRN);
			if ((CRCnt == 0L) && ChkHlp(CBfPtr))
				{
				ZDspCh('*');
				CBfPtr = CBfBeg - 1L;
				}
			else
				++CRCnt;
			PrvChr = PRV_DEF;
			break;
		case RCS_LF:
			if (CBfPtr == CBfBeg)	/* if immediate mode */
				{
				if (EtFlag & ET_SCOPE)
					{
					ZDspCh(CRETRN);
					ScrnOp(SCR_EEL);
					}
				HowFar = Ln2Chr(1L);
				ZCpyBl(HowFar, GapEnd+1L, GapBeg);
				GapBeg += HowFar;
				GapEnd += HowFar;
				if (EvFlag == 0)
					TypBuf(GapEnd+1L,GapEnd+Ln2Chr(1L)+1L);
				ZDspCh('*');
				CBfPtr = CBfBeg - 1L;
				}
			else
				ZDspCh(LINEFD);
			PrvChr = PRV_DEF;
			break;
		case RCS_BS:
			if (CBfPtr == CBfBeg)	/* if immediate mode */
				{
				if (EtFlag & ET_SCOPE)
					{
					ZDspCh(CRETRN);
					ScrnOp(SCR_EEL);
					}
				HowFar = Ln2Chr(-1L);
				GapBeg += HowFar;
				GapEnd += HowFar;
				ZCpyBl(-HowFar, GapBeg, GapEnd+1L);
				if (EvFlag == 0)
					TypBuf(GapEnd+1L,GapEnd+Ln2Chr(1L)+1L);
				ZDspCh('*');
				CBfPtr = CBfBeg - 1L;
				}
			else
				ZDspCh(BAKSPC);
			PrvChr = PRV_DEF;
			break;
		case RCS_CTG:
			ZDspBf("\7^G", 3);
			if (PrvChr == PRV_CTG)		/* ^G^G? */
				{
				CBfPtr = CBfBeg - 1L;
				ZDspBf(Prompt, 3);
				CRCnt = 0L;
				PrvChr = PRV_DEF;
				}
			else
				PrvChr = PRV_CTG;
			break;
		case RCS_AST:
			ZDspCh('*');
			if (PrvChr == PRV_CTG)
				{
				CBfPtr -= 2;
				ZDspBf(Prompt, 3);
				CRCnt = 0L;
				TypBuf(CBfBeg, CBfPtr);
				}
			PrvChr = PRV_DEF;
			break;
		case RCS_CTZ:
			ctrstr[1] = 'Z';
			ZDspBf(ctrstr, 2);
			if (PrvChr == PRV_Z2)
				ZAbort();
			else if (PrvChr == PRV_Z1)
				PrvChr = PRV_Z2;
			else
				PrvChr = PRV_Z1;
			break;
		case RCS_CCH:
			ctrstr[1] = *CBfPtr | '\100';
			ZDspBf(ctrstr, 2);
			PrvChr = PRV_DEF;
			break;
		case RCS_CTU:
			while (--CBfPtr >= CBfBeg)
				if (IsEOL(*CBfPtr))
					break;
			ZDspCh(CRETRN);
			if (EtFlag & ET_SCOPE)
				ScrnOp(SCR_EEL);	/* erase line */
			else
				ZDspCh(LINEFD);
			if (CBfPtr<CBfBeg)
				ZDspCh('*');
			PrvChr = PRV_DEF;
			break;
		case RCS_VF:
			ZDspBf("\015\014\014\014\014\014", 6);
			PrvChr = PRV_DEF;
			break;
		}			/* end of switch */
	if (++CBfPtr > CBfEnd)
		{
/* ??? */	ZDspBf("command buffer overflow\015\012", 25);
		ZAbort();			/* exit TECO-C */
		}
	*CBfPtr = ZChIn();			/* read a character */
}				/* end of FOREVER loop */

}				/* end of ReadCS function */
