1/*
2 * CDDL HEADER START
3 *
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License, Version 1.0 only
6 * (the "License").  You may not use this file except in compliance
7 * with the License.
8 *
9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
10 * or http://www.opensolaris.org/os/licensing.
11 * See the License for the specific language governing permissions
12 * and limitations under the License.
13 *
14 * When distributing Covered Code, include this CDDL HEADER in each
15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
16 * If applicable, add the following below this CDDL HEADER, with the
17 * fields enclosed by brackets "[]" replaced with your own identifying
18 * information: Portions Copyright [yyyy] [name of copyright owner]
19 *
20 * CDDL HEADER END
21 */
22/*
23 * Copyright (c) 1995-1998, 2000 by Sun Microsystems, Inc.
24 * All rights reserved.
25 */
26
27#pragma ident	"%Z%%M%	%I%	%E% SMI"
28
29/* LINTLIBRARY */
30
31/*
32 * wgetch.c
33 *
34 * XCurses Library
35 *
36 * Copyright 1990, 1995 by Mortice Kern Systems Inc.  All rights reserved.
37 *
38 */
39
40#ifdef M_RCSID
41#ifndef lint
42static char rcsID[] =
43"$Header: /team/ps/sun_xcurses/archive/local_changes/xcurses/src/lib/"
44"libxcurses/src/libc/xcurses/rcs/wgetch.c 1.23 1998/06/05 16:38:43 "
45"cbates Exp $";
46#endif
47#endif
48
49#include <private.h>
50#include <string.h>
51#include <errno.h>
52
53static struct termios	read_termios;
54
55int
56iqContainsFullLine(void)
57{
58	int	i;
59
60	if (!(PTERMIOS(_prog)->c_lflag & ICANON)) {
61		/*
62		 * Non-canonical mode ...
63		 * Don't care about full lines ... continue
64		 */
65		return (1);
66	}
67	if (read_termios.c_lflag & ICANON) {
68		/*
69		 * Terminal subsystem worries about lines etc. ...
70		 * continue ...
71		 */
72		return (1);
73	}
74	/* We turned off ICANON so we have to do it ourselves */
75	if ((read_termios.c_cc[VMIN] == 0) &&
76		(read_termios.c_cc[VTIME] != 0)) {
77		/* We set delay mode. Only error if noting in the read */
78		return (!iqIsEmpty());
79	}
80	for (i = __m_screen->_unget._count - 1; i >= 0; i--) {
81		int	ch = __m_screen->_unget._stack[i];
82		if (PTERMIOS(_shell)->c_cc[VEOL] == ch)
83			return (1);
84		if ('\n' == ch)
85			return (1);
86	}
87	return (0);
88}
89
90void
91iqPush(unsigned int ch)
92{
93	if (__m_screen->_unget._count >= __m_screen->_unget._size)
94		return;
95	__m_screen->_unget._stack[__m_screen->_unget._count++] =
96		(int) ch;
97}
98
99void
100iqAdd(unsigned int ch)
101{
102	int	count;
103
104	if (++(__m_screen->_unget._count) >= __m_screen->_unget._size)
105		__m_screen->_unget._count = __m_screen->_unget._size - 1;
106	count = __m_screen->_unget._count - 1;
107	if (count) {
108		(void) memmove(__m_screen->_unget._stack + 1,
109			__m_screen->_unget._stack, count * sizeof (int));
110	}
111	__m_screen->_unget._stack[0] = (int) ch;
112}
113
114int
115iqIsEmpty(void)
116{
117	return (__m_screen->_unget._count == 0);
118}
119
120void
121iqReset(void)
122{
123	__m_screen->_unget._count = 0;
124}
125
126/* Assumes count > 0 */
127int
128iqPull(void)
129{
130	int	ch;
131
132	ch = __m_screen->_unget._stack[--(__m_screen->_unget._count)];
133	return (ch);
134}
135
136/* Discard n characters from front of Q */
137void
138iqTrash(int n)
139{
140	__m_screen->_unget._count -= n;
141	if (__m_screen->_unget._count < 0) {
142		__m_screen->_unget._count = 0;
143	}
144}
145
146int
147iqGetNth(int n)
148{
149	int	ch;
150
151	if (__m_screen->_unget._count - n <= 0) {
152		return (EOF);
153	}
154	ch = __m_screen->_unget._stack[__m_screen->_unget._count - n - 1];
155	return (ch);
156}
157
158
159struct termios
160__m_tty_override_mode(int vmin, int vtime)
161{
162	struct termios	rval;
163	struct termios	newstuff;
164
165	rval = newstuff = *PTERMIOS(_actual);
166
167	/* If halfdelay mode. Leave canonical mode intact */
168	if (!(vmin == 0 && vtime == 0) &&
169		(cur_term->_flags & __TERM_HALF_DELAY))
170		return (rval);
171
172	/* If blocking mode. Leave canonical mode intact */
173	if (vmin == 1)
174		return (rval);
175
176	/* VMIN and VTIME trash VEOL and VEOF so canonical cannot work */
177	newstuff.c_cc[VMIN] = (cc_t) vmin;
178	newstuff.c_cc[VTIME] = (cc_t) vtime;
179	newstuff.c_lflag &= ~ICANON;
180
181	(void) __m_tty_set(&newstuff);
182	return (rval);
183}
184
185int
186__m_read_input_char(int *pChar)
187{
188	if (req_for_input != NULL) {
189		(void) TPUTS(req_for_input, 1, __m_outc);
190	}
191	clearerr(__m_screen->_if);
192	*pChar = 0;
193	/* save actual setting for later test */
194	read_termios = *PTERMIOS(_actual);
195
196	errno = 0;
197	if ((*pChar = fgetc(__m_screen->_if)) == EOF) {
198		return ((errno) ? ERR : OK);
199	}
200
201	if (((PTERMIOS(_prog)->c_cflag & CSIZE) != CS8) && (*pChar != EOF))
202		*pChar &= 0x7f;
203	return (OK);
204}
205
206int
207__m_typeahead_read_input_char(int *pChar)
208{
209	unsigned char	ch;
210	ssize_t	r;
211
212	if (req_for_input != NULL) {
213		(void) TPUTS(req_for_input, 1, __m_outc);
214	}
215
216	*pChar = 0;
217	/* save actual setting for later test */
218	read_termios = *PTERMIOS(_actual);
219
220	errno = 0;
221	if ((r = read(__m_screen->_kfd, (void *)&ch, 1)) > 0) {
222		if ((PTERMIOS(_prog)->c_cflag & CSIZE) != CS8) {
223			*pChar = ch & 0x7f;
224		} else {
225			*pChar = (int)ch;
226		}
227		return (OK);
228	} else if (r == 0) {
229		*pChar = EOF;
230		return (OK);
231	} else {
232		return (ERR);
233	}
234}
235
236
237static int	klugeTypeaheadInGetch = 0;
238
239int
240pollTypeahead(void)
241{
242	struct termios	save;
243	int	ch;
244
245	if (!(__m_screen->_flags & S_ISATTY) ||
246		!(__m_screen->_flags & S_TYPEAHEAD_OK)) {
247		/* Typeahead disabled */
248		return (0);
249	}
250	save = __m_tty_override_mode(0, 0);
251	while (__m_typeahead_read_input_char(&ch) == OK) {
252		if (ch == EOF)
253			break;
254		iqAdd(ch);
255	}
256	(void) __m_tty_set(&save);
257	/* if in wgetch, always do refresh */
258	return ((klugeTypeaheadInGetch) ? 0 : !iqIsEmpty());
259}
260
261/*
262 * Push single-byte character back onto the input queue.
263 *
264 * MKS EXTENSION permits the return value of wgetch(), which
265 * can be a KEY_ value, to be pushed back.
266 */
267int
268ungetch(int ch)
269{
270	iqPush(ch);
271	return (OK);
272}
273
274/*
275 * Return true if the SCREEN's stream has an I/O error.
276 * Ignore the window parameter.
277 */
278/* ARGSUSED */
279int
280__xc_ferror(void *w)
281{
282	return (ferror(__m_screen->_if));
283}
284
285/* ARGSUSED */
286int
287__xc_ungetc(int ch, void *w)
288{
289	iqPush(ch);
290	return (1);
291}
292
293/*
294 * Return true if the SCREEN's stream has seen EOF.
295 * Ignore the window parameter.
296 */
297/* ARGSUSED */
298int
299__xc_feof(void *w)
300{
301	return (feof(__m_screen->_if));
302}
303
304/*
305 * Clear the error and eof flags of the SCREEN's stream.
306 * Ignore the window parameter.
307 */
308/* ARGSUSED */
309void
310__xc_clearerr(void *w)
311{
312	clearerr(__m_screen->_if);
313}
314
315int
316__m_echo(WINDOW *w, int ch)
317{
318	if (!(__m_screen->_flags & S_ECHO))
319		return (ch);
320	if (!(0 <= ch && ch != EOF)) {
321		(void) beep();
322		return (ERR);
323	}
324	if (ch == '\b') {
325		if (w->_curx <= 0) {
326			(void) beep();
327			return (ch);
328		}
329		w->_curx--;
330		(void) wdelch(w);
331	} else {
332		(void) waddch(w, ch);
333	}
334	(void) wrefresh(w);
335	return (ch);
336}
337
338int
339wgetch(WINDOW *w)
340{
341	t_decode	*node;
342	int	ch, i, timeout;
343	struct termios	save;
344
345	__m_screen->_flags |= S_TYPEAHEAD_OK;
346
347	klugeTypeaheadInGetch = 1;
348	(void) wrefresh(w);
349	klugeTypeaheadInGetch = 0;
350
351	if (iqIsEmpty()) {
352	    save = __m_tty_override_mode(w->_vmin, w->_vtime);
353	    if (__m_read_input_char(&ch) == ERR) {
354			(void) __m_tty_set(&save);
355			return (ERR);
356	    }
357	    if (!((ch == EOF) && (PTERMIOS(_prog)->c_lflag & ICANON))) {
358			/* Put EOF on Q only in non-canonical mode */
359			iqAdd(ch);
360	    }
361		(void) __m_tty_set(&save);
362	}
363	ch = iqGetNth(0);
364	if (!iqContainsFullLine()) {
365	    return (ERR);
366	}
367
368	/*
369	 * Only check for function keys if keypad is true and we
370	 * did not read a KEY_ value (which are < 0), nor EOF.
371	 * It is conceivable that a KEY_ was pushed back with
372	 * ungetch().
373	 */
374	if ((w->_flags & W_USE_KEYPAD) && 0 <= ch && ch != EOF) {
375		/*
376		 * Treat the termios ERASE key the same as key_backspace.
377		 *
378		 * We used to change the key_backspace entry to be a string
379		 * containing the ERASE key in setupterm(), but this would
380		 * then disable the real terminfo entry for the backspace key.
381		 * Apparently VT300 terminals change the key code/sequence
382		 * of the backspace key in application keypad mode.
383		 * See SR 6014.
384		 *
385		 * Refer to _shell instead of _prog, since _shell will
386		 * correctly reflect the user's prefered settings, whereas
387		 * _prog may not have been initialised if both input and
388		 * output have been redirected.
389		 */
390#ifdef _POSIX_VDISABLE
391		if (PTERMIOS(_shell)->c_cc[VERASE] != _POSIX_VDISABLE)
392#endif
393			if (ch == PTERMIOS(_shell)->c_cc[VERASE]) {
394				/* Discard ch from Q */
395				(void) iqPull();
396				return (KEY_BACKSPACE);
397			}
398
399		/* Begin check for function key. */
400		node = (t_decode *) __m_screen->_decode;
401
402		/* Use input stack as a queue. */
403		timeout = w->_flags & W_USE_TIMEOUT;
404		for (i = 1; ; i++) {
405			while (node->ch != ch) {
406				node = node->sibling;
407				if (node == NULL)
408					goto uncoded;
409			}
410
411			/* Found funuction key? */
412			if (node->key != 0) {
413				/* Trash all input used to make the FKey */
414				iqTrash(i);
415				return (__m_echo(w, node->key));
416			}
417
418			/*
419			 * Get next candidate character -
420			 * either from Q or input
421			 */
422			if ((ch = iqGetNth(i)) == EOF) {
423				/*
424				 * Setup interbyte timer (once only).
425				 * fgetc() will return EOF if no input received,
426				 * which may not be a true EOF.
427				 */
428				if (timeout) {
429					(void) __m_tty_override_mode(0,
430						M_CURSES_INTERBYTE_TIME);
431				}
432				timeout = 0;
433				if (__m_read_input_char(&ch) == ERR)
434				    return (ERR);
435				/* Timeout or real eof. */
436				if (ch == EOF)
437					break;
438				iqAdd(ch);
439			}
440
441			/* Incomplete sequence, continue. */
442			node = node->child;
443		}
444	}
445uncoded:
446	/* Return first byte received or EOF. */
447	ch = iqPull();
448	return (__m_echo(w, ch));
449}
450