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