xref: /openbsd-src/usr.bin/mg/extend.c (revision f2da64fbbbf1b03f09f390ab01267c93dfd77c4c)
1 /*	$OpenBSD: extend.c,v 1.64 2016/09/01 21:06:09 lum Exp $	*/
2 
3 /* This file is in the public domain. */
4 
5 /*
6  *	Extended (M-X) commands, rebinding, and	startup file processing.
7  */
8 
9 #include <sys/queue.h>
10 #include <sys/types.h>
11 #include <ctype.h>
12 #include <limits.h>
13 #include <signal.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17 
18 #include "chrdef.h"
19 #include "def.h"
20 #include "funmap.h"
21 #include "kbd.h"
22 #include "key.h"
23 #include "macro.h"
24 
25 static int	 remap(KEYMAP *, int, PF, KEYMAP *);
26 static KEYMAP	*reallocmap(KEYMAP *);
27 static void	 fixmap(KEYMAP *, KEYMAP *, KEYMAP *);
28 static int	 dobind(KEYMAP *, const char *, int);
29 static char	*skipwhite(char *);
30 static char	*parsetoken(char *);
31 static int	 bindkey(KEYMAP **, const char *, KCHAR *, int);
32 
33 /*
34  * Insert a string, mainly for use from macros (created by selfinsert).
35  */
36 /* ARGSUSED */
37 int
38 insert(int f, int n)
39 {
40 	char	 buf[128], *bufp, *cp;
41 	int	 count, c;
42 
43 	if (inmacro) {
44 		while (--n >= 0) {
45 			for (count = 0; count < maclcur->l_used; count++) {
46 				if ((((c = maclcur->l_text[count]) == '\n')
47 				    ? lnewline() : linsert(1, c)) != TRUE)
48 					return (FALSE);
49 			}
50 		}
51 		maclcur = maclcur->l_fp;
52 		return (TRUE);
53 	}
54 	if (n == 1)
55 		/* CFINS means selfinsert can tack on the end */
56 		thisflag |= CFINS;
57 
58 	if ((bufp = eread("Insert: ", buf, sizeof(buf), EFNEW)) == NULL)
59 		return (ABORT);
60 	else if (bufp[0] == '\0')
61 		return (FALSE);
62 	while (--n >= 0) {
63 		cp = buf;
64 		while (*cp) {
65 			if (((*cp == '\n') ? lnewline() : linsert(1, *cp))
66 			    != TRUE)
67 				return (FALSE);
68 			cp++;
69 		}
70 	}
71 	return (TRUE);
72 }
73 
74 /*
75  * Bind a key to a function.  Cases range from the trivial (replacing an
76  * existing binding) to the extremely complex (creating a new prefix in a
77  * map_element that already has one, so the map_element must be split,
78  * but the keymap doesn't have enough room for another map_element, so
79  * the keymap is reallocated).	No attempt is made to reclaim space no
80  * longer used, if this is a problem flags must be added to indicate
81  * malloced versus static storage in both keymaps and map_elements.
82  * Structure assignments would come in real handy, but K&R based compilers
83  * don't have them.  Care is taken so running out of memory will leave
84  * the keymap in a usable state.
85  * Parameters are:
86  * curmap:  	pointer to the map being changed
87  * c:		character being changed
88  * funct: 	function being changed to
89  * pref_map: 	if funct==NULL, map to bind to or NULL for new
90  */
91 static int
92 remap(KEYMAP *curmap, int c, PF funct, KEYMAP *pref_map)
93 {
94 	int		 i, n1, n2, nold;
95 	KEYMAP		*mp, *newmap;
96 	PF		*pfp;
97 	struct map_element	*mep;
98 
99 	if (ele >= &curmap->map_element[curmap->map_num] || c < ele->k_base) {
100 		if (ele > &curmap->map_element[0] && (funct != NULL ||
101 		    (ele - 1)->k_prefmap == NULL))
102 			n1 = c - (ele - 1)->k_num;
103 		else
104 			n1 = HUGE;
105 		if (ele < &curmap->map_element[curmap->map_num] &&
106 		    (funct != NULL || ele->k_prefmap == NULL))
107 			n2 = ele->k_base - c;
108 		else
109 			n2 = HUGE;
110 		if (n1 <= MAPELEDEF && n1 <= n2) {
111 			ele--;
112 			if ((pfp = calloc(c - ele->k_base + 1,
113 			    sizeof(PF))) == NULL) {
114 				dobeep();
115 				ewprintf("Out of memory");
116 				return (FALSE);
117 			}
118 			nold = ele->k_num - ele->k_base + 1;
119 			for (i = 0; i < nold; i++)
120 				pfp[i] = ele->k_funcp[i];
121 			while (--n1)
122 				pfp[i++] = curmap->map_default;
123 			pfp[i] = funct;
124 			ele->k_num = c;
125 			ele->k_funcp = pfp;
126 		} else if (n2 <= MAPELEDEF) {
127 			if ((pfp = calloc(ele->k_num - c + 1,
128 			    sizeof(PF))) == NULL) {
129 				dobeep();
130 				ewprintf("Out of memory");
131 				return (FALSE);
132 			}
133 			nold = ele->k_num - ele->k_base + 1;
134 			for (i = 0; i < nold; i++)
135 				pfp[i + n2] = ele->k_funcp[i];
136 			while (--n2)
137 				pfp[n2] = curmap->map_default;
138 			pfp[0] = funct;
139 			ele->k_base = c;
140 			ele->k_funcp = pfp;
141 		} else {
142 			if (curmap->map_num >= curmap->map_max) {
143 				if ((newmap = reallocmap(curmap)) == NULL)
144 					return (FALSE);
145 				curmap = newmap;
146 			}
147 			if ((pfp = malloc(sizeof(PF))) == NULL) {
148 				dobeep();
149 				ewprintf("Out of memory");
150 				return (FALSE);
151 			}
152 			pfp[0] = funct;
153 			for (mep = &curmap->map_element[curmap->map_num];
154 			    mep > ele; mep--) {
155 				mep->k_base = (mep - 1)->k_base;
156 				mep->k_num = (mep - 1)->k_num;
157 				mep->k_funcp = (mep - 1)->k_funcp;
158 				mep->k_prefmap = (mep - 1)->k_prefmap;
159 			}
160 			ele->k_base = c;
161 			ele->k_num = c;
162 			ele->k_funcp = pfp;
163 			ele->k_prefmap = NULL;
164 			curmap->map_num++;
165 		}
166 		if (funct == NULL) {
167 			if (pref_map != NULL)
168 				ele->k_prefmap = pref_map;
169 			else {
170 				if ((mp = malloc(sizeof(KEYMAP) +
171 				    (MAPINIT - 1) * sizeof(struct map_element))) == NULL) {
172 					dobeep();
173 					ewprintf("Out of memory");
174 					ele->k_funcp[c - ele->k_base] =
175 					    curmap->map_default;
176 					return (FALSE);
177 				}
178 				mp->map_num = 0;
179 				mp->map_max = MAPINIT;
180 				mp->map_default = rescan;
181 				ele->k_prefmap = mp;
182 			}
183 		}
184 	} else {
185 		n1 = c - ele->k_base;
186 		if (ele->k_funcp[n1] == funct && (funct != NULL ||
187 		    pref_map == NULL || pref_map == ele->k_prefmap))
188 			/* no change */
189 			return (TRUE);
190 		if (funct != NULL || ele->k_prefmap == NULL) {
191 			if (ele->k_funcp[n1] == NULL)
192 				ele->k_prefmap = NULL;
193 			/* easy case */
194 			ele->k_funcp[n1] = funct;
195 			if (funct == NULL) {
196 				if (pref_map != NULL)
197 					ele->k_prefmap = pref_map;
198 				else {
199 					if ((mp = malloc(sizeof(KEYMAP) +
200 					    (MAPINIT - 1) *
201 					    sizeof(struct map_element))) == NULL) {
202 						dobeep();
203 						ewprintf("Out of memory");
204 						ele->k_funcp[c - ele->k_base] =
205 						    curmap->map_default;
206 						return (FALSE);
207 					}
208 					mp->map_num = 0;
209 					mp->map_max = MAPINIT;
210 					mp->map_default = rescan;
211 					ele->k_prefmap = mp;
212 				}
213 			}
214 		} else {
215 			/*
216 			 * This case is the splits.
217 			 * Determine which side of the break c goes on
218 			 * 0 = after break; 1 = before break
219 			 */
220 			n2 = 1;
221 			for (i = 0; n2 && i < n1; i++)
222 				n2 &= ele->k_funcp[i] != NULL;
223 			if (curmap->map_num >= curmap->map_max) {
224 				if ((newmap = reallocmap(curmap)) == NULL)
225 					return (FALSE);
226 				curmap = newmap;
227 			}
228 			if ((pfp = calloc(ele->k_num - c + !n2,
229 			    sizeof(PF))) == NULL) {
230 				dobeep();
231 				ewprintf("Out of memory");
232 				return (FALSE);
233 			}
234 			ele->k_funcp[n1] = NULL;
235 			for (i = n1 + n2; i <= ele->k_num - ele->k_base; i++)
236 				pfp[i - n1 - n2] = ele->k_funcp[i];
237 			for (mep = &curmap->map_element[curmap->map_num];
238 			    mep > ele; mep--) {
239 				mep->k_base = (mep - 1)->k_base;
240 				mep->k_num = (mep - 1)->k_num;
241 				mep->k_funcp = (mep - 1)->k_funcp;
242 				mep->k_prefmap = (mep - 1)->k_prefmap;
243 			}
244 			ele->k_num = c - !n2;
245 			(ele + 1)->k_base = c + n2;
246 			(ele + 1)->k_funcp = pfp;
247 			ele += !n2;
248 			ele->k_prefmap = NULL;
249 			curmap->map_num++;
250 			if (pref_map == NULL) {
251 				if ((mp = malloc(sizeof(KEYMAP) + (MAPINIT - 1)
252 				    * sizeof(struct map_element))) == NULL) {
253 					dobeep();
254 					ewprintf("Out of memory");
255 					ele->k_funcp[c - ele->k_base] =
256 					    curmap->map_default;
257 					return (FALSE);
258 				}
259 				mp->map_num = 0;
260 				mp->map_max = MAPINIT;
261 				mp->map_default = rescan;
262 				ele->k_prefmap = mp;
263 			} else
264 				ele->k_prefmap = pref_map;
265 		}
266 	}
267 	return (TRUE);
268 }
269 
270 /*
271  * Reallocate a keymap. Returns NULL (without trashing the current map)
272  * on failure.
273  */
274 static KEYMAP *
275 reallocmap(KEYMAP *curmap)
276 {
277 	struct maps_s	*mps;
278 	KEYMAP	*mp;
279 	int	 i;
280 
281 	if (curmap->map_max > SHRT_MAX - MAPGROW) {
282 		dobeep();
283 		ewprintf("keymap too large");
284 		return (NULL);
285 	}
286 	if ((mp = malloc(sizeof(KEYMAP) + (curmap->map_max + (MAPGROW - 1)) *
287 	    sizeof(struct map_element))) == NULL) {
288 		dobeep();
289 		ewprintf("Out of memory");
290 		return (NULL);
291 	}
292 	mp->map_num = curmap->map_num;
293 	mp->map_max = curmap->map_max + MAPGROW;
294 	mp->map_default = curmap->map_default;
295 	for (i = curmap->map_num; i--;) {
296 		mp->map_element[i].k_base = curmap->map_element[i].k_base;
297 		mp->map_element[i].k_num = curmap->map_element[i].k_num;
298 		mp->map_element[i].k_funcp = curmap->map_element[i].k_funcp;
299 		mp->map_element[i].k_prefmap = curmap->map_element[i].k_prefmap;
300 	}
301 	for (mps = maps; mps != NULL; mps = mps->p_next) {
302 		if (mps->p_map == curmap)
303 			mps->p_map = mp;
304 		else
305 			fixmap(curmap, mp, mps->p_map);
306 	}
307 	ele = &mp->map_element[ele - &curmap->map_element[0]];
308 	return (mp);
309 }
310 
311 /*
312  * Fix references to a reallocated keymap (recursive).
313  */
314 static void
315 fixmap(KEYMAP *curmap, KEYMAP *mp, KEYMAP *mt)
316 {
317 	int	 i;
318 
319 	for (i = mt->map_num; i--;) {
320 		if (mt->map_element[i].k_prefmap != NULL) {
321 			if (mt->map_element[i].k_prefmap == curmap)
322 				mt->map_element[i].k_prefmap = mp;
323 			else
324 				fixmap(curmap, mp, mt->map_element[i].k_prefmap);
325 		}
326 	}
327 }
328 
329 /*
330  * Do the input for local-set-key, global-set-key  and define-key
331  * then call remap to do the work.
332  */
333 static int
334 dobind(KEYMAP *curmap, const char *p, int unbind)
335 {
336 	KEYMAP	*pref_map = NULL;
337 	PF	 funct;
338 	char	 bprompt[80], *bufp, *pep;
339 	int	 c, s, n;
340 
341 	if (macrodef) {
342 		/*
343 		 * Keystrokes aren't collected. Not hard, but pretty useless.
344 		 * Would not work for function keys in any case.
345 		 */
346 		dobeep();
347 		ewprintf("Can't rebind key in macro");
348 		return (FALSE);
349 	}
350 	if (inmacro) {
351 		for (s = 0; s < maclcur->l_used - 1; s++) {
352 			if (doscan(curmap, c = CHARMASK(maclcur->l_text[s]), &curmap)
353 			    != NULL) {
354 				if (remap(curmap, c, NULL, NULL)
355 				    != TRUE)
356 					return (FALSE);
357 			}
358 		}
359 		(void)doscan(curmap, c = maclcur->l_text[s], NULL);
360 		maclcur = maclcur->l_fp;
361 	} else {
362 		n = strlcpy(bprompt, p, sizeof(bprompt));
363 		if (n >= sizeof(bprompt))
364 			n = sizeof(bprompt) - 1;
365 		pep = bprompt + n;
366 		for (;;) {
367 			ewprintf("%s", bprompt);
368 			pep[-1] = ' ';
369 			pep = getkeyname(pep, sizeof(bprompt) -
370 			    (pep - bprompt), c = getkey(FALSE));
371 			if (doscan(curmap, c, &curmap) != NULL)
372 				break;
373 			*pep++ = '-';
374 			*pep = '\0';
375 		}
376 	}
377 	if (unbind)
378 		funct = rescan;
379 	else {
380 		if ((bufp = eread("%s to command: ", bprompt, sizeof(bprompt),
381 		    EFFUNC | EFNEW, bprompt)) == NULL)
382 			return (ABORT);
383 		else if (bufp[0] == '\0')
384 			return (FALSE);
385 		if (((funct = name_function(bprompt)) == NULL) ?
386 		    (pref_map = name_map(bprompt)) == NULL : funct == NULL) {
387 			dobeep();
388 			ewprintf("[No match]");
389 			return (FALSE);
390 		}
391 	}
392 	return (remap(curmap, c, funct, pref_map));
393 }
394 
395 /*
396  * bindkey: bind key sequence to a function in the specified map.  Used by
397  * excline so it can bind function keys.  To close to release to change
398  * calling sequence, should just pass KEYMAP *curmap rather than
399  * KEYMAP **mapp.
400  */
401 static int
402 bindkey(KEYMAP **mapp, const char *fname, KCHAR *keys, int kcount)
403 {
404 	KEYMAP	*curmap = *mapp;
405 	KEYMAP	*pref_map = NULL;
406 	PF	 funct;
407 	int	 c;
408 
409 	if (fname == NULL)
410 		funct = rescan;
411 	else if (((funct = name_function(fname)) == NULL) ?
412 	    (pref_map = name_map(fname)) == NULL : funct == NULL) {
413 		dobeep();
414 		ewprintf("[No match: %s]", fname);
415 		return (FALSE);
416 	}
417 	while (--kcount) {
418 		if (doscan(curmap, c = *keys++, &curmap) != NULL) {
419 			if (remap(curmap, c, NULL, NULL) != TRUE)
420 				return (FALSE);
421 			/*
422 			 * XXX - Bizzarreness. remap creates an empty KEYMAP
423 			 *       that the last key is supposed to point to.
424 			 */
425 			curmap = ele->k_prefmap;
426 		}
427 	}
428 	(void)doscan(curmap, c = *keys, NULL);
429 	return (remap(curmap, c, funct, pref_map));
430 }
431 
432 /*
433  * Wrapper for bindkey() that converts escapes.
434  */
435 int
436 dobindkey(KEYMAP *map, const char *func, const char *str)
437 {
438 	int	 i;
439 
440 	for (i = 0; *str && i < MAXKEY; i++) {
441 		/* XXX - convert numbers w/ strol()? */
442 		if (*str == '^' && *(str + 1) !=  '\0') {
443 			key.k_chars[i] = CCHR(toupper((unsigned char)*++str));
444 		} else if (*str == '\\' && *(str + 1) != '\0') {
445 			switch (*++str) {
446 			case '^':
447 				key.k_chars[i] = '^';
448 				break;
449 			case 't':
450 			case 'T':
451 				key.k_chars[i] = '\t';
452 				break;
453 			case 'n':
454 			case 'N':
455 				key.k_chars[i] = '\n';
456 				break;
457 			case 'r':
458 			case 'R':
459 				key.k_chars[i] = '\r';
460 				break;
461 			case 'e':
462 			case 'E':
463 				key.k_chars[i] = CCHR('[');
464 				break;
465 			case '\\':
466 				key.k_chars[i] = '\\';
467 				break;
468 			}
469 		} else
470 			key.k_chars[i] = *str;
471 		str++;
472 	}
473 	key.k_count = i;
474 	return (bindkey(&map, func, key.k_chars, key.k_count));
475 }
476 
477 /*
478  * This function modifies the fundamental keyboard map.
479  */
480 /* ARGSUSED */
481 int
482 bindtokey(int f, int n)
483 {
484 	return (dobind(fundamental_map, "Global set key: ", FALSE));
485 }
486 
487 /*
488  * This function modifies the current mode's keyboard map.
489  */
490 /* ARGSUSED */
491 int
492 localbind(int f, int n)
493 {
494 	return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
495 	    "Local set key: ", FALSE));
496 }
497 
498 /*
499  * This function redefines a key in any keymap.
500  */
501 /* ARGSUSED */
502 int
503 redefine_key(int f, int n)
504 {
505 	static char	 buf[48];
506 	char		 tmp[32], *bufp;
507 	KEYMAP		*mp;
508 
509 	(void)strlcpy(buf, "Define key map: ", sizeof(buf));
510 	if ((bufp = eread("%s", tmp, sizeof(tmp), EFNEW, buf)) == NULL)
511 		return (ABORT);
512 	else if (bufp[0] == '\0')
513 		return (FALSE);
514 	(void)strlcat(buf, tmp, sizeof(buf));
515 	if ((mp = name_map(tmp)) == NULL) {
516 		dobeep();
517 		ewprintf("Unknown map %s", tmp);
518 		return (FALSE);
519 	}
520 	if (strlcat(buf, "key: ", sizeof(buf)) >= sizeof(buf))
521 		return (FALSE);
522 
523 	return (dobind(mp, buf, FALSE));
524 }
525 
526 /* ARGSUSED */
527 int
528 unbindtokey(int f, int n)
529 {
530 	return (dobind(fundamental_map, "Global unset key: ", TRUE));
531 }
532 
533 /* ARGSUSED */
534 int
535 localunbind(int f, int n)
536 {
537 	return (dobind(curbp->b_modes[curbp->b_nmodes]->p_map,
538 	    "Local unset key: ", TRUE));
539 }
540 
541 /*
542  * Extended command. Call the message line routine to read in the command
543  * name and apply autocompletion to it. When it comes back, look the name
544  * up in the symbol table and run the command if it is found.  Print an
545  * error if there is anything wrong.
546  */
547 int
548 extend(int f, int n)
549 {
550 	PF	 funct;
551 	char	 xname[NXNAME], *bufp;
552 
553 	if (!(f & FFARG))
554 		bufp = eread("M-x ", xname, NXNAME, EFNEW | EFFUNC);
555 	else
556 		bufp = eread("%d M-x ", xname, NXNAME, EFNEW | EFFUNC, n);
557 	if (bufp == NULL)
558 		return (ABORT);
559 	else if (bufp[0] == '\0')
560 		return (FALSE);
561 	if ((funct = name_function(bufp)) != NULL) {
562 		if (macrodef) {
563 			struct line	*lp = maclcur;
564 			macro[macrocount - 1].m_funct = funct;
565 			maclcur = lp->l_bp;
566 			maclcur->l_fp = lp->l_fp;
567 			free(lp);
568 		}
569 		return ((*funct)(f, n));
570 	}
571 	dobeep();
572 	ewprintf("[No match]");
573 	return (FALSE);
574 }
575 
576 /*
577  * Define the commands needed to do startup-file processing.
578  * This code is mostly a kludge just so we can get startup-file processing.
579  *
580  * If you're serious about having this code, you should rewrite it.
581  * To wit:
582  *	It has lots of funny things in it to make the startup-file look
583  *	like a GNU startup file; mostly dealing with parens and semicolons.
584  *	This should all vanish.
585  *
586  * We define eval-expression because it's easy.	 It can make
587  * *-set-key or define-key set an arbitrary key sequence, so it isn't
588  * useless.
589  */
590 
591 /*
592  * evalexpr - get one line from the user, and run it.
593  */
594 /* ARGSUSED */
595 int
596 evalexpr(int f, int n)
597 {
598 	char	 exbuf[128], *bufp;
599 
600 	if ((bufp = eread("Eval: ", exbuf, sizeof(exbuf),
601 	    EFNEW | EFCR)) == NULL)
602 		return (ABORT);
603 	else if (bufp[0] == '\0')
604 		return (FALSE);
605 	return (excline(exbuf));
606 }
607 
608 /*
609  * evalbuffer - evaluate the current buffer as line commands. Useful for
610  * testing startup files.
611  */
612 /* ARGSUSED */
613 int
614 evalbuffer(int f, int n)
615 {
616 	struct line		*lp;
617 	struct buffer		*bp = curbp;
618 	int		 s;
619 	static char	 excbuf[128];
620 
621 	for (lp = bfirstlp(bp); lp != bp->b_headp; lp = lforw(lp)) {
622 		if (llength(lp) >= 128)
623 			return (FALSE);
624 		(void)strncpy(excbuf, ltext(lp), llength(lp));
625 
626 		/* make sure it's terminated */
627 		excbuf[llength(lp)] = '\0';
628 		if ((s = excline(excbuf)) != TRUE)
629 			return (s);
630 	}
631 	return (TRUE);
632 }
633 
634 /*
635  * evalfile - go get a file and evaluate it as line commands. You can
636  *	go get your own startup file if need be.
637  */
638 /* ARGSUSED */
639 int
640 evalfile(int f, int n)
641 {
642 	char	 fname[NFILEN], *bufp;
643 
644 	if ((bufp = eread("Load file: ", fname, NFILEN,
645 	    EFNEW | EFCR)) == NULL)
646 		return (ABORT);
647 	else if (bufp[0] == '\0')
648 		return (FALSE);
649 	return (load(fname));
650 }
651 
652 /*
653  * load - go load the file name we got passed.
654  */
655 int
656 load(const char *fname)
657 {
658 	int	 s = TRUE, line, ret;
659 	int	 nbytes = 0;
660 	char	 excbuf[128];
661 	FILE    *ffp;
662 
663 	if ((fname = adjustname(fname, TRUE)) == NULL)
664 		/* just to be careful */
665 		return (FALSE);
666 
667 	ret = ffropen(&ffp, fname, NULL);
668 	if (ret != FIOSUC) {
669 		if (ret == FIODIR)
670 			(void)ffclose(ffp, NULL);
671 		return (FALSE);
672 	}
673 
674 	line = 0;
675 	while ((s = ffgetline(ffp, excbuf, sizeof(excbuf) - 1, &nbytes))
676 	    == FIOSUC) {
677 		line++;
678 		excbuf[nbytes] = '\0';
679 		if (excline(excbuf) != TRUE) {
680 			s = FIOERR;
681 			dobeep();
682 			ewprintf("Error loading file %s at line %d", fname, line);
683 			break;
684 		}
685 	}
686 	(void)ffclose(ffp, NULL);
687 	excbuf[nbytes] = '\0';
688 	if (s != FIOEOF || (nbytes && excline(excbuf) != TRUE))
689 		return (FALSE);
690 	return (TRUE);
691 }
692 
693 /*
694  * excline - run a line from a load file or eval-expression.
695  */
696 int
697 excline(char *line)
698 {
699 	PF	 fp;
700 	struct line	*lp, *np;
701 	int	 status, c, f, n;
702 	char	*funcp, *tmp;
703 	char	*argp = NULL;
704 	long	 nl;
705 	int	 bind;
706 	KEYMAP	*curmap;
707 #define BINDARG		0  /* this arg is key to bind (local/global set key) */
708 #define	BINDNO		1  /* not binding or non-quoted BINDARG */
709 #define BINDNEXT	2  /* next arg " (define-key) */
710 #define BINDDO		3  /* already found key to bind */
711 #define BINDEXT		1  /* space for trailing \0 */
712 
713 	lp = NULL;
714 
715 	if (macrodef || inmacro) {
716 		dobeep();
717 		ewprintf("Not now!");
718 		return (FALSE);
719 	}
720 	f = 0;
721 	n = 1;
722 	funcp = skipwhite(line);
723 	if (*funcp == '\0')
724 		return (TRUE);	/* No error on blank lines */
725 	line = parsetoken(funcp);
726 	if (*line != '\0') {
727 		*line++ = '\0';
728 		line = skipwhite(line);
729 		if (ISDIGIT(*line) || *line == '-') {
730 			argp = line;
731 			line = parsetoken(line);
732 		}
733 	}
734 	if (argp != NULL) {
735 		f = FFARG;
736 		nl = strtol(argp, &tmp, 10);
737 		if (*tmp != '\0')
738 			return (FALSE);
739 		if (nl >= INT_MAX || nl <= INT_MIN)
740 			return (FALSE);
741 		n = (int)nl;
742 	}
743 	if ((fp = name_function(funcp)) == NULL) {
744 		dobeep();
745 		ewprintf("Unknown function: %s", funcp);
746 		return (FALSE);
747 	}
748 	if (fp == bindtokey || fp == unbindtokey) {
749 		bind = BINDARG;
750 		curmap = fundamental_map;
751 	} else if (fp == localbind || fp == localunbind) {
752 		bind = BINDARG;
753 		curmap = curbp->b_modes[curbp->b_nmodes]->p_map;
754 	} else if (fp == redefine_key)
755 		bind = BINDNEXT;
756 	else
757 		bind = BINDNO;
758 	/* Pack away all the args now... */
759 	if ((np = lalloc(0)) == FALSE)
760 		return (FALSE);
761 	np->l_fp = np->l_bp = maclcur = np;
762 	while (*line != '\0') {
763 		argp = skipwhite(line);
764 		if (*argp == '\0')
765 			break;
766 		line = parsetoken(argp);
767 		if (*argp != '"') {
768 			if (*argp == '\'')
769 				++argp;
770 			if ((lp = lalloc((int) (line - argp) + BINDEXT)) ==
771 			    NULL) {
772 				status = FALSE;
773 				goto cleanup;
774 			}
775 			bcopy(argp, ltext(lp), (int)(line - argp));
776 			/* don't count BINDEXT */
777 			lp->l_used--;
778 			if (bind == BINDARG)
779 				bind = BINDNO;
780 		} else {
781 			/* quoted strings are special */
782 			++argp;
783 			if (bind != BINDARG) {
784 				lp = lalloc((int)(line - argp) + BINDEXT);
785 				if (lp == NULL) {
786 					status = FALSE;
787 					goto cleanup;
788 				}
789 				lp->l_used = 0;
790 			} else
791 				key.k_count = 0;
792 			while (*argp != '"' && *argp != '\0') {
793 				if (*argp != '\\')
794 					c = *argp++;
795 				else {
796 					switch (*++argp) {
797 					case 't':
798 					case 'T':
799 						c = CCHR('I');
800 						break;
801 					case 'n':
802 					case 'N':
803 						c = CCHR('J');
804 						break;
805 					case 'r':
806 					case 'R':
807 						c = CCHR('M');
808 						break;
809 					case 'e':
810 					case 'E':
811 						c = CCHR('[');
812 						break;
813 					case '^':
814 						/*
815 						 * split into two statements
816 						 * due to bug in OSK cpp
817 						 */
818 						c = CHARMASK(*++argp);
819 						c = ISLOWER(c) ?
820 						    CCHR(TOUPPER(c)) : CCHR(c);
821 						break;
822 					case '0':
823 					case '1':
824 					case '2':
825 					case '3':
826 					case '4':
827 					case '5':
828 					case '6':
829 					case '7':
830 						c = *argp - '0';
831 						if (argp[1] <= '7' &&
832 						    argp[1] >= '0') {
833 							c <<= 3;
834 							c += *++argp - '0';
835 							if (argp[1] <= '7' &&
836 							    argp[1] >= '0') {
837 								c <<= 3;
838 								c += *++argp
839 								    - '0';
840 							}
841 						}
842 						break;
843 					case 'f':
844 					case 'F':
845 						c = *++argp - '0';
846 						if (ISDIGIT(argp[1])) {
847 							c *= 10;
848 							c += *++argp - '0';
849 						}
850 						c += KFIRST;
851 						break;
852 					default:
853 						c = CHARMASK(*argp);
854 						break;
855 					}
856 					argp++;
857 				}
858 				if (bind == BINDARG)
859 					key.k_chars[key.k_count++] = c;
860 				else
861 					lp->l_text[lp->l_used++] = c;
862 			}
863 			if (*line)
864 				line++;
865 		}
866 		switch (bind) {
867 		case BINDARG:
868 			bind = BINDDO;
869 			break;
870 		case BINDNEXT:
871 			lp->l_text[lp->l_used] = '\0';
872 			if ((curmap = name_map(lp->l_text)) == NULL) {
873 				dobeep();
874 				ewprintf("No such mode: %s", lp->l_text);
875 				status = FALSE;
876 				free(lp);
877 				goto cleanup;
878 			}
879 			free(lp);
880 			bind = BINDARG;
881 			break;
882 		default:
883 			lp->l_fp = np->l_fp;
884 			lp->l_bp = np;
885 			np->l_fp = lp;
886 			np = lp;
887 		}
888 	}
889 	switch (bind) {
890 	default:
891 		dobeep();
892 		ewprintf("Bad args to set key");
893 		status = FALSE;
894 		break;
895 	case BINDDO:
896 		if (fp != unbindtokey && fp != localunbind) {
897 			lp->l_text[lp->l_used] = '\0';
898 			status = bindkey(&curmap, lp->l_text, key.k_chars,
899 			    key.k_count);
900 		} else
901 			status = bindkey(&curmap, NULL, key.k_chars,
902 			    key.k_count);
903 		break;
904 	case BINDNO:
905 		inmacro = TRUE;
906 		maclcur = maclcur->l_fp;
907 		status = (*fp)(f, n);
908 		inmacro = FALSE;
909 	}
910 cleanup:
911 	lp = maclcur->l_fp;
912 	while (lp != maclcur) {
913 		np = lp->l_fp;
914 		free(lp);
915 		lp = np;
916 	}
917 	free(lp);
918 	maclhead = NULL;
919 	macrodef = FALSE;
920 	return (status);
921 }
922 
923 /*
924  * a pair of utility functions for the above
925  */
926 static char *
927 skipwhite(char *s)
928 {
929 	while (*s == ' ' || *s == '\t' || *s == ')' || *s == '(')
930 		s++;
931 	if ((*s == ';') || (*s == '#'))
932 		*s = '\0';
933 	return (s);
934 }
935 
936 static char *
937 parsetoken(char *s)
938 {
939 	if (*s != '"') {
940 		while (*s && *s != ' ' && *s != '\t' && *s != ')' && *s != '(')
941 			s++;
942 		if (*s == ';')
943 			*s = '\0';
944 	} else
945 		do {
946 			/*
947 			 * Strings get special treatment.
948 			 * Beware: You can \ out the end of the string!
949 			 */
950 			if (*s == '\\')
951 				++s;
952 		} while (*++s != '"' && *s != '\0');
953 	return (s);
954 }
955