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