OpenSolaris_b135/lib/libxcurses2/src/libc/xcurses/wgetch.c

/*
 * CDDL HEADER START
 *
 * The contents of this file are subject to the terms of the
 * Common Development and Distribution License, Version 1.0 only
 * (the "License").  You may not use this file except in compliance
 * with the License.
 *
 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
 * or http://www.opensolaris.org/os/licensing.
 * See the License for the specific language governing permissions
 * and limitations under the License.
 *
 * When distributing Covered Code, include this CDDL HEADER in each
 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
 * If applicable, add the following below this CDDL HEADER, with the
 * fields enclosed by brackets "[]" replaced with your own identifying
 * information: Portions Copyright [yyyy] [name of copyright owner]
 *
 * CDDL HEADER END
 */
/*
 * Copyright (c) 1995-1998, 2000 by Sun Microsystems, Inc.
 * All rights reserved.
 */

#pragma ident	"%Z%%M%	%I%	%E% SMI"

/* LINTLIBRARY */

/*
 * wgetch.c
 *
 * XCurses Library
 *
 * Copyright 1990, 1995 by Mortice Kern Systems Inc.  All rights reserved.
 *
 */

#ifdef M_RCSID
#ifndef lint
static char rcsID[] =
"$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
"libxcurses/src/libc/xcurses/rcs/wgetch.c 1.23 1998/06/05 16:38:43 "
"cbates Exp $";
#endif
#endif

#include <private.h>
#include <string.h>
#include <errno.h>

static struct termios	read_termios;

int
iqContainsFullLine(void)
{
	int	i;

	if (!(PTERMIOS(_prog)->c_lflag & ICANON)) {
		/*
		 * Non-canonical mode ...
		 * Don't care about full lines ... continue
		 */
		return (1);
	}
	if (read_termios.c_lflag & ICANON) {
		/*
		 * Terminal subsystem worries about lines etc. ...
		 * continue ...
		 */
		return (1);
	}
	/* We turned off ICANON so we have to do it ourselves */
	if ((read_termios.c_cc[VMIN] == 0) &&
		(read_termios.c_cc[VTIME] != 0)) {
		/* We set delay mode. Only error if noting in the read */
		return (!iqIsEmpty());
	}
	for (i = __m_screen->_unget._count - 1; i >= 0; i--) {
		int	ch = __m_screen->_unget._stack[i];
		if (PTERMIOS(_shell)->c_cc[VEOL] == ch)
			return (1);
		if ('\n' == ch)
			return (1);
	}
	return (0);
}

void
iqPush(unsigned int ch)
{
	if (__m_screen->_unget._count >= __m_screen->_unget._size)
		return;
	__m_screen->_unget._stack[__m_screen->_unget._count++] =
		(int) ch;
}

void
iqAdd(unsigned int ch)
{
	int	count;

	if (++(__m_screen->_unget._count) >= __m_screen->_unget._size)
		__m_screen->_unget._count = __m_screen->_unget._size - 1;
	count = __m_screen->_unget._count - 1;
	if (count) {
		(void) memmove(__m_screen->_unget._stack + 1,
			__m_screen->_unget._stack, count * sizeof (int));
	}
	__m_screen->_unget._stack[0] = (int) ch;
}

int
iqIsEmpty(void)
{
	return (__m_screen->_unget._count == 0);
}

void
iqReset(void)
{
	__m_screen->_unget._count = 0;
}

/* Assumes count > 0 */
int
iqPull(void)
{
	int	ch;

	ch = __m_screen->_unget._stack[--(__m_screen->_unget._count)];
	return (ch);
}

/* Discard n characters from front of Q */
void
iqTrash(int n)
{
	__m_screen->_unget._count -= n;
	if (__m_screen->_unget._count < 0) {
		__m_screen->_unget._count = 0;
	}
}

int
iqGetNth(int n)
{
	int	ch;

	if (__m_screen->_unget._count - n <= 0) {
		return (EOF);
	}
	ch = __m_screen->_unget._stack[__m_screen->_unget._count - n - 1];
	return (ch);
}


