xref: /openbsd/usr.bin/mg/interpreter.c (revision 479c151d)
1 /*      $OpenBSD: interpreter.c,v 1.36 2024/09/20 02:00:46 jsg Exp $	*/
2 /*
3  * This file is in the public domain.
4  *
5  * Author: Mark Lumsden <mark@showcomplex.com>
6  */
7 
8 /*
9  * This file attempts to add some 'scripting' functionality into mg.
10  *
11  * The initial goal is to give mg the ability to use its existing functions
12  * and structures in a linked-up way. Hopefully resulting in user definable
13  * functions. The syntax is 'scheme' like but currently it is not a scheme
14  * interpreter.
15  *
16  * At the moment there is no manual page reference to this file. The code below
17  * is liable to change, so use at your own risk!
18  *
19  * If you do want to do some testing, you can add some lines to your .mg file
20  * like:
21  *
22  * 1. Give multiple arguments to a function that usually would accept only one:
23  * (find-file "a.txt" "b.txt" "c.txt")
24  *
25  * 2. Define a single value variable:
26  * (define myfile "d.txt")
27  *
28  * 3. Define a list:
29  * (define myfiles(list "e.txt" "f.txt"))
30  *
31  * 4. Use the previously defined variable or list:
32  * (find-file myfiles)
33  *
34  * To do:
35  * 1. multiline parsing - currently only single lines supported.
36  * 2. parsing for '(' and ')' throughout whole string and evaluate correctly.
37  * 3. conditional execution.
38  * 4. have memory allocated dynamically for variable values.
39  * 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-]
40  *    at the moment.
41  * 6. display line numbers with parsing errors.
42  * 7. oh so many things....
43  * [...]
44  * n. implement user definable functions.
45  *
46  * Notes:
47  * - Currently calls to excline() from this file have the line length and
48  *   line number set to zero.
49  *   That's because excline() uses '\0' as the end of line indicator
50  *   and only the call to foundparen() within excline() uses excline's 2nd
51  *   and 3rd arguments.
52  *   Importantly, any lines sent to there from here will not be
53  *   coming back here.
54  */
55 #include <sys/queue.h>
56 
57 #include <ctype.h>
58 #include <limits.h>
59 #include <regex.h>
60 #include <signal.h>
61 #include <stdio.h>
62 #include <stdlib.h>
63 #include <string.h>
64 
65 #include "def.h"
66 #include "funmap.h"
67 
68 #ifdef  MGLOG
69 #include "kbd.h"
70 #include "log.h"
71 #endif
72 
73 static int	 multiarg(char *, char *, int);
74 static int	 isvar(char **, char **, int);
75 /*static int	 dofunc(char **, char **, int);*/
76 static int	 founddef(char *, int, int, int, int);
77 static int	 foundlst(char *, int, int, int);
78 static int	 expandvals(char *, char *, char *);
79 static int	 foundfun(char *, int);
80 static int	 doregex(char *, char *);
81 static void	 clearexp(void);
82 static int	 parse(char *, const char *, const char *, int, int, int, int);
83 static int	 parsdef(char *, const char *, const char *, int, int, int);
84 static int	 parsval(char *, const char *, const char *, int, int, int);
85 static int	 parsexp(char *, const char *, const char *, int, int, int);
86 
87 static int	 exitinterpreter(char *, char *, int);
88 
89 TAILQ_HEAD(exphead, expentry) ehead;
90 struct expentry {
91 	TAILQ_ENTRY(expentry) eentry;
92 	char	*fun;		/* The 1st string found between parens.   */
93 	char	 funbuf[BUFSIZE];
94 	const char	*par1;	/* Parenthesis at start of string	  */
95 	const char	*par2;	/* Parenthesis at end of string		  */
96 	int	 expctr;	/* An incremental counter:+1 for each exp */
97 	int	 blkid;		/* Which block are we in?		  */
98 };
99 
100 /*
101  * Structure for scheme keywords.
102  */
103 #define NUMSCHKEYS	4
104 #define MAXLENSCHKEYS	17	/* 17 = longest keyword (16)  + 1 */
105 
106 char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] =
107 	{
108 		"define",
109 	  	"list",
110 	  	"if",
111 	  	"lambda"
112 	};
113 
114 static const char 	 lp = '(';
115 static const char 	 rp = ')';
116 static char 		*defnam = NULL;
117 static int		 lnm;
118 
119 /*
120  * Line has a '(' as the first non-white char.
121  * Do some very basic parsing of line.
122  * Multi-line not supported at the moment, To do.
123  */
124 int
foundparen(char * funstr,int llen,int lnum)125 foundparen(char *funstr, int llen, int lnum)
126 {
127 	const char	*lrp = NULL;
128 	char		*p, *begp = NULL, *endp = NULL, *prechr;
129 	char		*lastchr = NULL;
130 	int     	 i, ret, pctr, expctr, blkid, inquote, esc;
131 	int		 elen, spc, ns;
132 
133 	pctr = expctr = inquote = esc = elen = spc = ns = 0;
134 	blkid = 1;
135 	lnm = lnum;
136 
137 	/*
138 	 * load expressions into a list called 'expentry', to be processd
139 	 * when all are obtained.
140 	 * Not really live code at the moment. Just part of the process of
141 	 * working out what needs to be done.
142 	 */
143 	TAILQ_INIT(&ehead);
144 
145 	/*
146 	 * Check for blocks of code with opening and closing ().
147 	 * One block = (cmd p a r a m)
148 	 * Two blocks = (cmd p a r a m s)(hola)
149 	 * Two blocks = (cmd p a r (list a m s))(hola)
150 	 * Only single line at moment, but more for multiline.
151 	 */
152 	p = funstr;
153 
154 	for (i = 0; i < llen; ++i, p++) {
155 		if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') {
156 			if (*p == ')')
157 				return(dobeep_num("Extra ')' found on line:",
158 				    lnm));
159 			return(dobeep_num("Error line:", lnm));
160 		}
161 		if (begp != NULL)
162 			elen++;
163 
164 		if (*p == '\\') {
165 			esc = 1;
166 		} else if (*p == '(') {
167 			if (lastchr != NULL && *lastchr == '(')
168 				return(dobeep_num("Multiple consecutive "\
169 				    "left parantheses line", lnm));
170 			if (inquote == 0) {
171 				if (begp != NULL) {
172 					if (*prechr == ' ')
173 						ns--;
174 					if (endp == NULL)
175 						*p = '\0';
176 					else
177 						*endp = '\0';
178 
179 					ret = parse(begp, lrp, &lp, blkid,
180 					    ++expctr, elen - spc, ns);
181 					if (!ret) {
182 						cleanup();
183 						return(ret);
184 					}
185 					elen = 0;
186 				}
187 				lrp = &lp;
188 				begp = endp = NULL;
189 				pctr++;
190 			} else if (inquote != 1) {
191 				cleanup();
192 				return(dobeep_num("Opening and closing quote "\
193 				    "char error line:", lnm));
194 			}
195 			esc = spc = 0;
196 		} else if (*p == ')') {
197 			if (lastchr != NULL && *lastchr == '(')
198 				return(dobeep_num("Empty parenthesis "\
199 				    "not supported line", lnm));
200 			if (inquote == 0) {
201 				if (begp != NULL) {
202 					if (*prechr == ' ')
203 						ns--;
204 					if (endp == NULL)
205 						*p = '\0';
206 					else
207 						*endp = '\0';
208 
209 					ret = parse(begp, lrp, &rp, blkid,
210 					    ++expctr, elen - spc, ns);
211 					if (!ret) {
212 						cleanup();
213 						return(ret);
214 					}
215 					elen = 0;
216 				}
217 				lrp = &rp;
218 				begp = endp = NULL;
219 				pctr--;
220 			} else if (inquote != 1) {
221 				cleanup();
222 				return(dobeep_num("Opening and closing quote "\
223 				    "char error line:", lnm));
224 			}
225 			esc = spc = 0;
226 		} else if (*p != ' ' && *p != '\t') {
227 			if (begp == NULL) {
228 				begp = p;
229 				if (*begp == '"' || isdigit(*begp))
230 					return(dobeep_num("First char of "\
231 					    "expression error line:", lnm));
232 			}
233 			if (*p == '"') {
234 				if (inquote == 0 && esc == 0) {
235 					if (*prechr != ' ' && *prechr != '\t')
236 						return(dobeep_num("Parse error"\
237 						    " line:", lnm));
238 					inquote++;
239 				} else if (inquote > 0 && esc == 1)
240 					esc = 0;
241 				else
242 					inquote--;
243 			} else if (*prechr == '"' && inquote == 0) {
244 				return(dobeep_num("Parse error line:", lnm));
245 			}
246 			endp = NULL;
247 			spc = 0;
248 		} else if (endp == NULL && (*p == ' ' || *p == '\t')) {
249 			if (inquote == 0) {
250 				*p = ' ';
251 				endp = p;
252 				spc++;
253 				if (begp != NULL)
254 					ns++;
255 			}
256 			esc = 0;
257 		} else if (*p == '\t' || *p == ' ') {
258 			if (inquote == 0) {
259 				*p = ' ';
260 				spc++;
261 			}
262 			esc = 0;
263 		}
264 		if (*p != '\t' && *p != ' ' && inquote == 0)
265 			lastchr = p;
266 
267 		if (pctr == 0) {
268 			blkid++;
269 			expctr = 0;
270 			defnam = NULL;
271 		}
272 		prechr = p;
273 	}
274 
275 	if (pctr != 0) {
276 		cleanup();
277 		return(dobeep_num("Opening and closing parentheses error line:",
278 		    lnm));
279 	}
280 	if (ret == FALSE)
281 		cleanup();
282 	else
283 		clearexp();	/* leave lists but remove expressions */
284 
285 	return (ret);
286 }
287 
288 
289 static int
parse(char * begp,const char * par1,const char * par2,int blkid,int expctr,int elen,int ns)290 parse(char *begp, const char *par1, const char *par2, int blkid, int expctr,
291     int elen, int ns)
292 {
293 	char    *regs;
294 	int 	 ret = FALSE;
295 
296 	if (strncmp(begp, "define", 6) == 0) {
297 		ret = parsdef(begp, par1, par2, blkid, expctr, elen);
298 		if (ret == TRUE || ret == FALSE)
299 			return (ret);
300 	} else if (strncmp(begp, "list", 4) == 0)
301 		return(parsval(begp, par1, par2, blkid, expctr, elen));
302 
303 	regs = "^exit$";
304 	if (doregex(regs, begp))
305 		return(exitinterpreter(NULL, NULL, FALSE));
306 
307 	/* mg function name regex */
308 	regs = "^[A-Za-z-]+$";
309         if (doregex(regs, begp))
310 		return(excline(begp, 0, 0));
311 
312 	/* Corner case 1 */
313 	if (strncmp(begp, "global-set-key ", 15) == 0)
314 		/* function name as 2nd param screws up multiarg. */
315 		return(excline(begp, 0, 0));
316 
317 	/* Corner case 2 */
318 	if (strncmp(begp, "define-key ", 11) == 0)
319 		/* function name as 3rd param screws up multiarg. */
320 		return(excline(begp, 0, 0));
321 
322 	return (parsexp(begp, par1, par2, blkid, expctr, elen));
323 }
324 
325 static int
parsdef(char * begp,const char * par1,const char * par2,int blkid,int expctr,int elen)326 parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr,
327     int elen)
328 {
329 	char    *regs;
330 
331 	if ((defnam == NULL) && (expctr != 1))
332 		return(dobeep_num("'define' incorrectly used line:", lnm));
333 
334         /* Does the line have a incorrect variable 'define' like: */
335         /* (define i y z) */
336         regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$";
337         if (doregex(regs, begp))
338                 return(dobeep_num("Invalid use of define line:", lnm));
339 
340         /* Does the line have a single variable 'define' like: */
341         /* (define i 0) */
342         regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$";
343         if (doregex(regs, begp)) {
344 		if (par1 == &lp && par2 == &rp && expctr == 1)
345 			return(founddef(begp, blkid, expctr, 1, elen));
346 		return(dobeep_num("Invalid use of define line:", lnm));
347 	}
348 	/* Does the line have  '(define i(' */
349         regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$";
350         if (doregex(regs, begp)) {
351 		if (par1 == &lp && par2 == &lp && expctr == 1)
352                 	return(founddef(begp, blkid, expctr, 0, elen));
353 		return(dobeep_num("Invalid use of 'define' line:", lnm));
354 	}
355 	/* Does the line have  '(define (' */
356 	regs = "^define$";
357 	if (doregex(regs, begp)) {
358 		if (par1 == &lp && par2 == &lp && expctr == 1)
359 			return(foundfun(begp, expctr));
360 		return(dobeep_num("Invalid use of 'define' line:", lnm));
361 	}
362 
363 	return (ABORT);
364 }
365 
366 static int
parsval(char * begp,const char * par1,const char * par2,int blkid,int expctr,int elen)367 parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr,
368     int elen)
369 {
370 	char    *regs;
371 
372 	/* Does the line have 'list' */
373 	regs = "^list$";
374 	if (doregex(regs, begp))
375 		return(dobeep_num("Invalid use of list line:", lnm));
376 
377         /* Does the line have a 'list' like: */
378         /* (list "a" "b") */
379         regs = "^list[ ]+.*$";
380         if (doregex(regs, begp)) {
381 		if (expctr == 1)
382 			return(dobeep_num("list with no-where to go.", lnm));
383 
384 		if (par1 == &lp && expctr > 1)
385 			return(foundlst(begp, blkid, expctr, elen));
386 
387 		return(dobeep_num("Invalid use of list line:", lnm));
388 	}
389 	return (FALSE);
390 }
391 
392 static int
parsexp(char * begp,const char * par1,const char * par2,int blkid,int expctr,int elen)393 parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr,
394     int elen)
395 {
396 	struct expentry *e1 = NULL;
397 	PF		 funcp;
398 	char		*cmdp, *fendp, *valp, *fname, *funb = NULL;
399 	int		 numparams, ret;
400 
401 	cmdp = begp;
402 	fendp = strchr(cmdp, ' ');
403 	*fendp = '\0';
404 
405 	/*
406 	 * If no extant mg command found, just return.
407 	 */
408 	if ((funcp = name_function(cmdp)) == NULL)
409 		return (dobeep_msgs("Unknown command:", cmdp));
410 
411 	numparams = numparams_function(funcp);
412 	if (numparams == 0)
413 		return (dobeep_msgs("Command takes no arguments:", cmdp));
414 
415 	if (numparams == -1)
416 		return (dobeep_msgs("Interactive command found:", cmdp));
417 
418 	if ((e1 = malloc(sizeof(struct expentry))) == NULL) {
419 		cleanup();
420 		return (dobeep_msg("malloc Error"));
421 	}
422 	TAILQ_INSERT_HEAD(&ehead, e1, eentry);
423 	if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) {
424 		cleanup();
425 		return(dobeep_msg("strndup error"));
426 	}
427 	cmdp = e1->fun;
428 	fname = e1->fun;
429 	e1->funbuf[0] = '\0';
430 	funb = e1->funbuf;
431 	e1->expctr = expctr;
432 	e1->blkid = blkid;
433 	/* need to think about these two */
434 	e1->par1 = par1;
435 	e1->par2 = par2;
436 
437 	*fendp = ' ';
438 	valp = fendp + 1;
439 
440 	ret = expandvals(cmdp, valp, funb);
441 	if (!ret)
442 		return (ret);
443 
444 	return (multiarg(fname, funb, numparams));
445 }
446 
447 /*
448  * Pass a list of arguments to a function.
449  */
450 static int
multiarg(char * cmdp,char * argbuf,int numparams)451 multiarg(char *cmdp, char *argbuf, int numparams)
452 {
453 	char	 excbuf[BUFSIZE];
454 	char	*argp, *p, *s = " ";
455 	char	*regs;
456 	int	 spc, numspc;
457 	int	 fin, inquote;
458 
459 	argp = argbuf;
460 	spc = 1; /* initially fake a space so we find first argument */
461 	numspc = fin = inquote = 0;
462 
463 	for (p = argbuf; *p != '\0'; p++) {
464 		if (*(p + 1) == '\0')
465 			fin = 1;
466 
467 		if (*p != ' ') {
468 			if (*p == '"') {
469 				if (inquote == 1)
470 					inquote = 0;
471 				else
472 					inquote = 1;
473 			}
474 			if (spc == 1)
475 				if ((numspc % numparams) == 0) {
476 					argp = p;
477 				}
478 			spc = 0;
479 		}
480 		if ((*p == ' ' && inquote == 0) || fin) {
481 			if (spc == 1)/* || (numspc % numparams == 0))*/
482 				continue;
483 			if ((numspc % numparams) != (numparams - 1)) {
484 				numspc++;
485 				continue;
486 			}
487 			if (*p == ' ') {
488 				*p = '\0';		/* terminate arg string */
489 			}
490 			excbuf[0] = '\0';
491 			regs = "[\"]+.*[\"]+";
492 
493        			if (!doregex(regs, argp)) {
494 				const char *errstr;
495 
496 				strtonum(argp, 0, INT_MAX, &errstr);
497 				if (errstr != NULL)
498 					return (dobeep_msgs("Var not found:",
499 					    argp));
500 			}
501 
502 			if (strlcpy(excbuf, cmdp, sizeof(excbuf))
503 			    >= sizeof(excbuf))
504 				return (dobeep_msg("strlcpy error"));
505 			if (strlcat(excbuf, s, sizeof(excbuf))
506 			    >= sizeof(excbuf))
507 				return (dobeep_msg("strlcat error"));
508 			if (strlcat(excbuf, argp, sizeof(excbuf))
509 			    >= sizeof(excbuf))
510 				return (dobeep_msg("strlcat error"));
511 
512 			excline(excbuf, 0, 0);
513 
514 			if (fin)
515 				break;
516 
517 			*p = ' ';		/* unterminate arg string */
518 			numspc++;
519 			spc = 1;
520 		}
521 	}
522 	return (TRUE);
523 }
524 
525 /*
526  * Is an item a value or a variable?
527  */
528 static int
isvar(char ** argp,char ** varbuf,int sizof)529 isvar(char **argp, char **varbuf, int sizof)
530 {
531 	struct varentry *v1 = NULL;
532 
533 	if (SLIST_EMPTY(&varhead))
534 		return (FALSE);
535 #ifdef  MGLOG
536 	mglog_isvar(*varbuf, *argp, sizof);
537 #endif
538 	SLIST_FOREACH(v1, &varhead, entry) {
539 		if (strcmp(*argp, v1->v_name) == 0) {
540 			(void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof);
541 			return (TRUE);
542 		}
543 	}
544 	return (FALSE);
545 }
546 
547 
548 static int
foundfun(char * defstr,int expctr)549 foundfun(char *defstr, int expctr)
550 {
551 	return (TRUE);
552 }
553 
554 static int
foundlst(char * defstr,int blkid,int expctr,int elen)555 foundlst(char *defstr, int blkid, int expctr, int elen)
556 {
557 	char		*p;
558 
559 	p = strstr(defstr, " ");
560 	p = skipwhite(p);
561 	expandvals(NULL, p, defnam);
562 
563 	return (TRUE);
564 }
565 
566 /*
567  * 'define' strings follow the regex in parsdef().
568  */
569 static int
founddef(char * defstr,int blkid,int expctr,int hasval,int elen)570 founddef(char *defstr, int blkid, int expctr, int hasval, int elen)
571 {
572 	struct varentry *vt, *v1 = NULL;
573 	char		*p, *vnamep, *vendp = NULL, *valp;
574 
575 	p = strstr(defstr, " ");        /* move to first ' ' char.    */
576 	vnamep = skipwhite(p);		/* find first char of var name. */
577 	vendp = vnamep;
578 
579 	/* now find the end of the define/list name */
580 	while (1) {
581 		++vendp;
582 		if (*vendp == ' ')
583 			break;
584 	}
585 	*vendp = '\0';
586 
587 	/*
588 	 * Check list name is not an existing mg function.
589 	 */
590 	if (name_function(vnamep) != NULL)
591 		return(dobeep_msgs("Variable/function name clash:", vnamep));
592 
593 	if (!SLIST_EMPTY(&varhead)) {
594 		SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
595 			if (strcmp(vnamep, v1->v_name) == 0)
596 				SLIST_REMOVE(&varhead, v1, varentry, entry);
597 		}
598 	}
599 	if ((v1 = malloc(sizeof(struct varentry))) == NULL)
600 		return (ABORT);
601 	SLIST_INSERT_HEAD(&varhead, v1, entry);
602 	if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL)
603 		return(dobeep_msg("strndup error"));
604 	vnamep = v1->v_name;
605 	v1->v_count = 0;
606 	v1->v_vals = NULL;
607 	v1->v_buf[0] = '\0';
608 
609 	defnam = v1->v_buf;
610 
611 	if (hasval) {
612 		valp = skipwhite(vendp + 1);
613 
614 		expandvals(NULL, valp, defnam);
615 		defnam = NULL;
616 	}
617 	*vendp = ' ';
618 	return (TRUE);
619 }
620 
621 
622 static int
expandvals(char * cmdp,char * valp,char * bp)623 expandvals(char *cmdp, char *valp, char *bp)
624 {
625 	char	 excbuf[BUFSIZE], argbuf[BUFSIZE];
626 	char	 contbuf[BUFSIZE], varbuf[BUFSIZE];
627 	char	*argp, *endp, *p, *v, *s = " ";
628 	char	*regs;
629 	int	 spc, cnt;
630 	int	 sizof, fin, inquote;
631 
632 	/* now find the first argument */
633 	p = skipwhite(valp);
634 
635 	if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
636 		return (dobeep_msg("strlcpy error"));
637 	argp = argbuf;
638 	spc = 1; /* initially fake a space so we find first argument */
639 	fin = inquote = cnt = spc = 0;
640 
641 	for (p = argbuf; *p != '\0'; p++) {
642 		if (*(p + 1) == '\0')
643 			fin = 1;
644 
645 		if (*p != ' ') {
646 			if (*p == '"') {
647 				if (inquote == 1)
648 					inquote = 0;
649 				else
650 					inquote = 1;
651 			}
652 			if (spc == 1)
653 				argp = p;
654 			spc = 0;
655 		}
656 		if ((*p == ' ' && inquote == 0) || fin) {
657 			if (spc == 1)
658 				continue;
659 			/* terminate arg string */
660 			if (*p == ' ') {
661 				*p = '\0';
662 			}
663 			endp = p + 1;
664 			excbuf[0] = '\0';
665 			varbuf[0] = '\0';
666 			contbuf[0] = '\0';
667 			sizof = sizeof(varbuf);
668 			v = varbuf;
669 			regs = "[\"]+.*[\"]+";
670        			if (doregex(regs, argp))
671 				;			/* found quotes */
672 			else if (isvar(&argp, &v, sizof)) {
673 
674 				(void)(strlcat(varbuf, " ",
675                                     sizof) >= sizof);
676 
677 				*p = ' ';
678 				(void)(strlcpy(contbuf, endp,
679 				    sizeof(contbuf)) >= sizeof(contbuf));
680 
681 				(void)(strlcat(varbuf, contbuf,
682 				    sizof) >= sizof);
683 
684 				argbuf[0] = ' ';
685 				argbuf[1] = '\0';
686 				(void)(strlcat(argbuf, varbuf,
687 				    sizof) >= sizof);
688 
689 				p = argp = argbuf;
690 				spc = 1;
691 				fin = 0;
692 				continue;
693 			} else {
694 				const char *errstr;
695 
696 				strtonum(argp, 0, INT_MAX, &errstr);
697 				if (errstr != NULL)
698 					return (dobeep_msgs("Var not found:",
699 					    argp));
700 			}
701 #ifdef  MGLOG
702         mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE);
703 #endif
704 			if (*bp != '\0') {
705 				if (strlcat(bp, s, BUFSIZE) >= BUFSIZE)
706 					return (dobeep_msg("strlcat error"));
707 			}
708 			if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) {
709 				return (dobeep_msg("strlcat error"));
710 			}
711 /*			v1->v_count++;*/
712 
713 			if (fin)
714 				break;
715 
716 			*p = ' ';		/* unterminate arg string */
717 			spc = 1;
718 		}
719 	}
720 	return (TRUE);
721 }
722 
723 /*
724  * Finished with buffer evaluation, so clean up any vars.
725  * Perhaps keeps them in mg even after use,...
726  */
727 /*static int
728 clearvars(void)
729 {
730 	struct varentry	*v1 = NULL;
731 
732 	while (!SLIST_EMPTY(&varhead)) {
733 		v1 = SLIST_FIRST(&varhead);
734 		SLIST_REMOVE_HEAD(&varhead, entry);
735 		free(v1->v_name);
736 		free(v1);
737 	}
738 	return (FALSE);
739 }
740 */
741 /*
742  * Finished with block evaluation, so clean up any expressions.
743  */
744 static void
clearexp(void)745 clearexp(void)
746 {
747 	struct expentry	*e1 = NULL;
748 
749 	while (!TAILQ_EMPTY(&ehead)) {
750 		e1 = TAILQ_FIRST(&ehead);
751 		TAILQ_REMOVE(&ehead, e1, eentry);
752 		free(e1->fun);
753 		free(e1);
754 	}
755 	return;
756 }
757 
758 /*
759  * Cleanup before leaving.
760  */
761 void
cleanup(void)762 cleanup(void)
763 {
764 	defnam = NULL;
765 
766 	clearexp();
767 /*	clearvars();*/
768 }
769 
770 /*
771  * Test a string against a regular expression.
772  */
773 static int
doregex(char * r,char * e)774 doregex(char *r, char *e)
775 {
776 	regex_t  regex_buff;
777 
778 	if (regcomp(&regex_buff, r, REG_EXTENDED)) {
779 		regfree(&regex_buff);
780 		return(dobeep_num("Regex compilation error line:", lnm));
781 	}
782 	if (!regexec(&regex_buff, e, 0, NULL, 0)) {
783 		regfree(&regex_buff);
784 		return(TRUE);
785 	}
786 	regfree(&regex_buff);
787 	return(FALSE);
788 }
789 
790 /*
791  * Display a message so it is apparent that this is the method which stopped
792  * execution.
793  */
794 static int
exitinterpreter(char * ptr,char * dobuf,int dosiz)795 exitinterpreter(char *ptr, char *dobuf, int dosiz)
796 {
797 	cleanup();
798 	if (batch == 0)
799 		return(dobeep_msg("Interpreter exited via exit command."));
800 	return(FALSE);
801 }
802 
803 /*
804  * All code below commented out (until end of file).
805  *
806  * Need to think about how interpreter functions are done.
807  * Probably don't have a choice with string-append().
808 
809 static int 	 getenvironmentvariable(char *, char *, int);
810 static int	 stringappend(char *, char *, int);
811 
812 typedef int	 (*PFI)(char *, char *, int);
813 
814 
815 struct ifunmap {
816 	PFI		 fn_funct;
817 	const char 	*fn_name;
818 	struct ifunmap	*fn_next;
819 };
820 static struct ifunmap *ifuns;
821 
822 static struct ifunmap ifunctnames[] = {
823 	{exitinterpreter, "exit"},
824 	{getenvironmentvariable, "get-environment-variable"},
825 	{stringappend, "string-append"},
826 	{NULL, NULL}
827 };
828 
829 void
830 ifunmap_init(void)
831 {
832 	struct ifunmap *fn;
833 
834 	for (fn = ifunctnames; fn->fn_name != NULL; fn++) {
835 		fn->fn_next = ifuns;
836 		ifuns = fn;
837 	}
838 }
839 
840 PFI
841 name_ifun(const char *ifname)
842 {
843 	struct ifunmap 	*fn;
844 
845 	for (fn = ifuns; fn != NULL; fn = fn->fn_next) {
846 		if (strcmp(fn->fn_name, ifname) == 0)
847 			return (fn->fn_funct);
848 	}
849 
850 	return (NULL);
851 }
852 
853 
854 int
855 dofunc(char **ifname, char **tmpbuf, int sizof)
856 {
857 	PFI 	 fnc;
858 	char	*p, *tmp;
859 
860 	p = strstr(*ifname, " ");
861 	*p = '\0';
862 
863 	fnc = name_ifun(*ifname);
864 	if (fnc == NULL)
865 		return (FALSE);
866 
867 	*p = ' ';
868 
869 	tmp = *tmpbuf;
870 
871 	fnc(p, tmp, sizof);
872 
873 	return (TRUE);
874 }
875 
876 static int
877 getenvironmentvariable(char *ptr, char *dobuf, int dosiz)
878 {
879 	char		*t;
880 	char		*tmp;
881 	const char	*q = "\"";
882 
883 	t = skipwhite(ptr);
884 
885 	if (t[0] == *q || t[strlen(t) - 1] == *q)
886 		return (dobeep_msgs("Please remove '\"' around:", t));
887 	if ((tmp = getenv(t)) == NULL || *tmp == '\0')
888 		return(dobeep_msgs("Envar not found:", t));
889 
890 	dobuf[0] = '\0';
891 	if (strlcat(dobuf, q, dosiz) >= dosiz)
892 		return (dobeep_msg("strlcat error"));
893 	if (strlcat(dobuf, tmp, dosiz) >= dosiz)
894 		return (dobeep_msg("strlcat error"));
895 	if (strlcat(dobuf, q, dosiz) >= dosiz)
896 		return (dobeep_msg("strlcat error"));
897 
898 	return (TRUE);
899 }
900 
901 static int
902 stringappend(char *ptr, char *dobuf, int dosiz)
903 {
904 	char		 varbuf[BUFSIZE], funbuf[BUFSIZE];
905 	char            *p, *f, *v, *vendp;
906 	int		 sizof, fin = 0;
907 
908 	varbuf[0] = funbuf[0] = '\0';
909 	f = funbuf;
910 	v = varbuf;
911 	sizof = sizeof(varbuf);
912 	*dobuf = '\0';
913 
914 	p = skipwhite(ptr);
915 
916 	while (*p != '\0') {
917 		vendp = p;
918 		while (1) {
919 			if (*vendp == ' ') {
920 				break;
921 			} else if (*vendp == '\0') {
922 				fin = 1;
923 				break;
924 			}
925 			++vendp;
926 		}
927         	*vendp = '\0';
928 
929 		if (isvar(&p, &v, sizof)) {
930 			if (v[0] == '"' && v[strlen(v) - 1] == '"' ) {
931 				v[strlen(v) - 1] = '\0';
932 				v = v + 1;
933 			}
934 			if (strlcat(f, v, sizof) >= sizof)
935 				return (dobeep_msg("strlcat error"));
936 		} else {
937 			if (p[0] == '"' && p[strlen(p) - 1] == '"' ) {
938 				p[strlen(p) - 1] = '\0';
939 				p = p + 1;
940 			}
941 			if (strlcat(f, p, sizof) >= sizof)
942 				return (dobeep_msg("strlcat error"));
943 		}
944 		if (fin)
945 			break;
946 		vendp++;
947 		if (*vendp == '\0')
948 			break;
949 		p = skipwhite(vendp);
950 	}
951 
952 	(void)snprintf(dobuf, dosiz, "\"%s\"", f);
953 
954 	return (TRUE);
955 }
956 
957 Index: main.c
958 ===================================================================
959 RCS file: /cvs/src/usr.bin/mg/main.c,v
960 retrieving revision 1.89
961 diff -u -p -u -p -r1.89 main.c
962 --- main.c      20 Mar 2021 09:00:49 -0000      1.89
963 +++ main.c      12 Apr 2021 17:58:52 -0000
964 @@ -133,10 +133,12 @@ main(int argc, char **argv)
965                 extern void grep_init(void);
966                 extern void cmode_init(void);
967                 extern void dired_init(void);
968 +               extern void ifunmap_init(void);
969 
970                 dired_init();
971                 grep_init();
972                 cmode_init();
973 +               ifunmap_init();
974         }
975 
976 
977 */
978