xref: /netbsd-src/lib/libcurses/get_wch.c (revision bdc22b2e01993381dcefeff2bc9b56ca75a4235c)
1 /*   $NetBSD: get_wch.c,v 1.14 2017/01/31 09:17:53 roy 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.14 2017/01/31 09:17:53 roy 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 #ifdef HAVE_WCHAR
51 static short   wstate;		  /* state of the wcinkey function */
52 #endif /* HAVE_WCHAR */
53 extern short state;		/* storage declared in getch.c */
54 
55 /* prototypes for private functions */
56 #ifdef HAVE_WCHAR
57 static int inkey(wchar_t *wc, int to, int delay);
58 #endif /* HAVE_WCHAR */
59 
60 #ifdef HAVE_WCHAR
61 /*
62  * __init_get_wch - initialise all the pointers & structures needed to make
63  * get_wch work in keypad mode.
64  *
65  */
66 void
67 __init_get_wch(SCREEN *screen)
68 {
69 	wstate = INKEY_NORM;
70 	memset(&screen->cbuf, 0, sizeof(screen->cbuf));
71 	screen->cbuf_head = screen->cbuf_tail = screen->cbuf_cur = 0;
72 }
73 #endif /* HAVE_WCHAR */
74 
75 
76 #ifdef HAVE_WCHAR
77 /*
78  * inkey - do the work to process keyboard input, check for multi-key
79  * sequences and return the appropriate symbol if we get a match.
80  *
81  */
82 static int
83 inkey(wchar_t *wc, int to, int delay)
84 {
85 	wchar_t		 k = 0;
86 	int		 c, mapping, ret = 0;
87 	size_t	  mlen = 0;
88 	keymap_t	*current = _cursesi_screen->base_keymap;
89 	FILE		*infd = _cursesi_screen->infd;
90 	int		 *start = &_cursesi_screen->cbuf_head,
91 				*working = &_cursesi_screen->cbuf_cur,
92 				*end = &_cursesi_screen->cbuf_tail;
93 	char		*inbuf = &_cursesi_screen->cbuf[ 0 ];
94 
95 #ifdef DEBUG
96 	__CTRACE(__CTRACE_INPUT, "inkey (%p, %d, %d)\n", wc, to, delay);
97 #endif
98 	for (;;) { /* loop until we get a complete key sequence */
99 		if (wstate == INKEY_NORM) {
100 			if (delay && __timeout(delay) == ERR)
101 				return ERR;
102 			c = fgetc(infd);
103 			if (c == WEOF) {
104 				clearerr(infd);
105 				return ERR;
106 			}
107 
108 			if (delay && (__notimeout() == ERR))
109 				return ERR;
110 
111 			k = (wchar_t)c;
112 #ifdef DEBUG
113 			__CTRACE(__CTRACE_INPUT,
114 			    "inkey (wstate normal) got '%s'\n", unctrl(k));
115 #endif
116 
117 			inbuf[*end] = k;
118 			*end = (*end + 1) % MAX_CBUF_SIZE;
119 			*working = *start;
120 			wstate = INKEY_ASSEMBLING; /* go to assembling state */
121 #ifdef DEBUG
122 			__CTRACE(__CTRACE_INPUT,
123 			    "inkey: NORM=>ASSEMBLING: start(%d), "
124 			    "current(%d), end(%d)\n", *start, *working, *end);
125 #endif /* DEBUG */
126 		} else if (wstate == INKEY_BACKOUT) {
127 			k = inbuf[*working];
128 			*working = (*working + 1) % MAX_CBUF_SIZE;
129 			if (*working == *end) {	/* see if run out of keys */
130 				/* if so, switch to assembling */
131 				wstate = INKEY_ASSEMBLING;
132 #ifdef DEBUG
133 				__CTRACE(__CTRACE_INPUT,
134 				    "inkey: BACKOUT=>ASSEMBLING, start(%d), "
135 				    "current(%d), end(%d)\n",
136 				    *start, *working, *end);
137 #endif /* DEBUG */
138 			}
139 		} else if (wstate == INKEY_ASSEMBLING) {
140 			/* assembling a key sequence */
141 			if (delay) {
142 				if (__timeout(to ? (ESCDELAY / 100) : delay)
143 						== ERR)
144 					return ERR;
145 			} else {
146 				if (to && (__timeout(ESCDELAY / 100) == ERR))
147 					return ERR;
148 			}
149 
150 			c = fgetc(infd);
151 			if (ferror(infd)) {
152 				clearerr(infd);
153 				return ERR;
154 			}
155 
156 			if ((to || delay) && (__notimeout() == ERR))
157 				return ERR;
158 
159 			k = (wchar_t)c;
160 #ifdef DEBUG
161 			__CTRACE(__CTRACE_INPUT,
162 			    "inkey (wstate assembling) got '%s'\n", unctrl(k));
163 #endif /* DEBUG */
164 			if (feof(infd)) { /* inter-char T/O, start backout */
165 				clearerr(infd);
166 				if (*start == *end)
167 					/* no chars in the buffer, restart */
168 					continue;
169 
170 				k = inbuf[*start];
171 				wstate = INKEY_TIMEOUT;
172 #ifdef DEBUG
173 				__CTRACE(__CTRACE_INPUT,
174 				    "inkey: ASSEMBLING=>TIMEOUT, start(%d), "
175 				    "current(%d), end(%d)\n",
176 				    *start, *working, *end);
177 #endif /* DEBUG */
178 			} else {
179 				inbuf[*end] = k;
180 				*working = *end;
181 				*end = (*end + 1) % MAX_CBUF_SIZE;
182 #ifdef DEBUG
183 				__CTRACE(__CTRACE_INPUT,
184 				    "inkey: ASSEMBLING: start(%d), "
185 				    "current(%d), end(%d)",
186 				    *start, *working, *end);
187 #endif /* DEBUG */
188 			}
189 		} else if (wstate == INKEY_WCASSEMBLING) {
190 			/* assembling a wide-char sequence */
191 			if (delay) {
192 				if (__timeout(to ? (ESCDELAY / 100) : delay)
193 						== ERR)
194 					return ERR;
195 			} else {
196 				if (to && (__timeout(ESCDELAY / 100) == ERR))
197 					return ERR;
198 			}
199 
200 			c = fgetc(infd);
201 			if (ferror(infd)) {
202 				clearerr(infd);
203 				return ERR;
204 			}
205 
206 			if ((to || delay) && (__notimeout() == ERR))
207 				return ERR;
208 
209 			k = (wchar_t)c;
210 #ifdef DEBUG
211 			__CTRACE(__CTRACE_INPUT,
212 			    "inkey (wstate wcassembling) got '%s'\n",
213 				unctrl(k));
214 #endif
215 			if (feof(infd)) { /* inter-char T/O, start backout */
216 				clearerr(infd);
217 				if (*start == *end)
218 					/* no chars in the buffer, restart */
219 					continue;
220 
221 				*wc = inbuf[*start];
222 				*working = *start = (*start +1) % MAX_CBUF_SIZE;
223 				if (*start == *end) {
224 					state = wstate = INKEY_NORM;
225 #ifdef DEBUG
226 					__CTRACE(__CTRACE_INPUT,
227 					    "inkey: WCASSEMBLING=>NORM, "
228 					    "start(%d), current(%d), end(%d)",
229 					    *start, *working, *end);
230 #endif /* DEBUG */
231 				} else {
232 					state = wstate = INKEY_BACKOUT;
233 #ifdef DEBUG
234 					__CTRACE(__CTRACE_INPUT,
235 					    "inkey: WCASSEMBLING=>BACKOUT, "
236 					    "start(%d), current(%d), end(%d)",
237 					    *start, *working, *end);
238 #endif /* DEBUG */
239 				}
240 				return OK;
241 			} else {
242 				/* assembling wide characters */
243 				inbuf[*end] = k;
244 				*working = *end;
245 				*end = (*end + 1) % MAX_CBUF_SIZE;
246 #ifdef DEBUG
247 				__CTRACE(__CTRACE_INPUT,
248 				    "inkey: WCASSEMBLING[head(%d), "
249 				    "urrent(%d), tail(%d)]\n",
250 				    *start, *working, *end);
251 #endif /* DEBUG */
252 				ret = (int)mbrtowc(wc, inbuf + (*working), 1,
253 						   &_cursesi_screen->sp);
254 #ifdef DEBUG
255 				__CTRACE(__CTRACE_INPUT,
256 				    "inkey: mbrtowc returns %d, wc(%x)\n",
257 				    ret, *wc);
258 #endif /* DEBUG */
259 				if (ret == -2) {
260 					*working = (*working+1) % MAX_CBUF_SIZE;
261 					continue;
262 				}
263 				if ( ret == 0 )
264 					ret = 1;
265 				if ( ret == -1 ) {
266 					/* return the 1st character we know */
267 					*wc = inbuf[*start];
268 					*working = *start = (*start + 1) % MAX_CBUF_SIZE;
269 #ifdef DEBUG
270 					__CTRACE(__CTRACE_INPUT,
271 					    "inkey: Invalid wide char(%x) "
272 					    "[head(%d), current(%d), "
273 					    "tail(%d)]\n",
274 					    *wc, *start, *working, *end);
275 #endif /* DEBUG */
276 				} else { /* > 0 */
277 					/* return the wide character */
278 					*start = *working
279 					       = (*working + ret)%MAX_CBUF_SIZE;
280 #ifdef DEBUG
281 					__CTRACE(__CTRACE_INPUT,
282 					    "inkey: Wide char found(%x) "
283 					    "[head(%d), current(%d), "
284 					    "tail(%d)]\n",
285 					    *wc, *start, *working, *end);
286 #endif /* DEBUG */
287 				}
288 
289 				if (*start == *end) {
290 					/* only one char processed */
291 					state = wstate = INKEY_NORM;
292 #ifdef DEBUG
293 					__CTRACE(__CTRACE_INPUT,
294 					    "inkey: WCASSEMBLING=>NORM, "
295 					    "start(%d), current(%d), end(%d)",
296 					    *start, *working, *end);
297 #endif /* DEBUG */
298 				} else {
299 					/* otherwise we must have more than
300 					 * one char to backout */
301 					state = wstate = INKEY_BACKOUT;
302 #ifdef DEBUG
303 					__CTRACE(__CTRACE_INPUT,
304 					    "inkey: WCASSEMBLING=>BACKOUT, "
305 					    "start(%d), current(%d), end(%d)",
306 					    *start, *working, *end);
307 #endif /* DEBUG */
308 				}
309 				return OK;
310 			}
311 		} else {
312 			fprintf(stderr, "Inkey wstate screwed - exiting!!!");
313 			exit(2);
314 		}
315 
316 		/*
317 		 * Check key has no special meaning and we have not
318 		 * timed out and the key has not been disabled
319 		 */
320 		mapping = current->mapping[k];
321 		if (((wstate == INKEY_TIMEOUT) || (mapping < 0))
322 				|| ((current->key[mapping]->type
323 					== KEYMAP_LEAF)
324 				&& (current->key[mapping]->enable == FALSE)))
325 		{
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
398 				 * char to backout */
399 				state = wstate = INKEY_BACKOUT;
400 #ifdef DEBUG
401 				__CTRACE(__CTRACE_INPUT,
402 				    "inkey: Non-empty cbuf=>BACKOUT, "
403 				    "start(%d), current(%d), end(%d)\n",
404 				    *start, *working, *end);
405 #endif /* DEBUG */
406 			}
407 			return OK;
408 		} else {	/* must be part of a multikey sequence */
409 					/* check for completed key sequence */
410 			if (current->key[current->mapping[k]]->type
411 					== KEYMAP_LEAF) {
412 				/* eat the key sequence in cbuf */
413 				*start = *working = ( *working + 1 )
414 				    % MAX_CBUF_SIZE;
415 
416 				/* check if inbuf empty now */
417 #ifdef DEBUG
418 				__CTRACE(__CTRACE_INPUT,
419 				    "inkey: Key found(%s)\n",
420 				    key_name(current->key[mapping]->value.symbol));
421 #endif /* DEBUG */
422 				if (*start == *end) {
423 					/* if it is go back to normal */
424 					state = wstate = INKEY_NORM;
425 #ifdef DEBUG
426 					__CTRACE(__CTRACE_INPUT,
427 					    "[inkey]=>NORM, start(%d), "
428 					    "current(%d), end(%d)",
429 					    *start, *working, *end);
430 #endif /* DEBUG */
431 				} else {
432 					/* otherwise go to backout state */
433 					state = wstate = INKEY_BACKOUT;
434 #ifdef DEBUG
435 					__CTRACE(__CTRACE_INPUT,
436 					    "[inkey]=>BACKOUT, start(%d), "
437 					    "current(%d), end(%d)",
438 					    *start, *working, *end );
439 #endif /* DEBUG */
440 				}
441 
442 				/* return the symbol */
443 				*wc = current->key[mapping]->value.symbol;
444 				return KEY_CODE_YES;
445 			} else {
446 				/* Step to next part of multi-key sequence */
447 				current = current->key[current->mapping[k]]->value.next;
448 			}
449 		}
450 	}
451 }
452 #endif /* HAVE_WCHAR */
453 
454 /*
455  * get_wch --
456  *	Read in a wide character from stdscr.
457  */
458 int
459 get_wch(wint_t *ch)
460 {
461 #ifndef HAVE_WCHAR
462 	return ERR;
463 #else
464 	return wget_wch(stdscr, ch);
465 #endif /* HAVE_WCHAR */
466 }
467 
468 /*
469  * mvget_wch --
470  *	  Read in a character from stdscr at the given location.
471  */
472 int
473 mvget_wch(int y, int x, wint_t *ch)
474 {
475 #ifndef HAVE_WCHAR
476 	return ERR;
477 #else
478 	return mvwget_wch(stdscr, y, x, ch);
479 #endif /* HAVE_WCHAR */
480 }
481 
482 /*
483  * mvwget_wch --
484  *	  Read in a character from stdscr at the given location in the
485  *	  given window.
486  */
487 int
488 mvwget_wch(WINDOW *win, int y, int x, wint_t *ch)
489 {
490 #ifndef HAVE_WCHAR
491 	return ERR;
492 #else
493 	if (wmove(win, y, x) == ERR)
494 		return ERR;
495 
496 	return wget_wch(win, ch);
497 #endif /* HAVE_WCHAR */
498 }
499 
500 /*
501  * wget_wch --
502  *	Read in a wide character from the window.
503  */
504 int
505 wget_wch(WINDOW *win, wint_t *ch)
506 {
507 #ifndef HAVE_WCHAR
508 	return ERR;
509 #else
510 	int ret, weset;
511 	int c;
512 	FILE *infd = _cursesi_screen->infd;
513 	cchar_t wc;
514 	wchar_t inp, ws[2];
515 
516 	if (!(win->flags & __SCROLLOK)
517 	    && (win->flags & __FULLWIN)
518 	    && win->curx == win->maxx - 1
519 	    && win->cury == win->maxy - 1
520 	    && __echoit)
521 		return ERR;
522 
523 	if (is_wintouched(win))
524 		wrefresh(win);
525 #ifdef DEBUG
526 	__CTRACE(__CTRACE_INPUT, "wget_wch: __echoit = %d, "
527 	    "__rawmode = %d, __nl = %d, flags = %#.4x\n",
528 	    __echoit, __rawmode, _cursesi_screen->nl, win->flags);
529 #endif
530 	if (_cursesi_screen->resized) {
531 		_cursesi_screen->resized = 0;
532 		*ch = KEY_RESIZE;
533 		return KEY_CODE_YES;
534 	}
535 	if (_cursesi_screen->unget_pos) {
536 #ifdef DEBUG
537 		__CTRACE(__CTRACE_INPUT, "wget_wch returning char at %d\n",
538 		    _cursesi_screen->unget_pos);
539 #endif
540 		_cursesi_screen->unget_pos--;
541 		*ch = _cursesi_screen->unget_list[_cursesi_screen->unget_pos];
542 		if (__echoit) {
543 			ws[0] = *ch, ws[1] = L'\0';
544 			setcchar(&wc, ws, win->wattr, 0, NULL);
545 			wadd_wch(win, &wc);
546 		}
547 		return KEY_CODE_YES;
548 	}
549 	if (__echoit && !__rawmode) {
550 		cbreak();
551 		weset = 1;
552 	} else
553 		weset = 0;
554 
555 	__save_termios();
556 
557 	if (win->flags & __KEYPAD) {
558 		switch (win->delay) {
559 			case -1:
560 				ret = inkey(&inp,
561 					win->flags & __NOTIMEOUT ? 0 : 1, 0);
562 				break;
563 			case 0:
564 				if (__nodelay() == ERR)
565 					return ERR;
566 				ret = inkey(&inp, 0, 0);
567 				break;
568 			default:
569 				ret = inkey(&inp,
570 					win->flags & __NOTIMEOUT ? 0 : 1,
571 					win->delay);
572 				break;
573 		}
574 		if ( ret == ERR )
575 			return ERR;
576 	} else {
577 		switch (win->delay) {
578 			case -1:
579 				break;
580 			case 0:
581 				if (__nodelay() == ERR)
582 					return ERR;
583 				break;
584 			default:
585 				if (__timeout(win->delay) == ERR)
586 					return ERR;
587 				break;
588 		}
589 
590 		c = getwchar();
591 		if (feof(infd)) {
592 			clearerr(infd);
593 			__restore_termios();
594 			return ERR;	/* we have timed out */
595 		}
596 
597 		if (ferror(infd)) {
598 			clearerr(infd);
599 			return ERR;
600 		} else {
601 			ret = c;
602 			inp = c;
603 		}
604 	}
605 #ifdef DEBUG
606 	if (inp > 255)
607 		/* we have a key symbol - treat it differently */
608 		/* XXXX perhaps __unctrl should be expanded to include
609 		 * XXXX the keysyms in the table....
610 		 */
611 		__CTRACE(__CTRACE_INPUT, "wget_wch assembled keysym 0x%x\n",
612 		    inp);
613 	else
614 		__CTRACE(__CTRACE_INPUT, "wget_wch got '%s'\n", unctrl(inp));
615 #endif
616 	if (win->delay > -1) {
617 		if (__delay() == ERR)
618 			return ERR;
619 	}
620 
621 	__restore_termios();
622 
623 	if (__echoit) {
624 		if ( ret == KEY_CODE_YES ) {
625 			/* handle [DEL], [BS], and [LEFT] */
626 			if ( win->curx &&
627 					( inp == KEY_DC ||
628 					  inp == KEY_BACKSPACE ||
629 					  inp == KEY_LEFT )) {
630 				wmove( win, win->cury, win->curx - 1 );
631 				wdelch( win );
632 			}
633 		} else {
634 			ws[ 0 ] = inp, ws[ 1 ] = L'\0';
635 			setcchar( &wc, ws, win->wattr, 0, NULL );
636 			wadd_wch( win, &wc );
637 		}
638 	}
639 
640 	if (weset)
641 		nocbreak();
642 
643 	if (_cursesi_screen->nl && inp == 13)
644 		inp = 10;
645 
646 	*ch = inp;
647 
648 	if ( ret == KEY_CODE_YES )
649 		return KEY_CODE_YES;
650 	return inp < 0 ? ERR : OK;
651 #endif /* HAVE_WCHAR */
652 }
653 
654 /*
655  * unget_wch --
656  *	 Put the wide character back into the input queue.
657  */
658 int
659 unget_wch(const wchar_t c)
660 {
661 	return __unget((wint_t)c);
662 }
663