/*
*       Echo line reading and writing.
* Common routines for reading
* and writing characters in the echo line area
* of the display screen. Used by the entire
* known universe.
*/
#include    "def.h"

void    eerase ();
char    ereply ();
char    eread ();
void    eformat ();
void    eputi ();
void    eputs ();
void    eputc ();


extern    char    MSG_null[];
extern    char    MSG_y_n[];
extern    char    MSG_hex_dig[];
extern    char    MSG_hit_key[];

int     epresf = FALSE;         /* Stuff in echo line flag. */

/*
* Erase the echo line.
*/
void eerase ()
{
	writ_echo (MSG_null);  /* clear the echo line */
	epresf = FALSE;
}


/*
* Ask "yes" or "no" question.
* Return ABORT if the user answers the question
* with the abort ("^G") character. Return FALSE
* for "no" and TRUE for "yes". No formatting
* services are available.
*/
char    eyesno (sp)
char   *sp;
{

	register char   s;
	char    buf[NCOL];

	for (;;)
	{

		s = ereply (MSG_y_n, buf, sizeof (buf), sp);
		if (s == ABORT)
			return (ABORT);
		if (s != FALSE)
		{

			if (buf[0] == 'y' || buf[0] == 'Y')
				return (TRUE);
			if (buf[0] == 'n' || buf[0] == 'N')
				return (FALSE);
		}

	}

}


/*
* Write out a prompt, and read back a
* reply. The prompt is now written out with full "eprintf"
* formatting, although the arguments are in a rather strange
* place. This is always a new message, there is no auto
* completion, and the return is echoed as such.
*/
/* VARARGS3 */
char    ereply (fp, buf, nbuf, arg)
char   *fp;
char   *buf;
int     nbuf;
char   *arg;
{
	return (eread (fp, buf, nbuf, EFNEW | EFCR, arg));
}


/*
* This is the general "read input from the
* echo line" routine. The basic idea is that the prompt
* string "prompt" is written to the echo line, and a one
* line reply is read back into the supplied "buf" (with
* maximum length "len"). The "flag" contains EFNEW (a
* new prompt), an EFAUTO (autocomplete), or EFCR (echo
* the carriage return as CR).
*/
char    eread (fp, buf, nbuf, flag, ap)
char   *fp;
char   *buf;
char   *ap;
{

	register int    cpos;
	register    SYMBOL * sp1;
	register    SYMBOL * sp2;
	register int    i;
	register int    c;
	register int    h;
	register int    nhits;
	register int    nxtra;
	register int    bxtra;

	int     quote_flag;

	quote_flag = 0;
	cpos = 0;
	if (kbdmop != NULL)
	{
		/* In a macro.      */
		while ((c = *kbdmop++) != '\0')
			buf[cpos++] = c;
		buf[cpos] = '\0';
		goto done;
	}

	if ((flag & EFNEW) != 0 || ttrow != nrow - 1)
	{

		ttcolor (CTEXT);
		ttmove (nrow - 1, 0);
		epresf = TRUE;
	}
	else
		eputc (' ');
	eformat (fp, ap);
	tteeol ();
	ttflush ();
	for (;;)
	{
		c = getkey ();
		if (c == ' ' && (flag & EFAUTO) != 0)
		{
			nhits = 0;
			nxtra = HUGE;
			for (h = 0; h < NSHASH; ++h)
			{
				sp1 = symbol[h];
				while (sp1 != NULL)
				{
					for (i = 0; i < cpos; ++i)
					{
						if (buf[i] != sp1 -> s_name[i])
							break;
					}

					if (i == cpos)
					{
						if (nhits == 0)
							sp2 = sp1;
						++nhits;
						bxtra = getxtra (sp1, sp2, cpos);
						if (bxtra < nxtra)
							nxtra = bxtra;
					}

					sp1 = sp1 -> s_symp;
				}
			}

			if (nhits == 0)     /* No completion.   */
				continue;
			for (i = 0; i < nxtra && cpos < nbuf - 1; ++i)
			{
				c = sp2 -> s_name[cpos];
				buf[cpos++] = c;
				eputc (c);
			}

			ttflush ();
			if (nhits != 1)     /* Fake a CR if there   */
				continue;       /* is 1 choice.     */
			c = (KCTRL | 'M');
		}
		if (quote_flag)
		{
			c = c & 0x1f;
			quote_flag = 0;
		}


		switch (c)
		{
		case (KCTRL | 'Q'):
			quote_flag = 1;
			break;
		case (KCTRL | 'M'): /* Return, done.    */
			buf[cpos] = '\0';
			if (kbdmip != NULL)
			{
				if (kbdmip + cpos + 1 > &kbdm[NKBDM - 3])
				{
					(void) ctrlg (FALSE, 0, KRANDOM);
					ttflush ();
					return (ABORT);
				}

				for (i = 0; i < cpos; ++i)
					*kbdmip++ = buf[i];
				*kbdmip++ = '\0';
			}

			if ((flag & EFCR) != 0)
			{
				ttputc (0x0D);
				ttflush ();
			}

			goto done;

		case (KCTRL | 'G'): /* Bell, abort.     */
			eputc (0x07);
			(void) ctrlg (FALSE, 0, KRANDOM);
			ttflush ();
			return (ABORT);

		case 0x7F:          /* Rubout, erase.   */
		case (KCTRL | 'H'): /* Backspace, erase.    */
			if (cpos != 0)
			{
				ttputc ('\b');
				ttputc (' ');
				ttputc ('\b');
				--ttcol;
				if (ISCTRL (buf[--cpos]) != FALSE)
				{
					ttputc ('\b');
					ttputc (' ');
					ttputc ('\b');
					--ttcol;
				}

				ttflush ();
			}
			break;

		case (KCTRL | 'U'): /* C-U, kill line.  */
			while (cpos != 0)
			{
				ttputc ('\b');
				ttputc (' ');
				ttputc ('\b');
				--ttcol;
				if (ISCTRL (buf[--cpos]) != FALSE)
				{
					ttputc ('\b');
					ttputc (' ');
					ttputc ('\b');
					--ttcol;
				}

			}

			ttflush ();
			break;

		default:            /* All the rest.    */
			if ((cpos < nbuf - 1) && ((c & ~KCHAR) == 0))
			{
				buf[cpos++] = c;
				eputc (c);
				ttflush ();
			}
		}                   /* End switch */

	}

done:
	if (buf[0] == '\0')
		return (FALSE);
	return (TRUE);
}


