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
iqContainsFullLine(void)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
iqPush(unsigned int ch)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
iqAdd(unsigned int ch)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
iqIsEmpty(void)115 iqIsEmpty(void)
116 {
117 return (__m_screen->_unget._count == 0);
118 }
119
120 void
iqReset(void)121 iqReset(void)
122 {
123 __m_screen->_unget._count = 0;
124 }
125
126 /* Assumes count > 0 */
127 int
iqPull(void)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
iqTrash(int n)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
iqGetNth(int n)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
__m_tty_override_mode(int vmin,int vtime)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
__m_read_input_char(int * pChar)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
__m_typeahead_read_input_char(int * pChar)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
pollTypeahead(void)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
ungetch(int ch)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
__xc_ferror(void * w)280 __xc_ferror(void *w)
281 {
282 return (ferror(__m_screen->_if));
283 }
284
285 /* ARGSUSED */
286 int
__xc_ungetc(int ch,void * w)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
__xc_feof(void * w)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
__xc_clearerr(void * w)310 __xc_clearerr(void *w)
311 {
312 clearerr(__m_screen->_if);
313 }
314
315 int
__m_echo(WINDOW * w,int ch)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
wgetch(WINDOW * w)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