/*	INPUT.C
 *
 *	Various input routines
 *
 *	written by Daniel Lawrence 5/9/86
 *	modified by Petri Kutvonen
 */

#include	<stdio.h>
#include	"estruct.h"
#include	"edef.h"

#if	PKCODE
#if     MSDOS && TURBO
#include	<dir.h>
#endif
#endif

#if	PKCODE && (UNIX || (MSDOS && TURBO))
#define	COMPLC	1
#else
#define COMPLC	0
#endif

/*
 * Ask a yes or no question in the message line. Return either TRUE, FALSE, or
 * ABORT. The ABORT status is returned if the user bumps out of the question
 * with a ^G. Used any time a confirmation is required.
 */

mlyesno(prompt)

char *prompt;

{
	char c;			/* input character */
	char buf[NPAT];		/* prompt to user */

	for (;;) {
		/* build and prompt the user */
		strcpy(buf, prompt);
		strcat(buf, " (y/n)? ");
		mlwrite(buf);

		/* get the responce */
		c = tgetc();

		if (c == ectoc(abortc))		/* Bail out! */
			return(ABORT);

		if (c=='y' || c=='Y')
			return(TRUE);

		if (c=='n' || c=='N')
			return(FALSE);
	}
}

/*
 * Write a prompt into the message line, then read back a response. Keep
 * track of the physical position of the cursor. If we are in a keyboard
 * macro throw the prompt away, and return the remembered response. This
 * lets macros run at full speed. The reply is always terminated by a carriage
 * return. Handle erase, kill, and abort keys.
 */

mlreply(prompt, buf, nbuf)
    char *prompt;
    char *buf;
{
	return(nextarg(prompt, buf, nbuf, ctoec('\n')));
}

mlreplyt(prompt, buf, nbuf, eolchar)

char *prompt;
char *buf;
int eolchar;

{
	return(nextarg(prompt, buf, nbuf, eolchar));
}

/*	ectoc:	expanded character to character
		colapse the CONTROL and SPEC flags back into an ascii code   */

ectoc(c)

int c;

{
	if (c & CONTROL)
		c = c & ~(CONTROL | 0x40);
	if (c & SPEC)
		c= c & 255;
	return(c);
}

/*	ctoec:	character to extended character
		pull out the CONTROL and SPEC prefixes (if possible)	*/

ctoec(c)

int c;

{
        if (c>=0x00 && c<=0x1F)
                c = CONTROL | (c+'@');
        return (c);
}
 
/* get a command name from the command line. Command completion means
   that pressing a <SPACE> will attempt to complete an unfinished command
   name if it is unique.
*/

int (*getname())()

{
	register int cpos;	/* current column on screen output */
	register int c;
	register char *sp;	/* pointer to string for output */
	register NBIND *ffp;	/* first ptr to entry in name binding table */
	register NBIND *cffp;	/* current ptr to entry in name binding table */
	register NBIND *lffp;	/* last ptr to entry in name binding table */
	char buf[NSTRING];	/* buffer to hold tentative command name */
	int (*fncmatch())();

	/* starting at the beginning of the string buffer */
	cpos = 0;

	/* if we are executing a command line get the next arg and match it */
	if (clexec) {
		if (macarg(buf) != TRUE)
			return(FALSE);
		return(fncmatch(&buf[0]));
	}

	/* build a name string from the keyboard */
	while (TRUE) {
		c = tgetc();

		/* if we are at the end, just match it */
		if (c == 0x0d) {
			buf[cpos] = 0;

			/* and match it off */
			return(fncmatch(&buf[0]));

		} else if (c == ectoc(abortc)) {	/* Bell, abort */
			ctrlg(FALSE, 0);
			TTflush();
			return( (int (*)()) NULL);

		} else if (c == 0x7F || c == 0x08) {	/* rubout/erase */
			if (cpos != 0) {
				TTputc('\b');
				TTputc(' ');
				TTputc('\b');
				--ttcol;
				--cpos;
				TTflush();
			}

		} else if (c == 0x15) {	/* C-U, kill */
			while (cpos != 0) {
				TTputc('\b');
				TTputc(' ');
				TTputc('\b');
				--cpos;
				--ttcol;
			}

			TTflush();

		} else if (c == ' ' || c == 0x1b || c == 0x09) {
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
	/* attempt a completion */
	buf[cpos] = 0;		/* terminate it for us */
	ffp = &names[0];	/* scan for matches */
	while (ffp->n_func != NULL) {
		if (strncmp(buf, ffp->n_name, strlen(buf)) == 0) {
			/* a possible match! More than one? */
			if ((ffp + 1)->n_func == NULL ||
			   (strncmp(buf, (ffp+1)->n_name, strlen(buf)) != 0)) {
				/* no...we match, print it */
				sp = ffp->n_name + cpos;
				while (*sp)
					TTputc(*sp++);
				TTflush();
				return(ffp->n_func);
			} else {
/* << << << << << << << << << << << << << << << << << */
	/* try for a partial match against the list */

	/* first scan down until we no longer match the current input */
	lffp = (ffp + 1);
	while ((lffp+1)->n_func != NULL) {
		if (strncmp(buf, (lffp+1)->n_name, strlen(buf)) != 0)
			break;
		++lffp;
	}

	/* and now, attempt to partial complete the string, char at a time */
	while (TRUE) {
		/* add the next char in */
		buf[cpos] = ffp->n_name[cpos];

		/* scan through the candidates */
		cffp = ffp + 1;
		while (cffp <= lffp) {
			if (cffp->n_name[cpos] != buf[cpos])
				goto onward;
			++cffp;
		}

		/* add the character */
		TTputc(buf[cpos++]);
	}
/* << << << << << << << << << << << << << << << << << */
			}
		}
		++ffp;
	}

	/* no match.....beep and onward */
	TTbeep();
onward:;
	TTflush();
/* <<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<<< */
		} else {
			if (cpos < NSTRING-1 && c > ' ') {
				buf[cpos++] = c;
				TTputc(c);
			}

			++ttcol;
			TTflush();
		}
	}
}

