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