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