struct termios
__m_tty_override_mode(int vmin, int vtime)
{
	struct termios	rval;
	struct termios	newstuff;

	rval = newstuff = *PTERMIOS(_actual);

	/* If halfdelay mode. Leave canonical mode intact */
	if (!(vmin == 0 && vtime == 0) &&
		(cur_term->_flags & __TERM_HALF_DELAY))
		return (rval);

	/* If blocking mode. Leave canonical mode intact */
	if (vmin == 1)
		return (rval);

	/* VMIN and VTIME trash VEOL and VEOF so canonical cannot work */
	newstuff.c_cc[VMIN] = (cc_t) vmin;
	newstuff.c_cc[VTIME] = (cc_t) vtime;
	newstuff.c_lflag &= ~ICANON;

	(void) __m_tty_set(&newstuff);
	return (rval);
}

int
__m_read_input_char(int *pChar)
{
	if (req_for_input != NULL) {
		(void) TPUTS(req_for_input, 1, __m_outc);
	}
	clearerr(__m_screen->_if);
	*pChar = 0;
	/* save actual setting for later test */
	read_termios = *PTERMIOS(_actual);

	errno = 0;
	if ((*pChar = fgetc(__m_screen->_if)) == EOF) {
		return ((errno) ? ERR : OK);
	}

	if (((PTERMIOS(_prog)->c_cflag & CSIZE) != CS8) && (*pChar != EOF))
		*pChar &= 0x7f;
	return (OK);
}

int
__m_typeahead_read_input_char(int *pChar)
{
	unsigned char	ch;
	ssize_t	r;

	if (req_for_input != NULL) {
		(void) TPUTS(req_for_input, 1, __m_outc);
	}

	*pChar = 0;
	/* save actual setting for later test */
	read_termios = *PTERMIOS(_actual);

	errno = 0;
	if ((r = read(__m_screen->_kfd, (void *)&ch, 1)) > 0) {
		if ((PTERMIOS(_prog)->c_cflag & CSIZE) != CS8) {
			*pChar = ch & 0x7f;
		} else {
			*pChar = (int)ch;
		}
		return (OK);
	} else if (r == 0) {
		*pChar = EOF;
		return (OK);
	} else {
		return (ERR);
	}
}


static int	klugeTypeaheadInGetch = 0;

int
pollTypeahead(void)
{
	struct termios	save;
	int	ch;

	if (!(__m_screen->_flags & S_ISATTY) ||
		!(__m_screen->_flags & S_TYPEAHEAD_OK)) {
		/* Typeahead disabled */
		return (0);
	}
	save = __m_tty_override_mode(0, 0);
	while (__m_typeahead_read_input_char(&ch) == OK) {
		if (ch == EOF)
			break;
		iqAdd(ch);
	}
	(void) __m_tty_set(&save);
	/* if in wgetch, always do refresh */
	return ((klugeTypeaheadInGetch) ? 0 : !iqIsEmpty());
}

/*
 * Push single-byte character back onto the input queue.
 *
 * MKS EXTENSION permits the return value of wgetch(), which
 * can be a KEY_ value, to be pushed back.
 */
int
ungetch(int ch)
{
	iqPush(ch);
	return (OK);
}

/*
 * Return true if the SCREEN's stream has an I/O error.
 * Ignore the window parameter.
 */
/* ARGSUSED */
int
__xc_ferror(void *w)
{
	return (ferror(__m_screen->_if));
}

/* ARGSUSED */
int
__xc_ungetc(int ch, void *w)
{
	iqPush(ch);
	return (1);
}

/*
 * Return true if the SCREEN's stream has seen EOF.
 * Ignore the window parameter.
 */
/* ARGSUSED */
int
__xc_feof(void *w)
{
	return (feof(__m_screen->_if));
}

/*
 * Clear the error and eof flags of the SCREEN's stream.
 * Ignore the window parameter.
 */
/* ARGSUSED */
void
__xc_clearerr(void *w)
{
	clearerr(__m_screen->_if);
}

