1 /* $OpenBSD: kbd.c,v 1.37 2023/03/08 04:43:11 guenther Exp $ */
2
3 /* This file is in the public domain. */
4
5 /*
6 * Terminal independent keyboard handling.
7 */
8
9 #include <sys/queue.h>
10 #include <signal.h>
11 #include <stdio.h>
12
13 #include "def.h"
14 #include "kbd.h"
15 #include "key.h"
16 #include "macro.h"
17
18 #ifdef MGLOG
19 #include "log.h"
20 #endif
21
22 #define METABIT 0x80
23
24 #define PROMPTL 80
25 char prompt[PROMPTL] = "", *promptp = prompt;
26
27 static int mgwrap(PF, int, int);
28
29 static int use_metakey = TRUE;
30 static int pushed = FALSE;
31 static int pushedc;
32
33 struct map_element *ele;
34 struct key key;
35 int rptcount;
36
37 /*
38 * Toggle the value of use_metakey
39 */
40 int
do_meta(int f,int n)41 do_meta(int f, int n)
42 {
43 if (f & FFARG)
44 use_metakey = n > 0;
45 else
46 use_metakey = !use_metakey;
47 ewprintf("Meta keys %sabled", use_metakey ? "en" : "dis");
48 return (TRUE);
49 }
50
51 static int bs_map = 0;
52
53 /*
54 * Toggle backspace mapping
55 */
56 int
bsmap(int f,int n)57 bsmap(int f, int n)
58 {
59 if (f & FFARG)
60 bs_map = n > 0;
61 else
62 bs_map = !bs_map;
63 ewprintf("Backspace mapping %sabled", bs_map ? "en" : "dis");
64 return (TRUE);
65 }
66
67 void
ungetkey(int c)68 ungetkey(int c)
69 {
70 if (use_metakey && pushed && c == CCHR('['))
71 pushedc |= METABIT;
72 else
73 pushedc = c;
74 pushed = TRUE;
75 }
76
77 int
getkey(int flag)78 getkey(int flag)
79 {
80 int c;
81
82 if (flag && !pushed) {
83 if (prompt[0] != '\0' && ttwait(2000)) {
84 /* avoid problems with % */
85 ewprintf("%s", prompt);
86 /* put the cursor back */
87 update(CMODE);
88 epresf = KCLEAR;
89 }
90 if (promptp > prompt)
91 *(promptp - 1) = ' ';
92 }
93 if (pushed) {
94 c = pushedc;
95 pushed = FALSE;
96 } else
97 c = ttgetc();
98
99 if (bs_map) {
100 if (c == CCHR('H'))
101 c = CCHR('?');
102 else if (c == CCHR('?'))
103 c = CCHR('H');
104 }
105 if (use_metakey && (c & METABIT)) {
106 pushedc = c & ~METABIT;
107 pushed = TRUE;
108 c = CCHR('[');
109 }
110 if (flag && promptp < &prompt[PROMPTL - 5]) {
111 promptp = getkeyname(promptp,
112 sizeof(prompt) - (promptp - prompt) - 1, c);
113 *promptp++ = '-';
114 *promptp = '\0';
115 }
116 return (c);
117 }
118
119 /*
120 * doscan scans a keymap for a keyboard character and returns a pointer
121 * to the function associated with that character. Sets ele to the
122 * keymap element the keyboard was found in as a side effect.
123 */
124 PF
doscan(KEYMAP * map,int c,KEYMAP ** newmap)125 doscan(KEYMAP *map, int c, KEYMAP **newmap)
126 {
127 struct map_element *elec = &map->map_element[0];
128 struct map_element *last = &map->map_element[map->map_num];
129 PF ret;
130
131 while (elec < last && c > elec->k_num)
132 elec++;
133
134 /* used by prefix and binding code */
135 ele = elec;
136 if (elec >= last || c < elec->k_base)
137 ret = map->map_default;
138 else
139 ret = elec->k_funcp[c - elec->k_base];
140 if (ret == NULL && newmap != NULL)
141 *newmap = elec->k_prefmap;
142
143 return (ret);
144 }
145
146 int
doin(void)147 doin(void)
148 {
149 KEYMAP *curmap;
150 PF funct;
151
152 *(promptp = prompt) = '\0';
153 curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
154 key.k_count = 0;
155 while ((funct = doscan(curmap, (key.k_chars[key.k_count++] =
156 getkey(TRUE)), &curmap)) == NULL)
157 /* nothing */;
158
159 #ifdef MGLOG
160 if (!mglog(funct, curmap))
161 ewprintf("Problem with logging");
162 #endif
163
164 if (macrodef && macrocount < MAXMACRO)
165 macro[macrocount++].m_funct = funct;
166
167 return (mgwrap(funct, 0, 1));
168 }
169
170 int
rescan(int f,int n)171 rescan(int f, int n)
172 {
173 int c;
174 KEYMAP *curmap;
175 int i;
176 PF fp = NULL;
177 int md = curbp->b_nmodes;
178
179 for (;;) {
180 if (ISUPPER(key.k_chars[key.k_count - 1])) {
181 c = TOLOWER(key.k_chars[key.k_count - 1]);
182 curmap = curbp->b_modes[md]->p_map;
183 for (i = 0; i < key.k_count - 1; i++) {
184 if ((fp = doscan(curmap, (key.k_chars[i]),
185 &curmap)) != NULL)
186 break;
187 }
188 if (fp == NULL) {
189 if ((fp = doscan(curmap, c, NULL)) == NULL)
190 while ((fp = doscan(curmap,
191 key.k_chars[key.k_count++] =
192 getkey(TRUE), &curmap)) == NULL)
193 /* nothing */;
194 if (fp != rescan) {
195 if (macrodef && macrocount <= MAXMACRO)
196 macro[macrocount - 1].m_funct
197 = fp;
198 return (mgwrap(fp, f, n));
199 }
200 }
201 }
202 /* try previous mode */
203 if (--md < 0)
204 return (ABORT);
205 curmap = curbp->b_modes[md]->p_map;
206 for (i = 0; i < key.k_count; i++) {
207 if ((fp = doscan(curmap, (key.k_chars[i]), &curmap)) != NULL)
208 break;
209 }
210 if (fp == NULL) {
211 while ((fp = doscan(curmap, key.k_chars[i++] =
212 getkey(TRUE), &curmap)) == NULL)
213 /* nothing */;
214 key.k_count = i;
215 }
216 if (fp != rescan && i >= key.k_count - 1) {
217 if (macrodef && macrocount <= MAXMACRO)
218 macro[macrocount - 1].m_funct = fp;
219 return (mgwrap(fp, f, n));
220 }
221 }
222 }
223
224 int
universal_argument(int f,int n)225 universal_argument(int f, int n)
226 {
227 KEYMAP *curmap;
228 PF funct;
229 int c, nn = 4;
230
231 if (f & FFUNIV)
232 nn *= n;
233 for (;;) {
234 key.k_chars[0] = c = getkey(TRUE);
235 key.k_count = 1;
236 if (c == '-')
237 return (negative_argument(f, nn));
238 if (c >= '0' && c <= '9')
239 return (digit_argument(f, nn));
240 curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
241 while ((funct = doscan(curmap, c, &curmap)) == NULL) {
242 key.k_chars[key.k_count++] = c = getkey(TRUE);
243 }
244 if (funct != universal_argument) {
245 if (macrodef && macrocount < MAXMACRO - 1) {
246 if (f & FFARG)
247 macrocount--;
248 macro[macrocount++].m_count = nn;
249 macro[macrocount++].m_funct = funct;
250 }
251 return (mgwrap(funct, FFUNIV, nn));
252 }
253 nn <<= 2;
254 }
255 }
256
257 int
digit_argument(int f,int n)258 digit_argument(int f, int n)
259 {
260 KEYMAP *curmap;
261 PF funct;
262 int nn, c;
263
264 nn = key.k_chars[key.k_count - 1] - '0';
265 for (;;) {
266 c = getkey(TRUE);
267 if (c < '0' || c > '9')
268 break;
269 nn *= 10;
270 nn += c - '0';
271 }
272 key.k_chars[0] = c;
273 key.k_count = 1;
274 curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
275 while ((funct = doscan(curmap, c, &curmap)) == NULL) {
276 key.k_chars[key.k_count++] = c = getkey(TRUE);
277 }
278 if (macrodef && macrocount < MAXMACRO - 1) {
279 if (f & FFARG)
280 macrocount--;
281 else
282 macro[macrocount - 1].m_funct = universal_argument;
283 macro[macrocount++].m_count = nn;
284 macro[macrocount++].m_funct = funct;
285 }
286 return (mgwrap(funct, FFOTHARG, nn));
287 }
288
289 int
negative_argument(int f,int n)290 negative_argument(int f, int n)
291 {
292 KEYMAP *curmap;
293 PF funct;
294 int c;
295 int nn = 0;
296
297 for (;;) {
298 c = getkey(TRUE);
299 if (c < '0' || c > '9')
300 break;
301 nn *= 10;
302 nn += c - '0';
303 }
304 if (nn)
305 nn = -nn;
306 else
307 nn = -n;
308 key.k_chars[0] = c;
309 key.k_count = 1;
310 curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
311 while ((funct = doscan(curmap, c, &curmap)) == NULL) {
312 key.k_chars[key.k_count++] = c = getkey(TRUE);
313 }
314 if (macrodef && macrocount < MAXMACRO - 1) {
315 if (f & FFARG)
316 macrocount--;
317 else
318 macro[macrocount - 1].m_funct = universal_argument;
319 macro[macrocount++].m_count = nn;
320 macro[macrocount++].m_funct = funct;
321 }
322 return (mgwrap(funct, FFNEGARG, nn));
323 }
324
325 /*
326 * Insert a character. While defining a macro, create a "LINE" containing
327 * all inserted characters.
328 */
329 int
selfinsert(int f,int n)330 selfinsert(int f, int n)
331 {
332 struct line *lp;
333 int c;
334 int count;
335
336 if (n < 0)
337 return (FALSE);
338 if (n == 0)
339 return (TRUE);
340 c = key.k_chars[key.k_count - 1];
341
342 if (macrodef && macrocount < MAXMACRO) {
343 if (f & FFARG)
344 macrocount -= 2;
345
346 /* last command was insert -- tack on the end */
347 if (lastflag & CFINS) {
348 macrocount--;
349 /* Ensure the line can handle the new characters */
350 if (maclcur->l_size < maclcur->l_used + n) {
351 if (lrealloc(maclcur, maclcur->l_used + n) ==
352 FALSE)
353 return (FALSE);
354 }
355 maclcur->l_used += n;
356 /* Copy in the new data */
357 for (count = maclcur->l_used - n;
358 count < maclcur->l_used; count++)
359 maclcur->l_text[count] = c;
360 } else {
361 macro[macrocount - 1].m_funct = insert;
362 if ((lp = lalloc(n)) == NULL)
363 return (FALSE);
364 lp->l_bp = maclcur;
365 lp->l_fp = maclcur->l_fp;
366 maclcur->l_fp = lp;
367 maclcur = lp;
368 for (count = 0; count < n; count++)
369 lp->l_text[count] = c;
370 }
371 thisflag |= CFINS;
372 }
373 if (c == *curbp->b_nlchr) {
374 do {
375 count = lnewline();
376 } while (--n && count == TRUE);
377 return (count);
378 }
379
380 /* overwrite mode */
381 if (curbp->b_flag & BFOVERWRITE) {
382 lchange(WFEDIT);
383 while (curwp->w_doto < llength(curwp->w_dotp) && n--)
384 lputc(curwp->w_dotp, curwp->w_doto++, c);
385 if (n <= 0)
386 return (TRUE);
387 }
388 return (linsert(n, c));
389 }
390
391 /*
392 * selfinsert() can't be called directly from a startup file or by
393 * 'eval-current-buffer' since it is by design, meant to be called interactively
394 * as characters are typed in a buffer. ask_selfinsert() allows selfinsert() to
395 * be used by excline(). Having ask_selfinsert() helps with regression testing.
396 * No manual page entry since use case is a bit obscure. See 'insert' command.
397 */
398 int
ask_selfinsert(int f,int n)399 ask_selfinsert(int f, int n)
400 {
401 char *c, cbuf[2];
402
403 if ((c = eread("Insert a character: ", cbuf, sizeof(cbuf),
404 EFNEW)) == NULL || (c[0] == '\0'))
405 return (ABORT);
406
407 key.k_chars[0] = *c;
408 key.k_chars[1] = '\0';
409 key.k_count = 1;
410
411 return (selfinsert(FFRAND, 1));
412 }
413
414 /*
415 * This could be implemented as a keymap with everything defined as self-insert.
416 */
417 int
quote(int f,int n)418 quote(int f, int n)
419 {
420 int c;
421
422 key.k_count = 1;
423 if ((key.k_chars[0] = getkey(TRUE)) >= '0' && key.k_chars[0] <= '7') {
424 key.k_chars[0] -= '0';
425 if ((c = getkey(TRUE)) >= '0' && c <= '7') {
426 key.k_chars[0] <<= 3;
427 key.k_chars[0] += c - '0';
428 if ((c = getkey(TRUE)) >= '0' && c <= '7') {
429 key.k_chars[0] <<= 3;
430 key.k_chars[0] += c - '0';
431 } else
432 ungetkey(c);
433 } else
434 ungetkey(c);
435 }
436 return (selfinsert(f, n));
437 }
438
439 /*
440 * Wrapper function to count invocation repeats.
441 * We ignore any function whose sole purpose is to get us
442 * to the intended function.
443 */
444 static int
mgwrap(PF funct,int f,int n)445 mgwrap(PF funct, int f, int n)
446 {
447 static PF ofp;
448
449 if (funct != rescan &&
450 funct != negative_argument &&
451 funct != digit_argument &&
452 funct != universal_argument) {
453 if (funct == ofp)
454 rptcount++;
455 else
456 rptcount = 0;
457 ofp = funct;
458 }
459
460 return ((*funct)(f, n));
461 }
462