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