/*	tgetc:	Get a key from the terminal driver, resolve any keyboard
		macro action					*/

int tgetc()

{
	int c;	/* fetched character */

	/* if we are playing a keyboard macro back, */
	if (kbdmode == PLAY) {

		/* if there is some left... */
		if (kbdptr < kbdend)
			return((int)*kbdptr++);

		/* at the end of last repitition? */
		if (--kbdrep < 1) {
			kbdmode = STOP;
#if	VISMAC == 0
			/* force a screen update after all is done */
			update(FALSE);
#endif
		} else {

			/* reset the macro to the begining for the next rep */
			kbdptr = &kbdm[0];
			return((int)*kbdptr++);
		}
	}

	/* fetch a character from the terminal driver */
	c = TTgetc();

	/* record it for $lastkey */
	lastkey = c;

	/* save it if we need to */
	if (kbdmode == RECORD) {
		*kbdptr++ = c;
		kbdend = kbdptr;

		/* don't overrun the buffer */
		if (kbdptr == &kbdm[NKBDM - 1]) {
			kbdmode = STOP;
			TTbeep();
		}
	}

	/* and finally give the char back */
	return(c);
}

/*	GET1KEY:	Get one keystroke. The only prefixs legal here
			are the SPEC and CONTROL prefixes.
								*/

get1key()

{
	int    c;

	/* get a keystroke */
        c = tgetc();

#if	MSDOS 
	if (c == 0) {				/* Apply SPEC prefix	*/
	        c = tgetc();
	        if (c>=0x00 && c<=0x1F)		/* control key? */
        	        c = CONTROL | (c+'@');
		return(SPEC | c);
	}
#endif

        if (c>=0x00 && c<=0x1F)                 /* C0 control -> C-     */
                c = CONTROL | (c+'@');
        return (c);
}

/*	GETCMD:	Get a command from the keyboard. Process all applicable
		prefix keys
							*/
getcmd()

