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
42 static 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 
53 static struct termios	read_termios;
54 
55 int
56 iqContainsFullLine(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 
90 void
91 iqPush(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 
99 void
100 iqAdd(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 
114 int
115 iqIsEmpty(void)
116 {
117 	return (__m_screen->_unget._count == 0);
118 }
119 
120 void
121 iqReset(void)
122 {
123 	__m_screen->_unget._count = 0;
124 }
125 
126 /* Assumes count > 0 */
127 int
128 iqPull(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 */
137 void
138 iqTrash(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 
146 int
147 iqGetNth(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 
159 struct 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 
185 int
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 
206 int
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 
237 static int	klugeTypeaheadInGetch = 0;
238 
239 int
240 pollTypeahead(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  */
267 int
268 ungetch(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 */
279 int
280 __xc_ferror(void *w)
281 {
282 	return (ferror(__m_screen->_if));
283 }
284 
285 /* ARGSUSED */
286 int
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 */
298 int
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 */
309 void
310 __xc_clearerr(void *w)
311 {
312 	clearerr(__m_screen->_if);
313 }
314 
315 int
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 
338 int
339 wgetch(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 	}
445 uncoded:
446 	/* Return first byte received or EOF. */
447 	ch = iqPull();
448 	return (__m_echo(w, ch));
449 }
450