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