xref: /openbsd/usr.bin/mg/kbd.c (revision 5b133f3f)
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