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