{
	int c;		/* fetched keystroke */
#if VT220
	int d;		/* second character P.K. */
	int cmask = 0;
#endif
	/* get initial character */
	c = get1key();

#if VT220
proc_metac:
#endif
	/* process META prefix */
	if (c == (CONTROL | '[')) {
		c = get1key();
#if VT220
		if (c == '[' || c == 'O') {	/* CSI P.K. */
			c = get1key();
			if (c >= 'A' && c <= 'D')
				return(SPEC | c | cmask);
			if (c >= 'E' && c <= 'z' && c != 'i' && c != 'c')
				return(SPEC | c | cmask);
			d = get1key();
			if (d == '~')		/* ESC [ n ~   P.K. */
				return(SPEC | c | cmask);
			switch (c) {		/* ESC [ n n ~ P.K. */
			case '1':	c = d + 32;
					break;
			case '2':	c = d + 48;
					break;
			case '3':	c = d + 64;
					break;
			default:	c = '?';
					break;
			}
			if (d != '~')		/* eat tilde P.K. */
				get1key();
			if (c == 'i') {		/* DO key    P.K. */
				c = ctlxc;
				goto proc_ctlxc;
			}
			else if (c == 'c')	/* ESC key   P.K. */
				c = get1key();
			else
				return(SPEC | c | cmask);
		}
#endif
#if VT220
		if (c == (CONTROL | '[')) {
			cmask = META;
			goto proc_metac;
		}
#endif
	        if (islower(c))		/* Force to upper */
        	        c ^= DIFCASE;
	        if (c>=0x00 && c<=0x1F)		/* control key */
	        	c = CONTROL | (c+'@');
		return(META | c );
	}
#if	PKCODE
	else
	if (c == metac) {
		c = get1key();
#if VT220
		if (c == (CONTROL | '[')) {
			cmask = META;
			goto proc_metac;
		}
#endif
	        if (islower(c))		/* Force to upper */
        	        c ^= DIFCASE;
	        if (c>=0x00 && c<=0x1F)		/* control key */
	        	c = CONTROL | (c+'@');
		return(META | c );
	}
#endif


#if	VT220
proc_ctlxc:
#endif
	/* process CTLX prefix */
	if (c == ctlxc) {
		c = get1key();
#if VT220
		if (c == (CONTROL | '[')) {
			cmask = CTLX;
			goto proc_metac;
		}
#endif
	        if (c>='a' && c<='z')		/* Force to upper */
        	        c -= 0x20;
	        if (c>=0x00 && c<=0x1F)		/* control key */
	        	c = CONTROL | (c+'@');
		return(CTLX | c);
	}

	/* otherwise, just return it */
	return(c);
}

/*	A more generalized prompt/reply function allowing the caller
	to specify the proper terminator. If the terminator is not
	a return ('\n') it will echo as "<NL>"
							*/
getstring(prompt, buf, nbuf, eolchar)

char *prompt; char *buf; int eolchar;