/*
* The "sp1" and "sp2" point to extended command
* symbol table entries. The "cpos" is a horizontal position
* in the name. Return the longest block of characters that can
* be autocompleted at this point. Sometimes the two symbols
* are the same, but this is normal.
*/
int     getxtra (sp1, sp2, cpos)
SYMBOL * sp1;
SYMBOL * sp2;
{

	register int    i;

	i = cpos;
	for (;;)
	{

		if (sp1 -> s_name[i] != sp2 -> s_name[i])
			break;
		if (sp1 -> s_name[i] == '\0')
			break;
		++i;
	}

	return (i - cpos);
}

/*
* Printf style formatting. This is
* called by both "eprintf" and "ereply", to provide
* formatting services to their clients. The move to the
* start of the echo line, and the erase to the end of
* the echo line, is done by the caller.
*/
void eformat (fp, ap)
char  *fp;
char  *ap;
{

	register int    c;

	while ((c = *fp++) != '\0')
	{

		if (c != '%')
			eputc (c);
		else
		{

			c = *fp++;
			switch (c)
			{

			case 'd':
				eputi (*(int *) ap, 10);
				ap += sizeof (int);
				break;

			case 'x':       /* krw */
				eputi (*(int *) ap, 16);
				ap += sizeof (int);
				break;

			case 'o':
				eputi (*(int *) ap, 8);
				ap += sizeof (int);
				break;

			case 's':
				eputs (ap);
				ap += sizeof (char *);
				break;

			default:
				eputc (c);
			}

		}

	}

}


/*
* Put integer, in radix "r".
*/
void eputi (i, r)
int    i;
int    r;
{
	static char *convert =
	{
		MSG_hex_dig
	};


	register int    q;

	if ((q = i / r) != 0)
		eputi (q, r);
	eputc (convert[i % r]);

}


/*
* Put string.
*/
void eputs (s)
char  *s;
{
	register int    c;

	while ((c = *s++) != '\0')
		eputc (c);
}


/*
* Put character. Watch for
* control characters, and for the line
* getting too long.
*/
void eputc (c)
int    c;
{

	if (ttcol < ncol)
	{

		if (ISCTRL (c) != FALSE)
		{

			eputc ('^');
			c ^= 0x40;
		}

		ttputc (c);
		++ttcol;
	}

}
/*
 *   Print warning message and wait for the user to hit a key.
 */
void    err_echo (buf)
char    *buf;
{
	char   ch[NCOL * 2];

	strcpy (ch, buf);
	strcat (ch, MSG_hit_key);
	writ_echo (ch);
	ttbeep ();
	while (ttgetc () != CTL_G);
	{
		ttbeep ();
		ttflush ();
	}
}
