xref: /netbsd-src/lib/libcurses/get_wch.c (revision 8b0f9554ff8762542c4defc4f70e1eb76fb508fa)
1 /*   $NetBSD: get_wch.c,v 1.5 2007/12/08 18:38:11 jdc Exp $ */
2 
3 /*
4  * Copyright (c) 2005 The NetBSD Foundation Inc.
5  * All rights reserved.
6  *
7  * This code is derived from code donated to the NetBSD Foundation
8  * by Ruibiao Qiu <ruibiao@arl.wustl.edu,ruibiao@gmail.com>.
9  *
10  *
11  * Redistribution and use in source and binary forms, with or without
12  * modification, are permitted provided that the following conditions
13  * are met:
14  * 1. Redistributions of source code must retain the above copyright
15  *	notice, this list of conditions and the following disclaimer.
16  * 2. Redistributions in binary form must reproduce the above copyright
17  *	notice, this list of conditions and the following disclaimer in the
18  *	documentation and/or other materials provided with the distribution.
19  * 3. Neither the name of the NetBSD Foundation nor the names of its
20  *	contributors may be used to endorse or promote products derived
21  *	from this software without specific prior written permission.
22  *
23  * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND
24  * CONTRIBUTORS ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES,
25  * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
26  * MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
27  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
28  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
29  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
30  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
31  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
32  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
33  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
34  * SUCH DAMAGE.
35  */
36 
37 #include <sys/cdefs.h>
38 #ifndef lint
39 __RCSID("$NetBSD: get_wch.c,v 1.5 2007/12/08 18:38:11 jdc Exp $");
40 #endif						  /* not lint */
41 
42 #include <string.h>
43 #include <stdlib.h>
44 #include <unistd.h>
45 #include <stdio.h>
46 #include "curses.h"
47 #include "curses_private.h"
48 #include "keymap.h"
49 
50 static short   wstate;		  /* state of the wcinkey function */
51 extern short state;		/* storage declared in getch.c */
52 
53 /* prototypes for private functions */
54 static int inkey(wchar_t *wc, int to, int delay);
55 
56 /*
57  * __init_get_wch - initialise all the pointers & structures needed to make
58  * get_wch work in keypad mode.
59  *
60  */
61 void
62 __init_get_wch(SCREEN *screen)
63 {
64 #ifndef HAVE_WCHAR
65 	return;
66 #else
67 	wstate = INKEY_NORM;
68 	memset( &screen->cbuf, 0, MAX_CBUF_SIZE * sizeof( int ));
69 	screen->cbuf_head = screen->cbuf_tail = screen->cbuf_cur = 0;
70 #endif /* HAVE_WCHAR */
71 }
72 
73 
74 /*
75  * inkey - do the work to process keyboard input, check for multi-key
76  * sequences and return the appropriate symbol if we get a match.
77  *
78  */
79 
80 int
81 inkey(wchar_t *wc, int to, int delay)
82 {
83 #ifndef HAVE_WCHAR
84 	return ERR;
85 #else
86 	wchar_t		 k = 0;
87 	int		  c, mapping, ret = 0;
88 	size_t	  mlen = 0;
89 	keymap_t	*current = _cursesi_screen->base_keymap;
90 	FILE		*infd = _cursesi_screen->infd;
91 	int		 *start = &_cursesi_screen->cbuf_head,
92 				*working = &_cursesi_screen->cbuf_cur,
93 				*end = &_cursesi_screen->cbuf_tail;
94 	char		*inbuf = &_cursesi_screen->cbuf[ 0 ];
95 
96 #ifdef DEBUG
97 	__CTRACE(__CTRACE_INPUT, "inkey (%p, %d, %d)\n", wc, to, delay);
98 #endif
99 	for (;;) { /* loop until we get a complete key sequence */
100 		if (wstate == INKEY_NORM) {
101 			if (delay && __timeout(delay) == ERR)
102 				return ERR;
103 			c = getchar();
104 			if (c == WEOF) {
105 				clearerr(infd);
106 				return ERR;
107 			}
108 
109 			if (delay && (__notimeout() == ERR))
110 				return ERR;
111 
112 			k = (wchar_t) c;
113 #ifdef DEBUG
114 			__CTRACE(__CTRACE_INPUT,
115 			    "inkey (wstate normal) got '%s'\n", unctrl(k));
116 #endif
117 
118 			inbuf[ *end ] = k;
119 			*end = ( *end + 1 ) % MAX_CBUF_SIZE;
120 			*working = *start;
121 			wstate = INKEY_ASSEMBLING; /* go to assembling state */
122 #ifdef DEBUG
123 			__CTRACE(__CTRACE_INPUT,
124 			    "inkey: NORM=>ASSEMBLING: start(%d), "
125 			    "current(%d), end(%d)\n", *start, *working, *end);
126 #endif /* DEBUG */
127 		} else if (wstate == INKEY_BACKOUT) {
128 			k = inbuf[*working];
129 			*working = ( *working + 1 ) % MAX_CBUF_SIZE;
130 			if (*working == *end) {	/* see if run out of keys */
131 				/* if so, switch to assembling */
132 				wstate = INKEY_ASSEMBLING;
133 #ifdef DEBUG
134 				__CTRACE(__CTRACE_INPUT,
135 				    "inkey: BACKOUT=>ASSEMBLING, start(%d), "
136 				    "current(%d), end(%d)\n",
137 				    *start, *working, *end);
138 #endif /* DEBUG */
139 			}
140 		} else if (wstate == INKEY_ASSEMBLING) {
141 			/* assembling a key sequence */
142 			if (delay) {
143 				if (__timeout(to ? (ESCDELAY / 100) : delay)
144 						== ERR)
145 					return ERR;
146 			} else {
147 				if (to && (__timeout(ESCDELAY / 100) == ERR))
148 					return ERR;
149 			}
150 
151 			c = getchar();
152 			if (ferror(infd)) {
153 				clearerr(infd);
154 				return ERR;
155 			}
156 
157 			if ((to || delay) && (__notimeout() == ERR))
158 				return ERR;
159 
160 			k = (wchar_t) c;
161 #ifdef DEBUG
162 			__CTRACE(__CTRACE_INPUT,
163 			    "inkey (wstate assembling) got '%s'\n", unctrl(k));
164 #endif /* DEBUG */
165 			if (feof(infd)) { /* inter-char T/O, start backout */
166 				clearerr(infd);
167 				if (*start == *end)
168 					/* no chars in the buffer, restart */
169 					continue;
170 
171 				k = inbuf[*start];
172 				wstate = INKEY_TIMEOUT;
173 #ifdef DEBUG
174 				__CTRACE(__CTRACE_INPUT,
175 				    "inkey: ASSEMBLING=>TIMEOUT, start(%d), "
176 				    "current(%d), end(%d)\n",
177 				    *start, *working, *end);
178 #endif /* DEBUG */
179 			} else {
180 				inbuf[ *end ] = k;
181 				*working = *end;
182 				*end = ( *end + 1 ) % MAX_CBUF_SIZE;
183 #ifdef DEBUG
184 				__CTRACE(__CTRACE_INPUT,
185 				    "inkey: ASSEMBLING: start(%d), "
186 				    "current(%d), end(%d)",
187 				    *start, *working, *end);
188 #endif /* DEBUG */
189 			}
190 		} else if (wstate == INKEY_WCASSEMBLING) {
191 			/* assembling a wide char sequence */
192 			if (delay) {
193 				if (__timeout(to ? (ESCDELAY / 100) : delay)
194 						== ERR)
195 					return ERR;
196 			} else {
197 				if (to && (__timeout(ESCDELAY / 100) == ERR))
198 					return ERR;
199 			}
200 
201 			c = getchar();
202 			if (ferror(infd)) {
203 				clearerr(infd);
204 				return ERR;
205 			}
206 
207 			if ((to || delay) && (__notimeout() == ERR))
208 				return ERR;
209 
210 			k = (wchar_t) c;
211 #ifdef DEBUG
212 			__CTRACE(__CTRACE_INPUT,
213 			    "inkey (wstate wcassembling) got '%s'\n",
214 				unctrl(k));
215 #endif
216 			if (feof(infd)) { /* inter-char T/O, start backout */
217 				clearerr(infd);
218 				if (*start == *end)
219 					/* no chars in the buffer, restart */
220 					continue;
221 
222 				*wc = inbuf[*start];
223 				*working = *start
224 					= ( *start + 1 ) % MAX_CBUF_SIZE;
225 				if (*start == *end) {
226 					state = wstate = INKEY_NORM;
227 #ifdef DEBUG
228 					__CTRACE(__CTRACE_INPUT,
229 					    "inkey: WCASSEMBLING=>NORM, "
230 					    "start(%d), current(%d), end(%d)",
231 					    *start, *working, *end);
232 #endif /* DEBUG */
233 				} else {
234 					state = wstate = INKEY_BACKOUT;
235 #ifdef DEBUG
236 					__CTRACE(__CTRACE_INPUT,
237 					    "inkey: WCASSEMBLING=>BACKOUT, "
238 					    "start(%d), current(%d), end(%d)",
239 					    *start, *working, *end);
240 #endif /* DEBUG */
241 				}
242 				return OK;
243 			} else {
244 				/* assembling wide characters */
245 				inbuf[ *end ] = k;
246 				*working = *end;
247 				*end = ( *end + 1 ) % MAX_CBUF_SIZE;
248 #ifdef DEBUG
249 				__CTRACE(__CTRACE_INPUT,
250 				    "inkey: WCASSEMBLING[head(%d), "
251 				    "urrent(%d), tail(%d)]\n",
252 				    *start, *working, *end);
253 #endif /* DEBUG */
254 				ret = (int) mbrtowc( wc, inbuf + (*working), 1,
255 					&_cursesi_screen->sp );
256 #ifdef DEBUG
257 				__CTRACE(__CTRACE_INPUT,
258 				    "inkey: mbrtowc returns %d, wc(%x)\n",
259 				    ret, *wc );
260 #endif /* DEBUG */
261 				if ( ret == -2 ) {
262 					*working = (*working + 1)
263 						% MAX_CBUF_SIZE;
264 					continue;
265 				}
266 				if ( ret == 0 )
267 					ret = 1;
268 				if ( ret == -1 ) {
269 					/* return the 1st character we know */
270 					*wc = inbuf[ *start ];
271 					*working = *start = ( *start + 1 ) % MAX_CBUF_SIZE;
272 #ifdef DEBUG
273 					__CTRACE(__CTRACE_INPUT,
274 					    "inkey: Invalid wide char(%x) "
275 					    "[head(%d), current(%d), "
276 					    "tail(%d)]\n",
277 					    *wc, *start, *working, *end);
278 #endif /* DEBUG */
279 				} else { /* > 0 */
280 					/* return the wide character */
281 					*start = *working
282 					       = (*working + ret)%MAX_CBUF_SIZE;
283 #ifdef DEBUG
284 					__CTRACE(__CTRACE_INPUT,
285 					    "inkey: Wide char found(%x) "
286 					    "[head(%d), current(%d), "
287 					    "tail(%d)]\n",
288 					    *wc, *start, *working, *end);
289 #endif /* DEBUG */
290 				}
291 
292 				if (*start == *end) {	/* only one char processed */
293 					state = wstate = INKEY_NORM;
294 #ifdef DEBUG
295 					__CTRACE(__CTRACE_INPUT,
296 					    "inkey: WCASSEMBLING=>NORM, "
297 					    "start(%d), current(%d), end(%d)",
298 					    *start, *working, *end);
299 #endif /* DEBUG */
300 				} else {
301 					/* otherwise we must have more than one char to backout */
302 					state = wstate = INKEY_BACKOUT;
303 #ifdef DEBUG
304 					__CTRACE(__CTRACE_INPUT,
305 					    "inkey: WCASSEMBLING=>BACKOUT, "
306 					    "start(%d), current(%d), end(%d)",
307 					    *start, *working, *end);
308 #endif /* DEBUG */
309 				}
310 				return OK;
311 			}
312 		} else {
313 			fprintf(stderr, "Inkey wstate screwed - exiting!!!");
314 			exit(2);
315 		}
316 
317 		/*
318 		 * Check key has no special meaning and we have not
319 		 * timed out and the key has not been disabled
320 		 */
321 		mapping = current->mapping[k];
322 		if (((wstate == INKEY_TIMEOUT) || (mapping < 0))
323 				|| ((current->key[mapping]->type
324 					== KEYMAP_LEAF)
325 				&& (current->key[mapping]->enable == FALSE))) {
326 			/* wide character specific code */
327 #ifdef DEBUG
328 			__CTRACE(__CTRACE_INPUT,
329 			    "inkey: Checking for wide char\n");
330 #endif /* DEBUG */
331 			mbrtowc( NULL, NULL, 1, &_cursesi_screen->sp );
332 			*working = *start;
333 			mlen = *end > *working ?
334 				*end - *working : MAX_CBUF_SIZE - *working;
335 			if ( !mlen )
336 				return ERR;
337 #ifdef DEBUG
338 			__CTRACE(__CTRACE_INPUT,
339 			    "inkey: Check wide char[head(%d), "
340 			    "current(%d), tail(%d), mlen(%ld)]\n",
341 			    *start, *working, *end, (long) mlen);
342 #endif /* DEBUG */
343 			ret = (int) mbrtowc( wc, inbuf + (*working), mlen,
344 				&_cursesi_screen->sp );
345 #ifdef DEBUG
346 			__CTRACE(__CTRACE_INPUT,
347 			    "inkey: mbrtowc returns %d, wc(%x)\n", ret, *wc);
348 #endif /* DEBUG */
349 			if ( ret == -2 && *end < *working ) {
350 				/* second half of a wide character */
351 				*working = 0;
352 				mlen = *end;
353 				if ( mlen )
354 					ret = (int) mbrtowc( wc, inbuf, mlen,
355 						&_cursesi_screen->sp );
356 			}
357 			if ( ret == -2 && wstate != INKEY_TIMEOUT ) {
358 				*working = (*working + (int) mlen)
359 					% MAX_CBUF_SIZE;
360 				wstate = INKEY_WCASSEMBLING;
361 				continue;
362 			}
363 			if ( ret == 0 )
364 				ret = 1;
365 			if ( ret == -1 ) {
366 				/* return the first key we know about */
367 				*wc = inbuf[ *start ];
368 				*working = *start
369 					= ( *start + 1 ) % MAX_CBUF_SIZE;
370 #ifdef DEBUG
371 				__CTRACE(__CTRACE_INPUT,
372 				    "inkey: Invalid wide char(%x)[head(%d), "
373 				    "current(%d), tail(%d)]\n",
374 				    *wc, *start, *working, *end);
375 #endif /* DEBUG */
376 			} else { /* > 0 */
377 				/* return the wide character */
378 				*start = *working
379 					= ( *working + ret ) % MAX_CBUF_SIZE;
380 #ifdef DEBUG
381 				__CTRACE(__CTRACE_INPUT,
382 				    "inkey: Wide char found(%x)[head(%d), "
383 				    "current(%d), tail(%d)]\n",
384 				    *wc, *start, *working, *end);
385 #endif /* DEBUG */
386 			}
387 
388 			if (*start == *end) {	/* only one char processed */
389 				state = wstate = INKEY_NORM;
390 #ifdef DEBUG
391 				__CTRACE(__CTRACE_INPUT,
392 				    "inkey: Empty cbuf=>NORM, "
393 				    "start(%d), current(%d), end(%d)\n",
394 				    *start, *working, *end);
395 #endif /* DEBUG */
396 			} else {
397 				/* otherwise we must have more than one char to backout */
398 				state = wstate = INKEY_BACKOUT;
399 #ifdef DEBUG
400 				__CTRACE(__CTRACE_INPUT,
401 				    "inkey: Non-empty cbuf=>BACKOUT, "
402 				    "start(%d), current(%d), end(%d)\n",
403 				    *start, *working, *end);
404 #endif /* DEBUG */
405 			}
406 			return OK;
407 		} else {	/* must be part of a multikey sequence */
408 					/* check for completed key sequence */
409 			if (current->key[current->mapping[k]]->type
410 					== KEYMAP_LEAF) {
411 				/* eat the key sequence in cbuf */
412 				*start = *working = ( *working + 1 ) % MAX_CBUF_SIZE;
413 
414 				/* check if inbuf empty now */
415 #ifdef DEBUG
416 				__CTRACE(__CTRACE_INPUT,
417 				    "inkey: Key found(%s)\n",
418 				    key_name(current->key[mapping]->value.symbol));
419 #endif /* DEBUG */
420 				if (*start == *end) {
421 					/* if it is go back to normal */
422 					state = wstate = INKEY_NORM;
423 #ifdef DEBUG
424 					__CTRACE(__CTRACE_INPUT,
425 					    "[inkey]=>NORM, start(%d), "
426 					    "current(%d), end(%d)",
427 					    *start, *working, *end);
428 #endif /* DEBUG */
429 				} else {
430 					/* otherwise go to backout state */
431 					state = wstate = INKEY_BACKOUT;
432 #ifdef DEBUG
433 					__CTRACE(__CTRACE_INPUT,
434 					    "[inkey]=>BACKOUT, start(%d), "
435 					    "current(%d), end(%d)",
436 					    *start, *working, *end );
437 #endif /* DEBUG */
438 				}
439 
440 				/* return the symbol */
441 				*wc = current->key[mapping]->value.symbol;
442 				return KEY_CODE_YES;
443 			} else {
444 				/* Step to next part of multi-key sequence */
445 				current = current->key[current->mapping[k]]->value.next;
446 			}
447 		}
448 	}
449 #endif /* HAVE_WCHAR */
450 }
451 
452 /*
453  * get_wch --
454  *	Read in a wide character from stdscr.
455  */
456 int
457 get_wch(wint_t *ch)
458 {
459 #ifndef HAVE_WCHAR
460 	return ERR;
461 #else
462 	return wget_wch(stdscr, ch);
463 #endif /* HAVE_WCHAR */
464 }
465 
466 /*
467  * mvget_wch --
468  *	  Read in a character from stdscr at the given location.
469  */
470 int
471 mvget_wch(int y, int x, wint_t *ch)
472 {
473 #ifndef HAVE_WCHAR
474 	return ERR;
475 #else
476 	return mvwget_wch(stdscr, y, x, ch);
477 #endif /* HAVE_WCHAR */
478 }
479 
480 /*
481  * mvwget_wch --
482  *	  Read in a character from stdscr at the given location in the
483  *	  given window.
484  */
485 int
486 mvwget_wch(WINDOW *win, int y, int x, wint_t *ch)
487 {
488 #ifndef HAVE_WCHAR
489 	return ERR;
490 #else
491 	if (wmove(win, y, x) == ERR)
492 		return ERR;
493 
494 	return wget_wch(win, ch);
495 #endif /* HAVE_WCHAR */
496 }
497 
498 /*
499  * wget_wch --
500  *	Read in a wide character from the window.
501  */
502 int
503 wget_wch(WINDOW *win, wint_t *ch)
504 {
505 #ifndef HAVE_WCHAR
506 	return ERR;
507 #else
508 	int ret, weset;
509 	int c;
510 	FILE *infd = _cursesi_screen->infd;
511 	cchar_t wc;
512 	wchar_t inp, ws[ 2 ];
513 
514 	if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN)
515 			&& win->curx == win->maxx - 1
516 			&& win->cury == win->maxy - 1
517 			&& __echoit)
518 		return (ERR);
519 
520 	if (is_wintouched(win))
521 		wrefresh(win);
522 #ifdef DEBUG
523 	__CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, "
524 	    "__rawmode = %d, __nl = %d, flags = %#.4x\n",
525 	    __echoit, __rawmode, _cursesi_screen->nl, win->flags);
526 #endif
527 	if (_cursesi_screen->resized) {
528 		_cursesi_screen->resized = 0;
529 		*ch = KEY_RESIZE;
530 		return KEY_CODE_YES;
531 	}
532 	if (_cursesi_screen->unget_pos) {
533 #ifdef DEBUG
534 		__CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n",
535 		    _cursesi_screen->unget_pos);
536 #endif
537 		_cursesi_screen->unget_pos--;
538 		*ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos];
539 		if (__echoit) {
540 			ws[0] = *ch, ws[1] = L'\0';
541 			setcchar(&wc, ws, win->wattr, 0, NULL);
542 			wadd_wch(win, &wc);
543 		}
544 		return KEY_CODE_YES;
545 	}
546 	if (__echoit && !__rawmode) {
547 		cbreak();
548 		weset = 1;
549 	} else
550 		weset = 0;
551 
552 	__save_termios();
553 
554 	if (win->flags & __KEYPAD) {
555 		switch (win->delay) {
556 			case -1:
557 				ret = inkey(&inp,
558 					win->flags & __NOTIMEOUT ? 0 : 1, 0);
559 				break;
560 			case 0:
561 				if (__nodelay() == ERR) {
562 					__restore_termios();
563 					return ERR;
564 				}
565 				ret = inkey(&inp, 0, 0);
566 				break;
567 			default:
568 				ret = inkey(&inp,
569 					win->flags & __NOTIMEOUT ? 0 : 1,
570 					win->delay);
571 				break;
572 		}
573 		if ( ret == ERR )
574 			return ERR;
575 	} else {
576 		switch (win->delay) {
577 			case -1:
578 				break;
579 			case 0:
580 				if (__nodelay() == ERR) {
581 					__restore_termios();
582 					return ERR;
583 				}
584 				break;
585 			default:
586 				if (__timeout(win->delay) == ERR) {
587 					__restore_termios();
588 					return ERR;
589 				}
590 				break;
591 		}
592 
593 		c = getwchar();
594 		if (feof(infd)) {
595 			clearerr(infd);
596 			__restore_termios();
597 			return ERR;	/* we have timed out */
598 		}
599 
600 		if (ferror(infd)) {
601 			clearerr(infd);
602 			return ERR;
603 		} else {
604 			ret = c;
605 			inp = c;
606 		}
607 	}
608 #ifdef DEBUG
609 	if (inp > 255)
610 		/* we have a key symbol - treat it differently */
611 		/* XXXX perhaps __unctrl should be expanded to include
612 		 * XXXX the keysyms in the table....
613 		 */
614 		__CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n",
615 		    inp);
616 	else
617 		__CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp));
618 #endif
619 	if (win->delay > -1) {
620 		if (__delay() == ERR) {
621 			__restore_termios();
622 			return ERR;
623 		}
624 	}
625 
626 	__restore_termios();
627 
628 	if (__echoit) {
629 		if ( ret == KEY_CODE_YES ) {
630 			/* handle [DEL], [BS], and [LEFT] */
631 			if ( win->curx &&
632 					( inp == KEY_DC ||
633 					  inp == KEY_BACKSPACE ||
634 					  inp == KEY_LEFT )) {
635 				wmove( win, win->cury, win->curx - 1 );
636 				wdelch( win );
637 			}
638 		} else {
639 			ws[ 0 ] = inp, ws[ 1 ] = L'\0';
640 			setcchar( &wc, ws, win->wattr, 0, NULL );
641 			wadd_wch( win, &wc );
642 		}
643 	}
644 
645 	if (weset)
646 		nocbreak();
647 
648 	if (_cursesi_screen->nl && inp == 13)
649 		inp = 10;
650 
651 	*ch = inp;
652 
653 	if ( ret == KEY_CODE_YES )
654 		return KEY_CODE_YES;
655 	return ( inp < 0 ? ERR : OK );
656 #endif /* HAVE_WCHAR */
657 }
658 
659 /*
660  * unget_wch --
661  *	 Put the wide character back into the input queue.
662  */
663 int
664 unget_wch(const wchar_t c)
665 {
666 	return __unget((wint_t) c);
667 }
668