{
	register int cpos;	/* current character position in string */
	register int c;
	register int quotef;	/* are we quoting the next char? */
#if	COMPLC
	int ffile, ocpos, nskip, didtry = 0;
#if     MSDOS
	struct ffblk ffblk;
	char *fcp;
#endif
#if	UNIX
	static char tmp[] = "/tmp/meXXXXXX";
	FILE *tmpf = NULL;
#endif
	ffile = (strcmp(prompt, "Find file: ") == 0
	      || strcmp(prompt, "View file: ") == 0
	      || strcmp(prompt, "Insert file: ") == 0
	      || strcmp(prompt, "Write file: ") == 0
	      || strcmp(prompt, "Read file: ") == 0
	      || strcmp(prompt, "File to execute: ") == 0);
#endif

	cpos = 0;
	quotef = FALSE;

	/* prompt the user for the input string */
	mlwrite(prompt);

	for (;;) {
#if	COMPLC
		if (! didtry)
			nskip = -1;
		didtry = 0;
#endif
		/* get a character from the user */
		c = get1key();

		/* If it is a <ret>, change it to a <NL> */
#if	PKCODE
		if (c == (CONTROL | 0x4d) && !quotef)
#else
		if (c == (CONTROL | 0x4d))
#endif
			c = CONTROL | 0x40 | '\n';

		/* if they hit the line terminate, wrap it up */
		if (c == eolchar && quotef == FALSE) {
			buf[cpos++] = 0;

			/* clear the message line */
			mlwrite("");
			TTflush();

			/* if we default the buffer, return FALSE */
			if (buf[0] == 0)
				return(FALSE);

			return(TRUE);
		}

		/* change from command form back to character form */
		c = ectoc(c);

		if (c == ectoc(abortc) && quotef == FALSE) {
			/* Abort the input? */
			ctrlg(FALSE, 0);
			TTflush();
			return(ABORT);
		} else if ((c==0x7F || c==0x08) && quotef==FALSE) {
			/* rubout/erase */
			if (cpos != 0) {
				outstring("\b \b");
				--ttcol;

				if (buf[--cpos] < 0x20) {
					outstring("\b \b");
					--ttcol;
				}
				if (buf[cpos] == '\n') {
					outstring("\b\b  \b\b");
					ttcol -= 2;
				}

				TTflush();
			}

		} else if (c == 0x15 && quotef == FALSE) {
			/* C-U, kill */
			while (cpos != 0) {
				outstring("\b \b");
				--ttcol;

				if (buf[--cpos] < 0x20) {
					outstring("\b \b");
					--ttcol;
				}
				if (buf[cpos] == '\n') {
					outstring("\b\b  \b\b");
					ttcol -= 2;
				}
			}
			TTflush();

#if	COMPLC
		} else if ((c == 0x09 || c == ' ') && quotef == FALSE && ffile) {
			/* TAB, complete file name */
			char ffbuf[255];
#if	MSDOS
			char sffbuf[128];
			int lsav = -1;
#endif
			int n, iswild = 0;

			didtry = 1;
			ocpos = cpos;
			while (cpos != 0) {
				outstring("\b \b");
				--ttcol;

				if (buf[--cpos] < 0x20) {
					outstring("\b \b");
					--ttcol;
				}
				if (buf[cpos] == '\n') {
					outstring("\b\b  \b\b");
					ttcol -= 2;
				}
				if (buf[cpos] == '*' || buf[cpos] == '?')
					iswild = 1;
#if	MSDOS
				if (lsav < 0 && (buf[cpos] == '\\' ||
						 buf[cpos] == '/'  ||
						 buf[cpos] == ':'  && cpos == 1))
					lsav = cpos;
#endif
			}
			TTflush();
			if (nskip < 0)
			{
				buf[ocpos] = 0;
#if	UNIX
				if (tmpf != NULL)
					fclose(tmpf);
				strcpy(tmp, "/tmp/meXXXXXX");
				strcpy(ffbuf, "echo ");
				strcat(ffbuf, buf);
				if (! iswild)
					strcat(ffbuf,"*");
				strcat(ffbuf, " >");
				mktemp(tmp);
				strcat(ffbuf, tmp);
				strcat(ffbuf, " 2>&1");
				system(ffbuf);
				tmpf = fopen(tmp, "r");
#endif
#if	MSDOS
				strcpy(sffbuf, buf);
				if (! iswild)
					strcat(sffbuf,"*.*");
#endif
				nskip = 0;
			}
#if	UNIX
			c = ' ';
			for (n = nskip; n > 0; n--)
				while ((c = getc(tmpf)) != EOF && c != ' ');
#endif
#if	MSDOS
			if (nskip == 0)
			{
				strcpy(ffbuf, sffbuf);
				c = findfirst(ffbuf, &ffblk, FA_DIREC) ? '*' : ' ';
			}
			else if (nskip > 0)
				c = findnext(&ffblk) ? 0 : ' ';
#endif
			nskip++;

			if (c != ' ')
			{
				TTbeep();
				nskip = 0;
			}

#if	UNIX
			while ((c = getc(tmpf)) != EOF && c != '\n' && c != ' ' && c != '*')
#endif
#if	MSDOS
			if (c == '*')
				fcp = sffbuf;
			else
			{
				strncpy(buf, sffbuf, lsav+1);
				cpos = lsav+1;
				fcp = ffblk.ff_name;
			}
			while (c != 0 && (c = *fcp++) != 0 && c != '*')
#endif
			{
				if (cpos < nbuf-1)
					buf[cpos++] = c;
			}
#if	UNIX
			if (c == '*')
				TTbeep();
#endif

			for (n = 0; n < cpos; n++)
			{
				c = buf[n];
				if ((c < ' ') && (c != '\n')) {
					outstring("^");
					++ttcol;
					c ^= 0x40;
				}

				if (c != '\n') {
					if (disinp)
						TTputc(c);
				} else {	/* put out <NL> for <ret> */
					outstring("<NL>");
					ttcol += 3;
				}
				++ttcol;
			}
			TTflush();
#if	UNIX
			rewind(tmpf);
			unlink(tmp);
#endif
#endif

		} else if ((c == quotec || c == 0x16) && quotef == FALSE) {
			quotef = TRUE;
		} else {
			quotef = FALSE;
			if (cpos < nbuf-1) {
				buf[cpos++] = c;

				if ((c < ' ') && (c != '\n')) {
					outstring("^");
					++ttcol;
					c ^= 0x40;
				}

				if (c != '\n') {
					if (disinp)
						TTputc(c);
				} else {	/* put out <NL> for <ret> */
					outstring("<NL>");
					ttcol += 3;
				}
				++ttcol;
				TTflush();
			}
		}
	}
}

outstring(s)	/* output a string of characters */

char *s;	/* string to output */

{
	if (disinp)
		while (*s)
			TTputc(*s++);
}

ostring(s)	/* output a string of output characters */

char *s;	/* string to output */

{
	if (discmd)
		while (*s)
			TTputc(*s++);
}