int
__m_echo(WINDOW *w, int ch)
{
	if (!(__m_screen->_flags & S_ECHO))
		return (ch);
	if (!(0 <= ch && ch != EOF)) {
		(void) beep();
		return (ERR);
	}
	if (ch == '\b') {
		if (w->_curx <= 0) {
			(void) beep();
			return (ch);
		}
		w->_curx--;
		(void) wdelch(w);
	} else {
		(void) waddch(w, ch);
	}
	(void) wrefresh(w);
	return (ch);
}

int
wgetch(WINDOW *w)
{
	t_decode	*node;
	int	ch, i, timeout;
	struct termios	save;

	__m_screen->_flags |= S_TYPEAHEAD_OK;

	klugeTypeaheadInGetch = 1;
	(void) wrefresh(w);
	klugeTypeaheadInGetch = 0;

	if (iqIsEmpty()) {
	    save = __m_tty_override_mode(w->_vmin, w->_vtime);
	    if (__m_read_input_char(&ch) == ERR) {
			(void) __m_tty_set(&save);
			return (ERR);
	    }
	    if (!((ch == EOF) && (PTERMIOS(_prog)->c_lflag & ICANON))) {
			/* Put EOF on Q only in non-canonical mode */
			iqAdd(ch);
	    }
		(void) __m_tty_set(&save);
	}
	ch = iqGetNth(0);
	if (!iqContainsFullLine()) {
	    return (ERR);
	}

	/*
	 * Only check for function keys if keypad is true and we
	 * did not read a KEY_ value (which are < 0), nor EOF.
	 * It is conceivable that a KEY_ was pushed back with
	 * ungetch().
	 */
	if ((w->_flags & W_USE_KEYPAD) && 0 <= ch && ch != EOF) {
		/*
		 * Treat the termios ERASE key the same as key_backspace.
		 *
		 * We used to change the key_backspace entry to be a string
		 * containing the ERASE key in setupterm(), but this would
		 * then disable the real terminfo entry for the backspace key.
		 * Apparently VT300 terminals change the key code/sequence
		 * of the backspace key in application keypad mode.
		 * See SR 6014.
		 *
		 * Refer to _shell instead of _prog, since _shell will
		 * correctly reflect the user's prefered settings, whereas
		 * _prog may not have been initialised if both input and
		 * output have been redirected.
		 */
#ifdef _POSIX_VDISABLE
		if (PTERMIOS(_shell)->c_cc[VERASE] != _POSIX_VDISABLE)
#endif
			if (ch == PTERMIOS(_shell)->c_cc[VERASE]) {
				/* Discard ch from Q */
				(void) iqPull();
				return (KEY_BACKSPACE);
			}

		/* Begin check for function key. */
		node = (t_decode *) __m_screen->_decode;

		/* Use input stack as a queue. */
		timeout = w->_flags & W_USE_TIMEOUT;
		for (i = 1; ; i++) {
			while (node->ch != ch) {
				node = node->sibling;
				if (node == NULL)
					goto uncoded;
			}

			/* Found funuction key? */
			if (node->key != 0) {
				/* Trash all input used to make the FKey */
				iqTrash(i);
				return (__m_echo(w, node->key));
			}

			/*
			 * Get next candidate character -
			 * either from Q or input
			 */
			if ((ch = iqGetNth(i)) == EOF) {
				/*
				 * Setup interbyte timer (once only).
				 * fgetc() will return EOF if no input received,
				 * which may not be a true EOF.
				 */
				if (timeout) {
					(void) __m_tty_override_mode(0,
						M_CURSES_INTERBYTE_TIME);
				}
				timeout = 0;
				if (__m_read_input_char(&ch) == ERR)
				    return (ERR);
				/* Timeout or real eof. */
				if (ch == EOF)
					break;
				iqAdd(ch);
			}

			/* Incomplete sequence, continue. */
			node = node->child;
		}
	}
uncoded:
	/* Return first byte received or EOF. */
	ch = iqPull();
	return (__m_echo(w, ch));
}