/* interpret.c - interpret an incoming character
 *
 * A lot of this code is very similar to part of the Linux kernel, which is
 * because I looked at the file `drivers/char/console.c' in the 2.0.34
 * source to see how it was done.
 *
 * Note that tab stops are ignored; tab stops are always assumed to be every
 * 8th column. Also, the following are not supported and are always ignored:
 *
 *   application keyboard mode
 *   setG0|1
 *   cursor visibility
 *   ^N and ^O
 *   set/clear tab stops
 *   set scrolling region
 *   scroll scrolling region up/down
 */

#include "Internal.h"


/* Interpret character "c" for terminal "vt". Returns zero on success,
 * negative on error, and positive if a callback function returned 1
 * indicating that interpretation should stop immediately (although "c" will
 * have been processed).
 *
 * If this seems confusing, look at vt100_interpret_buf, which calls this
 * function.
 *
 * Note that if adding a character causes the end of the line to be reached,
 * the current X coordinate is left at greater than vt->cols, and wrapping
 * and/or scrolling is only done when the next character is added.
 */
int vt100_interpret_char(vt_info vt, unsigned char c)
{
	int r;

	if (!vt)
		return (-1);

	if (vt->pending)
		vt100_clear_pending(vt);    /* clear pending actions */

	switch (c) {			    /* always check for control characters */
	case 0:
		return (0);		    /* ignore ^@ */
	case 7:			    /* ^G */
		if (vt->esc_state != VT_ESxtitle)
			return (vt100_action(vt, VT_ACTION_BELL));
		vt->esc_state = VT_ESnone;  /* end xterm title sequence */
		return (vt100_action(vt, VT_ACTION_XTITLE));
	case 8:			    /* backspace */
		vt->x--;
		if (vt->x < 1)
			vt->x = 1;
		return (0);
	case 9:			    /* tab */
		vt->text[((vt->y - 1) * vt->cols) + (vt->x - 1)] = ' ';
		vt->attrs[((vt->y - 1) * vt->cols) + (vt->x - 1)] =
		    vt->current_attr;
		vt->x++;
		while ((vt->x % 8) != 1) {
			vt->text[((vt->y - 1) * vt->cols) + (vt->x - 1)] =
			    ' ';
			vt->attrs[((vt->y - 1) * vt->cols) + (vt->x - 1)] =
			    vt->current_attr;
			vt->x++;
		}
		return (0);
	case 10:
		return (vt100_action(vt, VT_ACTION_LF));	/* newline */
	case 13:
		return (vt100_action(vt, VT_ACTION_CR));	/* carriage return */
	case 14:
	case 15:
		return (0);		    /* IGNORED: ^N, ^O */
	case 24:
	case 26:
		vt->esc_state = VT_ESnone;
		return (0);		    /* end esc sequence */
	case 27:
		vt->esc_state = VT_ESesc;
		return (0);		    /* escape sequence */
	case 127:			    /* delete character */
		vt->par[0] = 0;
		vt->npar = 0;
		return (vt100_action(vt, VT_ACTION_DEL_CHAR));
	case 128 + 27:
		vt->esc_state = VT_ESsquare;
		return (0);
	}

	if (vt->esc_state == VT_ESnone) {   /* normal character - just add it */

		if (c < 32)
			return (0);	    /* ignore control characters */

		if (vt->x > vt->cols) {	    /* at end of line */
			vt->x = 1;
			vt->y++;
			if (vt->y > vt->rows) {	/* end of screen */
				r = vt100_action(vt, VT_ACTION_SCROLL_UP);
				vt->y = vt->rows;
				return (r);
			}
		}

		vt->text[((vt->y - 1) * vt->cols) + (vt->x - 1)] = c;
		vt->attrs[((vt->y - 1) * vt->cols) + (vt->x - 1)] =
		    vt->current_attr;
		vt->x++;
		return (0);
	}

	switch (vt->esc_state) {

	case VT_ESesc:			    /* at start of an escape sequence */
		vt->esc_state = VT_ESnone;
		switch (c) {
		case ']':
			vt->esc_state = VT_ESnonstd;
			return (0);
		case '[':
			vt->esc_state = VT_ESsquare;
			return (0);
		case '%':
			vt->esc_state = VT_ESpercent;
			return (0);
		case '(':
			vt->esc_state = VT_ESsetG0;
			return (0);
		case ')':
			vt->esc_state = VT_ESsetG1;
			return (0);
		case '#':
			vt->esc_state = VT_EShash;
			return (0);
		case 'E':
			vt->x = 1;
			return (vt100_action(vt, VT_ACTION_LF));
		case 'M':
			return (vt100_action(vt, VT_ACTION_RI));
		case 'D':
			return (vt100_action(vt, VT_ACTION_LF));
		case 'H':
			return (0);	    /* IGNORED: set tab stops */
		case 'Z':
			return (vt100_action(vt, VT_ACTION_ID));
		case '7':
			return (vt100_action(vt, VT_ACTION_SAVE_CUR));
		case '8':
			return (vt100_action(vt, VT_ACTION_RESTORE_CUR));
		case 'c':
			return (vt100_action(vt, VT_ACTION_RESET));
		case '>':		    /* numeric keypad *//* IGNORED */
		case '=':
			return (0);	    /* application keypad *//* IGNORED */
		}
		return (0);

	case VT_ESnonstd:		    /* sequence starting ESC ] */
		vt->esc_state = VT_ESnone;
		if (c != '2')
			return (0);	    /* ignore palette sequences etc */
		vt->xtpos = -1;
		vt->xtitle[0] = 0;
		vt->esc_state = VT_ESxtitle;	/* start reading xterm title */
		return (0);

	case VT_ESpercent:		    /* sequence starting ESC % */
		vt->esc_state = VT_ESnone;  /* ignore UTF stuff */
		return (0);

	case VT_ESfunckey:		    /* sequence starting ESC [ [ */
		vt->esc_state = VT_ESnone;  /* ignore */
		return (0);

	case VT_EShash:		    /* sequence starting ESC # */
		vt->esc_state = VT_ESnone;
		if (c == 8) {		    /* fill screen with Es */
			return (vt100_action(vt, VT_ACTION_DECALIGN));
		}
		return (0);

	case VT_ESsetG0:		    /* sequence starting ESC ( */
	case VT_ESsetG1:		    /* sequence starting ESC ) */
		vt->esc_state = VT_ESnone;  /* IGNORED: setG0|1 */
		return (0);

	case VT_ESxtitle:		    /* in xterm title sequence */
		if (vt->xtpos >= 0) {
			vt->xtitle[vt->xtpos] = c;
			vt->xtitle[vt->xtpos + 1] = 0;
		}
		vt->xtpos++;
		if (vt->xtpos >= (VT_MAX_XTITLE - 2)) {	/* stop if too long */
			vt->esc_state = VT_ESnone;
			return (vt100_action(vt, VT_ACTION_XTITLE));
		}
		return (0);

	case VT_ESsquare:		    /* sequence starting ESC [ */
		for (r = 0; r < VT_MAX_PAR; r++)
			vt->par[r] = 0;
		vt->npar = 0;
		vt->esc_state = VT_ESgetpars;
		if (c == '[') {
			vt->esc_state = VT_ESfunckey;
			return (0);
		}
		vt->question = (c == '?');
		if (vt->question)
			return (0);

	case VT_ESgetpars:		    /* reading parameters in sequence */
		if (c == ';' && vt->npar < VT_MAX_PAR) {
			vt->npar++;
			return (0);
		} else if (c >= '0' && c <= '9') {
			vt->par[vt->npar] *= 10;
			vt->par[vt->npar] += c - '0';
			return (0);
		} else
			vt->esc_state = VT_ESgotpars;

	case VT_ESgotpars:		    /* finished reading parameters */
		vt->esc_state = VT_ESnone;
		switch (c) {
		case 'h':
			return (vt100_action(vt, VT_ACTION_SET_MODE));
		case 'l':
			return (vt100_action(vt, VT_ACTION_RESET_MODE));
		case 'n':
			if (!vt->question) {
				if (vt->par[0] == 5)
					return (vt100_action
						(vt, VT_ACTION_STATUS));
				if (vt->par[0] == 6)
					return (vt100_action
						(vt, VT_ACTION_CURSOR));
			}
			return (0);
		}

		vt->question = 0;

		switch (c) {
		case 'A':		    /* cursor move up */
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = vt->x;
			vt->new_y = vt->y - vt->par[0];
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'B':		    /* cursor move down */
		case 'e':
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = vt->x;
			vt->new_y = vt->y + vt->par[0];
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'C':		    /* cursor move right */
		case 'a':
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = vt->x + vt->par[0];
			vt->new_y = vt->y;
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'D':		    /* cursor move left */
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = vt->x + vt->par[0];
			vt->new_y = vt->y;
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'E':		    /* move down some lines */
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = 1;
			vt->new_y = vt->y + vt->par[0];
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'F':		    /* move up some lines */
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = 1;
			vt->new_y = vt->y - vt->par[0];
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'G':		    /* cursor move on same row */
		case '`':
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = vt->par[0];
			vt->new_y = vt->y;
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'd':		    /* cursor move on same col */
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			vt->new_x = vt->x;
			vt->new_y = vt->par[0];
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'H':		    /* cursor move to (y,x) */
		case 'f':
			if (vt->par[0] == 0)
				vt->par[0] = 1;
			if (vt->par[1] == 0)
				vt->par[1] = 1;
			vt->new_x = vt->par[1];
			vt->new_y = vt->par[0];
			return (vt100_action(vt, VT_ACTION_GOTO));
		case 'J':
			vt->par[0] += 10;
			c = 'K';
		case 'K':
			return (vt100_action(vt, VT_ACTION_CLEAR));
		case 'L':
			return (vt100_action(vt, VT_ACTION_INS_LINE));
		case 'M':
			return (vt100_action(vt, VT_ACTION_DEL_LINE));
		case '@':
			return (vt100_action(vt, VT_ACTION_INS_CHAR));
		case 'P':
			return (vt100_action(vt, VT_ACTION_DEL_CHAR));
		case 'c':
			return (vt100_action(vt, VT_ACTION_ID));
		case 'g':
			return (0);	    /* IGNORED: clear tab stops */
		case 'm':
			return (vt100_action(vt, VT_ACTION_RENDITION));
		case 'r':
			return (0);	    /* IGNORED: set scrolling region */
		case 'S':
			return (0);	    /* IGNORED: scroll region up */
		case 'T':
			return (0);	    /* IGNORED: scroll region down */
		case 's':
			return (vt100_action(vt, VT_ACTION_SAVE_CUR));
		case 'u':
			return (vt100_action(vt, VT_ACTION_RESTORE_CUR));
		}
		return (0);

	default:
		vt->esc_state = VT_ESnone;
		return (0);
	}

	return (0);
}

/* EOF */
