1 /*        $NetBSD: getch.c,v 1.79 2024/05/14 10:22:48 uwe Exp $       */
2 
3 /*
4  * Copyright (c) 1981, 1993, 1994
5  *        The Regents of the University of California.  All rights reserved.
6  *
7  * Redistribution and use in source and binary forms, with or without
8  * modification, are permitted provided that the following conditions
9  * are met:
10  * 1. Redistributions of source code must retain the above copyright
11  *    notice, this list of conditions and the following disclaimer.
12  * 2. Redistributions in binary form must reproduce the above copyright
13  *    notice, this list of conditions and the following disclaimer in the
14  *    documentation and/or other materials provided with the distribution.
15  * 3. Neither the name of the University nor the names of its contributors
16  *    may be used to endorse or promote products derived from this software
17  *    without specific prior written permission.
18  *
19  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
20  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
23  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
24  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
25  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
26  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
27  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
28  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
29  * SUCH DAMAGE.
30  */
31 
32 #include <sys/cdefs.h>
33 #ifndef lint
34 #if 0
35 static char sccsid[] = "@(#)getch.c     8.2 (Berkeley) 5/4/94";
36 #else
37 __RCSID("$NetBSD: getch.c,v 1.79 2024/05/14 10:22:48 uwe Exp $");
38 #endif
39 #endif                                            /* not lint */
40 
41 #include <errno.h>
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 short _cursesi_state;                   /* state of the inkey function */
51 
52 static const struct tcdata tc[] = {
53           {TICODE_kSAV, KEY_SSAVE},
54           {TICODE_kSPD, KEY_SSUSPEND},
55           {TICODE_kUND, KEY_SUNDO},
56           {TICODE_kHLP, KEY_SHELP},
57           {TICODE_kHOM, KEY_SHOME},
58           {TICODE_kIC, KEY_SIC},
59           {TICODE_kLFT, KEY_SLEFT},
60           {TICODE_krdo, KEY_REDO},
61           {TICODE_khlp, KEY_HELP},
62           {TICODE_kmrk, KEY_MARK},
63           {TICODE_kmsg, KEY_MESSAGE},
64           {TICODE_kmov, KEY_MOVE},
65           {TICODE_knxt, KEY_NEXT},
66           {TICODE_kopn, KEY_OPEN},
67           {TICODE_kopt, KEY_OPTIONS},
68           {TICODE_kprv, KEY_PREVIOUS},
69           {TICODE_kprt, KEY_PRINT},
70           {TICODE_kMSG, KEY_SMESSAGE},
71           {TICODE_kMOV, KEY_SMOVE},
72           {TICODE_kNXT, KEY_SNEXT},
73           {TICODE_kOPT, KEY_SOPTIONS},
74           {TICODE_kPRV, KEY_SPREVIOUS},
75           {TICODE_kPRT, KEY_SPRINT},
76           {TICODE_kRDO, KEY_SREDO},
77           {TICODE_kRPL, KEY_SREPLACE},
78           {TICODE_kRIT, KEY_SRIGHT},
79           {TICODE_kRES, KEY_SRSUME},
80           {TICODE_kCAN, KEY_SCANCEL},
81           {TICODE_kref, KEY_REFERENCE},
82           {TICODE_krfr, KEY_REFRESH},
83           {TICODE_krpl, KEY_REPLACE},
84           {TICODE_krst, KEY_RESTART},
85           {TICODE_kres, KEY_RESUME},
86           {TICODE_ksav, KEY_SAVE},
87           {TICODE_kspd, KEY_SUSPEND},
88           {TICODE_kund, KEY_UNDO},
89           {TICODE_kBEG, KEY_SBEG},
90           {TICODE_kFND, KEY_SFIND},
91           {TICODE_kCMD, KEY_SCOMMAND},
92           {TICODE_kCPY, KEY_SCOPY},
93           {TICODE_kCRT, KEY_SCREATE},
94           {TICODE_kDC, KEY_SDC},
95           {TICODE_kDL, KEY_SDL},
96           {TICODE_kslt, KEY_SELECT},
97           {TICODE_kEND, KEY_SEND},
98           {TICODE_kEOL, KEY_SEOL},
99           {TICODE_kEXT, KEY_SEXIT},
100           {TICODE_kfnd, KEY_FIND},
101           {TICODE_kbeg, KEY_BEG},
102           {TICODE_kcan, KEY_CANCEL},
103           {TICODE_kclo, KEY_CLOSE},
104           {TICODE_kcmd, KEY_COMMAND},
105           {TICODE_kcpy, KEY_COPY},
106           {TICODE_kcrt, KEY_CREATE},
107           {TICODE_kend, KEY_END},
108           {TICODE_kent, KEY_ENTER},
109           {TICODE_kext, KEY_EXIT},
110           {TICODE_kf11, KEY_F(11)},
111           {TICODE_kf12, KEY_F(12)},
112           {TICODE_kf13, KEY_F(13)},
113           {TICODE_kf14, KEY_F(14)},
114           {TICODE_kf15, KEY_F(15)},
115           {TICODE_kf16, KEY_F(16)},
116           {TICODE_kf17, KEY_F(17)},
117           {TICODE_kf18, KEY_F(18)},
118           {TICODE_kf19, KEY_F(19)},
119           {TICODE_kf20, KEY_F(20)},
120           {TICODE_kf21, KEY_F(21)},
121           {TICODE_kf22, KEY_F(22)},
122           {TICODE_kf23, KEY_F(23)},
123           {TICODE_kf24, KEY_F(24)},
124           {TICODE_kf25, KEY_F(25)},
125           {TICODE_kf26, KEY_F(26)},
126           {TICODE_kf27, KEY_F(27)},
127           {TICODE_kf28, KEY_F(28)},
128           {TICODE_kf29, KEY_F(29)},
129           {TICODE_kf30, KEY_F(30)},
130           {TICODE_kf31, KEY_F(31)},
131           {TICODE_kf32, KEY_F(32)},
132           {TICODE_kf33, KEY_F(33)},
133           {TICODE_kf34, KEY_F(34)},
134           {TICODE_kf35, KEY_F(35)},
135           {TICODE_kf36, KEY_F(36)},
136           {TICODE_kf37, KEY_F(37)},
137           {TICODE_kf38, KEY_F(38)},
138           {TICODE_kf39, KEY_F(39)},
139           {TICODE_kf40, KEY_F(40)},
140           {TICODE_kf41, KEY_F(41)},
141           {TICODE_kf42, KEY_F(42)},
142           {TICODE_kf43, KEY_F(43)},
143           {TICODE_kf44, KEY_F(44)},
144           {TICODE_kf45, KEY_F(45)},
145           {TICODE_kf46, KEY_F(46)},
146           {TICODE_kf47, KEY_F(47)},
147           {TICODE_kf48, KEY_F(48)},
148           {TICODE_kf49, KEY_F(49)},
149           {TICODE_kf50, KEY_F(50)},
150           {TICODE_kf51, KEY_F(51)},
151           {TICODE_kf52, KEY_F(52)},
152           {TICODE_kf53, KEY_F(53)},
153           {TICODE_kf54, KEY_F(54)},
154           {TICODE_kf55, KEY_F(55)},
155           {TICODE_kf56, KEY_F(56)},
156           {TICODE_kf57, KEY_F(57)},
157           {TICODE_kf58, KEY_F(58)},
158           {TICODE_kf59, KEY_F(59)},
159           {TICODE_kf60, KEY_F(60)},
160           {TICODE_kf61, KEY_F(61)},
161           {TICODE_kf62, KEY_F(62)},
162           {TICODE_kf63, KEY_F(63)},
163           {TICODE_ka1, KEY_A1},
164           {TICODE_kb2, KEY_B2},
165           {TICODE_ka3, KEY_A3},
166           {TICODE_kc1, KEY_C1},
167           {TICODE_kc3, KEY_C3},
168           {TICODE_kmous, KEY_MOUSE},
169           {TICODE_kf0, KEY_F0},
170           {TICODE_kf1, KEY_F(1)},
171           {TICODE_kf2, KEY_F(2)},
172           {TICODE_kf3, KEY_F(3)},
173           {TICODE_kf4, KEY_F(4)},
174           {TICODE_kf5, KEY_F(5)},
175           {TICODE_kf6, KEY_F(6)},
176           {TICODE_kf7, KEY_F(7)},
177           {TICODE_kf8, KEY_F(8)},
178           {TICODE_kf9, KEY_F(9)},
179           {TICODE_kf10, KEY_F(10)},
180           {TICODE_kil1, KEY_IL},
181           {TICODE_ktbc, KEY_CATAB},
182           {TICODE_kcbt, KEY_BTAB},
183           {TICODE_kbs, KEY_BACKSPACE},
184           {TICODE_kclr, KEY_CLEAR},
185           {TICODE_kdch1, KEY_DC},
186           {TICODE_kcud1, KEY_DOWN},
187           {TICODE_kel, KEY_EOL},
188           {TICODE_kind, KEY_SF},
189           {TICODE_kll, KEY_LL},
190           {TICODE_khome, KEY_HOME},
191           {TICODE_kich1, KEY_IC},
192           {TICODE_kdl1, KEY_DL},
193           {TICODE_kcub1, KEY_LEFT},
194           {TICODE_krmir, KEY_EIC},
195           {TICODE_knp, KEY_NPAGE},
196           {TICODE_kpp, KEY_PPAGE},
197           {TICODE_kri, KEY_SR},
198           {TICODE_kcuf1, KEY_RIGHT},
199           {TICODE_ked, KEY_EOS},
200           {TICODE_khts, KEY_STAB},
201           {TICODE_kctab, KEY_CTAB},
202           {TICODE_kcuu1, KEY_UP}
203 };
204 /* Number of TC entries .... */
205 static const int num_tcs = (sizeof(tc) / sizeof(struct tcdata));
206 
207 /* Key buffer */
208 #define INBUF_SZ 16           /* size of key buffer - must be larger than
209                                          * longest multi-key sequence */
210 static wchar_t      inbuf[INBUF_SZ];
211 static int          start, end, working; /* pointers for manipulating inbuf data */
212 
213 /* prototypes for private functions */
214 static void add_key_sequence(SCREEN *screen, const char *sequence, int key_type);
215 static key_entry_t *add_new_key(keymap_t *current, char ch, int key_type,
216         int symbol);
217 static void delete_key_sequence(keymap_t *current, int key_type);
218 static void do_keyok(keymap_t *current, int key_type, bool set, bool flag,
219           int *retval);
220 static keymap_t *new_keymap(void); /* create a new keymap */
221 static key_entry_t *new_key(void); /* create a new key entry */
222 static wchar_t                inkey(int to, int delay);
223 
224 /*
225  * Free the storage associated with the given keymap
226  */
227 void
_cursesi_free_keymap(keymap_t * map)228 _cursesi_free_keymap(keymap_t *map)
229 {
230           int i;
231 
232             /* check for, and free, child keymaps */
233           for (i = 0; i < MAX_CHAR; i++) {
234                     if (map->mapping[i] >= 0) {
235                               if (map->key[map->mapping[i]]->type == KEYMAP_MULTI)
236                                         _cursesi_free_keymap(
237                                                   map->key[map->mapping[i]]->value.next);
238                     }
239           }
240 
241             /* now free any allocated keymap structs */
242           for (i = 0; i < map->count; i += KEYMAP_ALLOC_CHUNK) {
243                     free(map->key[i]);
244           }
245 
246           free(map->key);
247           free(map);
248 }
249 
250 
251 /*
252  * Add a new key entry to the keymap pointed to by current.  Entry
253  * contains the character to add to the keymap, type is the type of
254  * entry to add (either multikey or leaf) and symbol is the symbolic
255  * value for a leaf type entry.  The function returns a pointer to the
256  * new keymap entry.
257  */
258 static key_entry_t *
add_new_key(keymap_t * current,char chr,int key_type,int symbol)259 add_new_key(keymap_t *current, char chr, int key_type, int symbol)
260 {
261           key_entry_t *the_key;
262         int i, ki;
263 
264           __CTRACE(__CTRACE_MISC,
265               "Adding character %s of type %d, symbol 0x%x\n",
266               unctrl(chr), key_type, symbol);
267           if (current->mapping[(unsigned char)chr] < 0) {
268                     if (current->mapping[(unsigned char)chr] == MAPPING_UNUSED) {
269                                 /* first time for this char */
270                               current->mapping[(unsigned char)chr] =
271                                         current->count;     /* map new entry */
272                               ki = current->count;
273 
274                                 /* make sure we have room in the key array first */
275                               if ((current->count & (KEYMAP_ALLOC_CHUNK - 1)) == 0)
276                               {
277                                         if ((current->key =
278                                              realloc(current->key,
279                                                        ki * sizeof(key_entry_t *)
280                                                        + KEYMAP_ALLOC_CHUNK * sizeof(key_entry_t *))) == NULL) {
281                                                   fprintf(stderr,
282                                                     "Could not malloc for key entry\n");
283                                                   exit(1);
284                                         }
285 
286                                         the_key = new_key();
287                                         for (i = 0; i < KEYMAP_ALLOC_CHUNK; i++) {
288                                                   current->key[ki + i] = &the_key[i];
289                                         }
290                               }
291                 } else {
292                                 /* the mapping was used but freed, reuse it */
293                               ki = - current->mapping[(unsigned char) chr];
294                               current->mapping[(unsigned char) chr] = ki;
295                     }
296 
297                     current->count++;
298 
299                       /* point at the current key array element to use */
300                     the_key = current->key[ki];
301 
302                     the_key->type = key_type;
303 
304                     switch (key_type) {
305                     case KEYMAP_MULTI:
306                               /* need for next key */
307                               __CTRACE(__CTRACE_MISC, "Creating new keymap\n");
308                               the_key->value.next = new_keymap();
309                               the_key->enable = TRUE;
310                               break;
311 
312                     case KEYMAP_LEAF:
313                               /* the associated symbol for the key */
314                               __CTRACE(__CTRACE_MISC, "Adding leaf key\n");
315                               the_key->value.symbol = symbol;
316                               the_key->enable = TRUE;
317                               break;
318 
319                     default:
320                               fprintf(stderr, "add_new_key: bad type passed\n");
321                               exit(1);
322                     }
323           } else {
324                     /* the key is already known - just return the address. */
325                     __CTRACE(__CTRACE_MISC, "Keymap already known\n");
326                     the_key = current->key[current->mapping[(unsigned char)chr]];
327           }
328 
329         return the_key;
330 }
331 
332 /*
333  * Delete the given key symbol from the key mappings for the screen.
334  *
335  */
336 static void
delete_key_sequence(keymap_t * current,int key_type)337 delete_key_sequence(keymap_t *current, int key_type)
338 {
339           key_entry_t *key;
340           int i;
341 
342             /*
343              * we need to iterate over all the keys as there may be
344              * multiple instances of the leaf symbol.
345              */
346           for (i = 0; i < MAX_CHAR; i++) {
347                     if (current->mapping[i] < 0)
348                               continue; /* no mapping for the key, next! */
349 
350                     key = current->key[current->mapping[i]];
351 
352                     if (key->type == KEYMAP_MULTI) {
353                                 /* have not found the leaf, recurse down */
354                               delete_key_sequence(key->value.next, key_type);
355                                 /* if we deleted the last key in the map, free */
356                               if (key->value.next->count == 0)
357                                         _cursesi_free_keymap(key->value.next);
358                     } else if ((key->type == KEYMAP_LEAF)
359                                  && (key->value.symbol == key_type)) {
360                     __CTRACE(__CTRACE_INPUT,
361                         "delete_key_sequence: found keysym %d, deleting\n",
362                         key_type);
363                               key->enable = FALSE;
364                     }
365           }
366 }
367 
368 /*
369  * Add the sequence of characters given in sequence as the key mapping
370  * for the given key symbol.
371  */
372 static void
add_key_sequence(SCREEN * screen,const char * sequence,int key_type)373 add_key_sequence(SCREEN *screen, const char *sequence, int key_type)
374 {
375           key_entry_t *tmp_key;
376           keymap_t *current;
377           int length, j, key_ent;
378 
379           __CTRACE(__CTRACE_MISC, "add_key_sequence: add key sequence: %s(%s)\n",
380               sequence, keyname(key_type));
381           current = screen->base_keymap;          /* always start with
382                                                    * base keymap. */
383           length = (int)strlen(sequence);
384 
385           /*
386            * OK - we really should never get a zero length string here, either
387            * the terminfo entry is there and it has a value or we are not called
388            * at all.  Unfortunately, if someone assigns a terminfo string to the
389            * ^@ value we get passed a null string which messes up our length.
390            * So, if we get a null string then just insert a leaf value in
391            * the 0th char position of the root keymap.  Note that we are
392            * totally screwed if someone terminates a multichar sequence
393            * with ^@... oh well.
394            */
395           if (length == 0)
396                     length = 1;
397 
398           for (j = 0; j < length - 1; j++) {
399                       /* add the entry to the struct */
400                     tmp_key = add_new_key(current, sequence[j], KEYMAP_MULTI, 0);
401 
402                       /* index into the key array - it's
403                          clearer if we stash this */
404                     key_ent = current->mapping[(unsigned char) sequence[j]];
405 
406                     current->key[key_ent] = tmp_key;
407 
408                       /* next key uses this map... */
409                     current = current->key[key_ent]->value.next;
410           }
411 
412           /*
413            * This is the last key in the sequence (it may have been the
414            * only one but that does not matter) this means it is a leaf
415            * key and should have a symbol associated with it.
416            */
417           tmp_key = add_new_key(current, sequence[length - 1], KEYMAP_LEAF,
418                                     key_type);
419           current->key[current->mapping[(int)sequence[length - 1]]] = tmp_key;
420 }
421 
422 /*
423  * Init_getch - initialise all the pointers & structures needed to make
424  * getch work in keypad mode.
425  *
426  */
427 void
__init_getch(SCREEN * screen)428 __init_getch(SCREEN *screen)
429 {
430           char entry[1024], *p;
431           const char *s;
432           int     i;
433           size_t limit, l;
434 #ifdef DEBUG
435           int k, length;
436 #endif
437 
438           /* init the inkey state variable */
439           _cursesi_state = INKEY_NORM;
440 
441           /* init the base keymap */
442           screen->base_keymap = new_keymap();
443 
444           /* key input buffer pointers */
445           start = end = working = 0;
446 
447           /* now do the terminfo snarfing ... */
448 
449           for (i = 0; i < num_tcs; i++) {
450                     p = entry;
451                     limit = 1023;
452                     s = screen->term->strs[tc[i].code];
453                     if (s == NULL)
454                               continue;
455                     l = strlen(s) + 1;
456                     if (limit < l)
457                               continue;
458                     strlcpy(p, s, limit);
459                     p += l;
460                     limit -= l;
461 #ifdef DEBUG
462                               __CTRACE(__CTRACE_INIT,
463                                   "Processing terminfo entry %d, sequence ",
464                                   tc[i].code);
465                               length = (int) strlen(entry);
466                               for (k = 0; k <= length -1; k++)
467                                         __CTRACE(__CTRACE_INIT, "%s", unctrl(entry[k]));
468                               __CTRACE(__CTRACE_INIT, "\n");
469 #endif
470                     add_key_sequence(screen, entry, tc[i].symbol);
471           }
472 }
473 
474 
475 /*
476  * new_keymap - allocates & initialises a new keymap structure.  This
477  * function returns a pointer to the new keymap.
478  *
479  */
480 static keymap_t *
new_keymap(void)481 new_keymap(void)
482 {
483           int     i;
484           keymap_t *new_map;
485 
486           if ((new_map = malloc(sizeof(keymap_t))) == NULL) {
487                     perror("Inkey: Cannot allocate new keymap");
488                     exit(2);
489           }
490 
491           /* Initialise the new map */
492           new_map->count = 0;
493           for (i = 0; i < MAX_CHAR; i++) {
494                     new_map->mapping[i] = MAPPING_UNUSED; /* no mapping for char */
495           }
496 
497           /* key array will be allocated when first key is added */
498           new_map->key = NULL;
499 
500           return new_map;
501 }
502 
503 /*
504  * new_key - allocates & initialises a new key entry.  This function returns
505  * a pointer to the newly allocated key entry.
506  *
507  */
508 static key_entry_t *
new_key(void)509 new_key(void)
510 {
511           key_entry_t *new_one;
512           int i;
513 
514           new_one = malloc(KEYMAP_ALLOC_CHUNK * sizeof(key_entry_t));
515           if (new_one == NULL) {
516                     perror("inkey: Cannot allocate new key entry chunk");
517                     exit(2);
518           }
519 
520           for (i = 0; i < KEYMAP_ALLOC_CHUNK; i++) {
521                     new_one[i].type = 0;
522                     new_one[i].value.next = NULL;
523           }
524 
525           return new_one;
526 }
527 
528 /*
529  * inkey - do the work to process keyboard input, check for multi-key
530  * sequences and return the appropriate symbol if we get a match.
531  *
532  */
533 
534 static wchar_t
inkey(int to,int delay)535 inkey(int to, int delay)
536 {
537           wchar_t              k;
538           int              c, mapping;
539           keymap_t  *current = _cursesi_screen->base_keymap;
540           FILE            *infd = _cursesi_screen->infd;
541 
542           k = 0;              /* XXX gcc -Wuninitialized */
543 
544           __CTRACE(__CTRACE_INPUT, "inkey (%d, %d)\n", to, delay);
545           for (;;) {                    /* loop until we get a complete key sequence */
546 reread:
547                     if (_cursesi_state == INKEY_NORM) {
548                               if (delay && __timeout(delay) == ERR)
549                                         return ERR;
550                               c = __fgetc_resize(infd);
551                               if (c == ERR || c == KEY_RESIZE) {
552                                         clearerr(infd);
553                                         return c;
554                               }
555 
556                               if (delay && (__notimeout() == ERR))
557                                         return ERR;
558 
559                               k = (wchar_t)c;
560                               __CTRACE(__CTRACE_INPUT,
561                                   "inkey (state normal) got '%s'\n", unctrl(k));
562 
563                               working = start;
564                               inbuf[working] = k;
565                               INC_POINTER(working);
566                               end = working;
567 
568                               /* go to the assembling state now */
569                               _cursesi_state = INKEY_ASSEMBLING;
570 
571                     } else if (_cursesi_state == INKEY_BACKOUT) {
572                               k = inbuf[working];
573                               INC_POINTER(working);
574                               if (working == end) {         /* see if we have run
575                                                              * out of keys in the
576                                                              * backlog */
577 
578                                         /* if we have then switch to assembling */
579                                         _cursesi_state = INKEY_ASSEMBLING;
580                               }
581                     } else if (_cursesi_state == INKEY_ASSEMBLING) {
582                               /* assembling a key sequence */
583                               if (delay) {
584                                         if (__timeout(to ? (ESCDELAY / 100) : delay)
585                                             == ERR)
586                                                   return ERR;
587                               } else {
588                                         if (to && (__timeout(ESCDELAY / 100) == ERR))
589                                                   return ERR;
590                               }
591 
592                               c = __fgetc_resize(infd);
593                               if (ferror(infd)) {
594                                         clearerr(infd);
595                                         return c;
596                               }
597 
598                               if ((to || delay) && (__notimeout() == ERR))
599                                                   return ERR;
600 
601                               __CTRACE(__CTRACE_INPUT,
602                                   "inkey (state assembling) got '%s'\n", unctrl(k));
603                               if (feof(infd) || c == -1) {  /* inter-char timeout,
604                                                                        * start backing out */
605                                         clearerr(infd);
606                                         if (start == end)
607                                                   /* no chars in the buffer, restart */
608                                                   goto reread;
609 
610                                         k = inbuf[start];
611                                         _cursesi_state = INKEY_TIMEOUT;
612                               } else {
613                                         k = (wchar_t) c;
614                                         inbuf[working] = k;
615                                         INC_POINTER(working);
616                                         end = working;
617                               }
618                     } else {
619                               fprintf(stderr, "Inkey state screwed - exiting!!!");
620                               exit(2);
621                     }
622 
623                       /*
624                        * Check key has no special meaning and we have not
625                        * timed out and the key has not been disabled
626                        */
627                     mapping = current->mapping[k];
628                     if (((_cursesi_state == INKEY_TIMEOUT) || (mapping < 0))
629                               || ((current->key[mapping]->type == KEYMAP_LEAF)
630                                   && (current->key[mapping]->enable == FALSE))) {
631                               /* return the first key we know about */
632                               k = inbuf[start];
633 
634                               INC_POINTER(start);
635                               working = start;
636 
637                               if (start == end) { /* only one char processed */
638                                         _cursesi_state = INKEY_NORM;
639                               } else {/* otherwise we must have more than one char
640                                          * to backout */
641                                         _cursesi_state = INKEY_BACKOUT;
642                               }
643                               return k;
644                     } else {  /* must be part of a multikey sequence */
645                               /* check for completed key sequence */
646                               if (current->key[current->mapping[k]]->type == KEYMAP_LEAF) {
647                                         start = working;    /* eat the key sequence
648                                                                        * in inbuf */
649 
650                                         /* check if inbuf empty now */
651                                         if (start == end) {
652                                                   /* if it is go back to normal */
653                                                   _cursesi_state = INKEY_NORM;
654                                         } else {
655                                                   /* otherwise go to backout state */
656                                                   _cursesi_state = INKEY_BACKOUT;
657                                         }
658 
659                                         /* return the symbol */
660                                         return current->key[current->mapping[k]]->value.symbol;
661 
662                               } else {
663                                         /*
664                                          * Step on to next part of the multi-key
665                                          * sequence.
666                                          */
667                                         current = current->key[current->mapping[k]]->value.next;
668                               }
669                     }
670           }
671 }
672 
673 #ifndef _CURSES_USE_MACROS
674 /*
675  * getch --
676  *        Read in a character from stdscr.
677  */
678 int
getch(void)679 getch(void)
680 {
681           return wgetch(stdscr);
682 }
683 
684 /*
685  * mvgetch --
686  *      Read in a character from stdscr at the given location.
687  */
688 int
mvgetch(int y,int x)689 mvgetch(int y, int x)
690 {
691           return mvwgetch(stdscr, y, x);
692 }
693 
694 /*
695  * mvwgetch --
696  *      Read in a character from stdscr at the given location in the
697  *      given window.
698  */
699 int
mvwgetch(WINDOW * win,int y,int x)700 mvwgetch(WINDOW *win, int y, int x)
701 {
702           if (wmove(win, y, x) == ERR)
703                     return ERR;
704 
705           return wgetch(win);
706 }
707 
708 #endif
709 
710 /*
711  * keyok --
712  *      Set the enable flag for a keysym, if the flag is false then
713  * getch will not return this keysym even if the matching key sequence
714  * is seen.
715  */
716 int
keyok(int key_type,bool flag)717 keyok(int key_type, bool flag)
718 {
719           int result = ERR;
720 
721           if (_cursesi_screen != NULL)
722                     do_keyok(_cursesi_screen->base_keymap, key_type,
723                         true, flag, &result);
724           return result;
725 }
726 
727 /*
728  * do_keyok --
729  *       Does the actual work for keyok, we need to recurse through the
730  * keymaps finding the passed key symbol.
731  */
732 static void
do_keyok(keymap_t * current,int key_type,bool set,bool flag,int * retval)733 do_keyok(keymap_t *current, int key_type, bool set, bool flag, int *retval)
734 {
735           key_entry_t *key;
736           int i;
737 
738             /*
739              * we need to iterate over all the keys as there may be
740              * multiple instances of the leaf symbol.
741              */
742           for (i = 0; i < MAX_CHAR; i++) {
743                     if (current->mapping[i] < 0)
744                               continue; /* no mapping for the key, next! */
745 
746                     key = current->key[current->mapping[i]];
747 
748                     if (key->type == KEYMAP_MULTI)
749                               do_keyok(key->value.next, key_type, set, flag, retval);
750                     else if ((key->type == KEYMAP_LEAF)
751                                && (key->value.symbol == key_type)) {
752                               if (set)
753                                         key->enable = flag;
754                               *retval = OK; /* we found at least one instance, ok */
755                     }
756           }
757 }
758 
759 /*
760  * define_key --
761  *      Add a custom mapping of a key sequence to key symbol.
762  *
763  */
764 int
define_key(const char * sequence,int symbol)765 define_key(const char *sequence, int symbol)
766 {
767 
768           if (symbol <= 0 || _cursesi_screen == NULL)
769                     return ERR;
770 
771           if (sequence == NULL) {
772                     __CTRACE(__CTRACE_INPUT, "define_key: deleting keysym %d\n",
773                         symbol);
774                     delete_key_sequence(_cursesi_screen->base_keymap, symbol);
775           } else
776                     add_key_sequence(_cursesi_screen, sequence, symbol);
777 
778           return OK;
779 }
780 
781 /*
782  * wgetch --
783  *        Read in a character from the window.
784  */
785 int
wgetch(WINDOW * win)786 wgetch(WINDOW *win)
787 {
788           int inp, weset;
789           int c;
790           FILE *infd = _cursesi_screen->infd;
791 
792           __CTRACE(__CTRACE_INPUT, "wgetch: win(%p)\n", win);
793           if (win == NULL)
794                     return ERR;
795           if (!(win->flags & __SCROLLOK) && (win->flags & __FULLWIN)
796               && win->curx == win->maxx - 1 && win->cury == win->maxy - 1
797               && __echoit)
798                     return ERR;
799 
800           if (!(win->flags & __ISPAD)) {
801                     if (is_wintouched(win))
802                               wrefresh(win);
803                     else if (__echoit && ((_cursesi_screen->curscr->cury != (win->begy + win->cury))
804                              || (_cursesi_screen->curscr->curx != (win->begx + win->curx)))) {
805                               __CTRACE(__CTRACE_INPUT,
806                                   "wgetch: curscr cury %d cury %d "
807                                   "curscr curx %d curx %d\n",
808                                   _cursesi_screen->curscr->cury,
809                                   win->begy + win->cury,
810                                   _cursesi_screen->curscr->curx,
811                                   win->begx + win->curx);
812                               /*
813                                * Just in case the window is not dirty but the
814                                * cursor was  moved, check and update the
815                                * cursor location.
816                                */
817                               mvcur(_cursesi_screen->curscr->cury,
818                                   _cursesi_screen->curscr->curx,
819                                   win->cury + win->begy, win->curx + win->begx);
820                               _cursesi_screen->curscr->cury =
821                                   win->cury + win->begy;
822                               _cursesi_screen->curscr->curx =
823                                   win->curx + win->begx;
824                     }
825           }
826 
827           __CTRACE(__CTRACE_INPUT, "wgetch: __echoit = %d, "
828               "__rawmode = %d, __nl = %d, flags = %#.4x, delay = %d\n",
829               __echoit, __rawmode, _cursesi_screen->nl, win->flags, win->delay);
830           if (_cursesi_screen->resized) {
831                     resizeterm(LINES, COLS);
832                     _cursesi_screen->resized = 0;
833                     __CTRACE(__CTRACE_INPUT, "wgetch returning KEY_RESIZE\n");
834                     return KEY_RESIZE;
835           }
836           if (_cursesi_screen->unget_pos) {
837                     __CTRACE(__CTRACE_INPUT, "wgetch returning char at %d\n",
838                         _cursesi_screen->unget_pos);
839                     _cursesi_screen->unget_pos--;
840                     c = _cursesi_screen->unget_list[_cursesi_screen->unget_pos];
841                     if (__echoit)
842                               waddch(win, (chtype) c);
843                     return c;
844           }
845           if (__echoit && !__rawmode) {
846                     cbreak();
847                     weset = 1;
848           } else
849                     weset = 0;
850 
851           __save_termios();
852 
853           if (win->flags & __KEYPAD) {
854                     switch (win->delay) {
855                     case -1:
856                               inp = inkey (win->flags & __NOTIMEOUT ? 0 : 1, 0);
857                               break;
858                     case 0:
859                               if (__nodelay() == ERR)
860                                         return ERR;
861                               inp = inkey(0, 0);
862                               break;
863                     default:
864                               inp = inkey(win->flags & __NOTIMEOUT ? 0 : 1, win->delay);
865                               break;
866                     }
867           } else {
868                     switch (win->delay) {
869                     case -1:
870                               if (__delay() == ERR)
871                                         return ERR;
872                               break;
873                     case 0:
874                               if (__nodelay() == ERR)
875                                         return ERR;
876                               break;
877                     default:
878                               if (__timeout(win->delay) == ERR)
879                                         return ERR;
880                               break;
881                     }
882 
883                     inp = __fgetc_resize(infd);
884                     if (inp == ERR || inp == KEY_RESIZE) {
885                               clearerr(infd);
886                               __restore_termios();
887                               return inp;
888                     }
889           }
890 #ifdef DEBUG
891           if (inp > 255)
892                       /* we have a key symbol - treat it differently */
893                       /* XXXX perhaps __unctrl should be expanded to include
894                        * XXXX the keysyms in the table....
895                        */
896                     __CTRACE(__CTRACE_INPUT, "wgetch assembled keysym 0x%x\n", inp);
897           else
898                     __CTRACE(__CTRACE_INPUT, "wgetch got '%s'\n", unctrl(inp));
899 #endif
900           if (win->delay > -1) {
901                     if (__delay() == ERR)
902                               return ERR;
903           }
904 
905           __restore_termios();
906 
907           if ((__echoit) && (inp < KEY_MIN))
908                     waddch(win, (chtype) inp);
909 
910           if (weset)
911                     nocbreak();
912 
913           if (_cursesi_screen->nl && inp == 13)
914                     inp = 10;
915 
916           return ((inp < 0) || (inp == ERR) ? ERR : inp);
917 }
918 
919 /*
920  * ungetch --
921  *     Put the character back into the input queue.
922  */
923 int
ungetch(int c)924 ungetch(int c)
925 {
926           return __unget((wint_t)c);
927 }
928 
929 /*
930  * __unget --
931  *    Do the work for ungetch() and unget_wch();
932  */
933 int
__unget(wint_t c)934 __unget(wint_t c)
935 {
936           wchar_t   *p;
937           int       len;
938 
939           __CTRACE(__CTRACE_INPUT, "__unget(%x)\n", c);
940           if (_cursesi_screen == NULL)
941                     return ERR;
942           if (_cursesi_screen->unget_pos >= _cursesi_screen->unget_len) {
943                     len = _cursesi_screen->unget_len + 32;
944                     if ((p = realloc(_cursesi_screen->unget_list,
945                         sizeof(wchar_t) * len)) == NULL) {
946                               /* Can't realloc(), so just lose the oldest entry */
947                               memmove(_cursesi_screen->unget_list,
948                                   _cursesi_screen->unget_list + sizeof(wchar_t),
949                                   _cursesi_screen->unget_len - 1);
950                               _cursesi_screen->unget_list[_cursesi_screen->unget_len
951                                   - 1] = c;
952                               _cursesi_screen->unget_pos =
953                                   _cursesi_screen->unget_len;
954                               return OK;
955                     } else {
956                               _cursesi_screen->unget_pos =
957                                   _cursesi_screen->unget_len;
958                               _cursesi_screen->unget_len = len;
959                               _cursesi_screen->unget_list = p;
960                     }
961           }
962           _cursesi_screen->unget_list[_cursesi_screen->unget_pos] = c;
963           _cursesi_screen->unget_pos++;
964           return OK;
965 }
966 
967 int
has_key(int key_type)968 has_key(int key_type)
969 {
970           int result = ERR;
971 
972           if (_cursesi_screen != NULL)
973                     do_keyok(_cursesi_screen->base_keymap, key_type,
974                         false, false, &result);
975           return result;
976 }
977 
978 /*
979  * set_escdelay --
980  *   Sets the escape delay for the current screen.
981  */
982 int
set_escdelay(int escdelay)983 set_escdelay(int escdelay)
984 {
985 
986           if (_cursesi_screen == NULL)
987                     return ERR;
988           _cursesi_screen->ESCDELAY = escdelay;
989           ESCDELAY = escdelay;
990           return OK;
991 }
992 
993 /*
994  * __fgetc_resize --
995  *    Any call to fgetc(3) should use this function instead
996  *    and test for the return value of KEY_RESIZE as well as ERR.
997  */
998 int
__fgetc_resize(FILE * infd)999 __fgetc_resize(FILE *infd)
1000 {
1001           int c;
1002 
1003           c = fgetc(infd);
1004           if (c != EOF)
1005                     return c;
1006 
1007           if (!ferror(infd) || errno != EINTR || !_cursesi_screen->resized)
1008                     return ERR;
1009           __CTRACE(__CTRACE_INPUT, "__fgetc_resize returning KEY_RESIZE\n");
1010           resizeterm(LINES, COLS);
1011           _cursesi_screen->resized = 0;
1012           return KEY_RESIZE;
1013 }
1014