xref: /openbsd-src/usr.bin/mg/interpreter.c (revision 479c151d3429b7cfa6228ee428d945620629789d)
1*479c151dSjsg /*      $OpenBSD: interpreter.c,v 1.36 2024/09/20 02:00:46 jsg Exp $	*/
23c2c8acbSlum /*
33c2c8acbSlum  * This file is in the public domain.
43c2c8acbSlum  *
53c2c8acbSlum  * Author: Mark Lumsden <mark@showcomplex.com>
63c2c8acbSlum  */
73c2c8acbSlum 
83c2c8acbSlum /*
93c2c8acbSlum  * This file attempts to add some 'scripting' functionality into mg.
103c2c8acbSlum  *
117b1feaf2Sguenther  * The initial goal is to give mg the ability to use its existing functions
123c2c8acbSlum  * and structures in a linked-up way. Hopefully resulting in user definable
133c2c8acbSlum  * functions. The syntax is 'scheme' like but currently it is not a scheme
143c2c8acbSlum  * interpreter.
153c2c8acbSlum  *
163c2c8acbSlum  * At the moment there is no manual page reference to this file. The code below
173c2c8acbSlum  * is liable to change, so use at your own risk!
183c2c8acbSlum  *
193c2c8acbSlum  * If you do want to do some testing, you can add some lines to your .mg file
203c2c8acbSlum  * like:
213c2c8acbSlum  *
223c2c8acbSlum  * 1. Give multiple arguments to a function that usually would accept only one:
23036c776aSlum  * (find-file "a.txt" "b.txt" "c.txt")
243c2c8acbSlum  *
25fefadbf1Slum  * 2. Define a single value variable:
26036c776aSlum  * (define myfile "d.txt")
273c2c8acbSlum  *
28fefadbf1Slum  * 3. Define a list:
29036c776aSlum  * (define myfiles(list "e.txt" "f.txt"))
30fefadbf1Slum  *
31fefadbf1Slum  * 4. Use the previously defined variable or list:
323c2c8acbSlum  * (find-file myfiles)
333c2c8acbSlum  *
343c2c8acbSlum  * To do:
353c2c8acbSlum  * 1. multiline parsing - currently only single lines supported.
363c2c8acbSlum  * 2. parsing for '(' and ')' throughout whole string and evaluate correctly.
373c2c8acbSlum  * 3. conditional execution.
387ec1563aSlum  * 4. have memory allocated dynamically for variable values.
397b3a2786Slum  * 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-]
407b3a2786Slum  *    at the moment.
41e5166e5aSlum  * 6. display line numbers with parsing errors.
42e5166e5aSlum  * 7. oh so many things....
433c2c8acbSlum  * [...]
443c2c8acbSlum  * n. implement user definable functions.
45036c776aSlum  *
4693960a54Slum  * Notes:
4781b9a829Slum  * - Currently calls to excline() from this file have the line length and
4881b9a829Slum  *   line number set to zero.
4981b9a829Slum  *   That's because excline() uses '\0' as the end of line indicator
5093960a54Slum  *   and only the call to foundparen() within excline() uses excline's 2nd
5181b9a829Slum  *   and 3rd arguments.
5281b9a829Slum  *   Importantly, any lines sent to there from here will not be
5393960a54Slum  *   coming back here.
543c2c8acbSlum  */
553c2c8acbSlum #include <sys/queue.h>
568fa75880Slum 
5742033fc5Slum #include <ctype.h>
588fa75880Slum #include <limits.h>
593c2c8acbSlum #include <regex.h>
603c2c8acbSlum #include <signal.h>
613c2c8acbSlum #include <stdio.h>
623c2c8acbSlum #include <stdlib.h>
633c2c8acbSlum #include <string.h>
643c2c8acbSlum 
653c2c8acbSlum #include "def.h"
663c2c8acbSlum #include "funmap.h"
673c2c8acbSlum 
687bf68b76Slum #ifdef  MGLOG
697bf68b76Slum #include "kbd.h"
707bf68b76Slum #include "log.h"
717bf68b76Slum #endif
727bf68b76Slum 
7317c1e8f1Slum static int	 multiarg(char *, char *, int);
743c2c8acbSlum static int	 isvar(char **, char **, int);
7517c1e8f1Slum /*static int	 dofunc(char **, char **, int);*/
765e37f6aeSlum static int	 founddef(char *, int, int, int, int);
775e37f6aeSlum static int	 foundlst(char *, int, int, int);
7817c1e8f1Slum static int	 expandvals(char *, char *, char *);
7917c1e8f1Slum static int	 foundfun(char *, int);
80228b1246Slum static int	 doregex(char *, char *);
81bbed9a29Slum static void	 clearexp(void);
8241167f9aSlum static int	 parse(char *, const char *, const char *, int, int, int, int);
835e37f6aeSlum static int	 parsdef(char *, const char *, const char *, int, int, int);
845e37f6aeSlum static int	 parsval(char *, const char *, const char *, int, int, int);
855e37f6aeSlum static int	 parsexp(char *, const char *, const char *, int, int, int);
8617c1e8f1Slum 
8717c1e8f1Slum static int	 exitinterpreter(char *, char *, int);
88bbed9a29Slum 
894937e3a7Slum TAILQ_HEAD(exphead, expentry) ehead;
90bbed9a29Slum struct expentry {
914937e3a7Slum 	TAILQ_ENTRY(expentry) eentry;
9217c1e8f1Slum 	char	*fun;		/* The 1st string found between parens.   */
9317c1e8f1Slum 	char	 funbuf[BUFSIZE];
9417c1e8f1Slum 	const char	*par1;	/* Parenthesis at start of string	  */
9517c1e8f1Slum 	const char	*par2;	/* Parenthesis at end of string		  */
96bbed9a29Slum 	int	 expctr;	/* An incremental counter:+1 for each exp */
97bbed9a29Slum 	int	 blkid;		/* Which block are we in?		  */
98bbed9a29Slum };
993c2c8acbSlum 
1003c2c8acbSlum /*
1014937e3a7Slum  * Structure for scheme keywords.
1024937e3a7Slum  */
1034937e3a7Slum #define NUMSCHKEYS	4
1044937e3a7Slum #define MAXLENSCHKEYS	17	/* 17 = longest keyword (16)  + 1 */
1054937e3a7Slum 
1064937e3a7Slum char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] =
1074937e3a7Slum 	{
1084937e3a7Slum 		"define",
1094937e3a7Slum 	  	"list",
1104937e3a7Slum 	  	"if",
1114937e3a7Slum 	  	"lambda"
1124937e3a7Slum 	};
1134937e3a7Slum 
11481b9a829Slum static const char 	 lp = '(';
11581b9a829Slum static const char 	 rp = ')';
11681b9a829Slum static char 		*defnam = NULL;
11781b9a829Slum static int		 lnm;
1184937e3a7Slum 
1194937e3a7Slum /*
120bbed9a29Slum  * Line has a '(' as the first non-white char.
121bbed9a29Slum  * Do some very basic parsing of line.
122bbed9a29Slum  * Multi-line not supported at the moment, To do.
123bbed9a29Slum  */
124bbed9a29Slum int
12581b9a829Slum foundparen(char *funstr, int llen, int lnum)
126bbed9a29Slum {
12717c1e8f1Slum 	const char	*lrp = NULL;
12855a1b3e1Slum 	char		*p, *begp = NULL, *endp = NULL, *prechr;
12955a1b3e1Slum 	char		*lastchr = NULL;
1307ec1563aSlum 	int     	 i, ret, pctr, expctr, blkid, inquote, esc;
13141167f9aSlum 	int		 elen, spc, ns;
132bbed9a29Slum 
13341167f9aSlum 	pctr = expctr = inquote = esc = elen = spc = ns = 0;
134bbed9a29Slum 	blkid = 1;
13581b9a829Slum 	lnm = lnum;
136bbed9a29Slum 
137bbed9a29Slum 	/*
138bbed9a29Slum 	 * load expressions into a list called 'expentry', to be processd
139bbed9a29Slum 	 * when all are obtained.
140bbed9a29Slum 	 * Not really live code at the moment. Just part of the process of
141bbed9a29Slum 	 * working out what needs to be done.
142bbed9a29Slum 	 */
1434937e3a7Slum 	TAILQ_INIT(&ehead);
1444937e3a7Slum 
14584dad9ebSlum 	/*
14684dad9ebSlum 	 * Check for blocks of code with opening and closing ().
14784dad9ebSlum 	 * One block = (cmd p a r a m)
14884dad9ebSlum 	 * Two blocks = (cmd p a r a m s)(hola)
14984dad9ebSlum 	 * Two blocks = (cmd p a r (list a m s))(hola)
15084dad9ebSlum 	 * Only single line at moment, but more for multiline.
15184dad9ebSlum 	 */
15284dad9ebSlum 	p = funstr;
15384dad9ebSlum 
154bdb72052Slum 	for (i = 0; i < llen; ++i, p++) {
155818faf02Slum 		if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') {
156818faf02Slum 			if (*p == ')')
15781b9a829Slum 				return(dobeep_num("Extra ')' found on line:",
15881b9a829Slum 				    lnm));
15981b9a829Slum 			return(dobeep_num("Error line:", lnm));
160818faf02Slum 		}
1615e37f6aeSlum 		if (begp != NULL)
1625e37f6aeSlum 			elen++;
1635e37f6aeSlum 
1647ec1563aSlum 		if (*p == '\\') {
1657ec1563aSlum 			esc = 1;
1667ec1563aSlum 		} else if (*p == '(') {
16755a1b3e1Slum 			if (lastchr != NULL && *lastchr == '(')
16855a1b3e1Slum 				return(dobeep_num("Multiple consecutive "\
16955a1b3e1Slum 				    "left parantheses line", lnm));
17005d4bb1aSlum 			if (inquote == 0) {
171bdb72052Slum 				if (begp != NULL) {
17241167f9aSlum 					if (*prechr == ' ')
17341167f9aSlum 						ns--;
174bbed9a29Slum 					if (endp == NULL)
175bbed9a29Slum 						*p = '\0';
176bbed9a29Slum 					else
177bbed9a29Slum 						*endp = '\0';
17884dad9ebSlum 
17905d4bb1aSlum 					ret = parse(begp, lrp, &lp, blkid,
18041167f9aSlum 					    ++expctr, elen - spc, ns);
18184dad9ebSlum 					if (!ret) {
182036c776aSlum 						cleanup();
18384dad9ebSlum 						return(ret);
184bbed9a29Slum 					}
1855e37f6aeSlum 					elen = 0;
186036c776aSlum 				}
18717c1e8f1Slum 				lrp = &lp;
188bdb72052Slum 				begp = endp = NULL;
189bbed9a29Slum 				pctr++;
19005d4bb1aSlum 			} else if (inquote != 1) {
191036c776aSlum 				cleanup();
19281b9a829Slum 				return(dobeep_num("Opening and closing quote "\
19381b9a829Slum 				    "char error line:", lnm));
194036c776aSlum 			}
1955e37f6aeSlum 			esc = spc = 0;
19605d4bb1aSlum 		} else if (*p == ')') {
19755a1b3e1Slum 			if (lastchr != NULL && *lastchr == '(')
19855a1b3e1Slum 				return(dobeep_num("Empty parenthesis "\
19955a1b3e1Slum 				    "not supported line", lnm));
20005d4bb1aSlum 			if (inquote == 0) {
201bdb72052Slum 				if (begp != NULL) {
20241167f9aSlum 					if (*prechr == ' ')
20341167f9aSlum 						ns--;
204bbed9a29Slum 					if (endp == NULL)
205bbed9a29Slum 						*p = '\0';
206bbed9a29Slum 					else
207bbed9a29Slum 						*endp = '\0';
20884dad9ebSlum 
20905d4bb1aSlum 					ret = parse(begp, lrp, &rp, blkid,
21041167f9aSlum 					    ++expctr, elen - spc, ns);
21184dad9ebSlum 					if (!ret) {
212036c776aSlum 						cleanup();
21384dad9ebSlum 						return(ret);
21484dad9ebSlum 					}
2155e37f6aeSlum 					elen = 0;
216036c776aSlum 				}
21717c1e8f1Slum 				lrp = &rp;
218bdb72052Slum 				begp = endp = NULL;
219bbed9a29Slum 				pctr--;
22005d4bb1aSlum 			} else if (inquote != 1) {
22105d4bb1aSlum 				cleanup();
22281b9a829Slum 				return(dobeep_num("Opening and closing quote "\
22381b9a829Slum 				    "char error line:", lnm));
22405d4bb1aSlum 			}
2255e37f6aeSlum 			esc = spc = 0;
226bbed9a29Slum 		} else if (*p != ' ' && *p != '\t') {
22723ef0c45Slum 			if (begp == NULL) {
228edf7cdfaSlum 				begp = p;
22942033fc5Slum 				if (*begp == '"' || isdigit(*begp))
23023ef0c45Slum 					return(dobeep_num("First char of "\
23123ef0c45Slum 					    "expression error line:", lnm));
23223ef0c45Slum 			}
233036c776aSlum 			if (*p == '"') {
234e5166e5aSlum 				if (inquote == 0 && esc == 0) {
235e5166e5aSlum 					if (*prechr != ' ' && *prechr != '\t')
23681b9a829Slum 						return(dobeep_num("Parse error"\
23781b9a829Slum 						    " line:", lnm));
2387ec1563aSlum 					inquote++;
239e5166e5aSlum 				} else if (inquote > 0 && esc == 1)
2407ec1563aSlum 					esc = 0;
241036c776aSlum 				else
2427ec1563aSlum 					inquote--;
243e5166e5aSlum 			} else if (*prechr == '"' && inquote == 0) {
24481b9a829Slum 				return(dobeep_num("Parse error line:", lnm));
245036c776aSlum 			}
246bbed9a29Slum 			endp = NULL;
2475e37f6aeSlum 			spc = 0;
248bdb72052Slum 		} else if (endp == NULL && (*p == ' ' || *p == '\t')) {
2495e37f6aeSlum 			if (inquote == 0) {
250bbed9a29Slum 				*p = ' ';
251bbed9a29Slum 				endp = p;
2525e37f6aeSlum 				spc++;
25341167f9aSlum 				if (begp != NULL)
25441167f9aSlum 					ns++;
2555e37f6aeSlum 			}
2567ec1563aSlum 			esc = 0;
2575e37f6aeSlum 		} else if (*p == '\t' || *p == ' ') {
2585e37f6aeSlum 			if (inquote == 0) {
259bbed9a29Slum 				*p = ' ';
2605e37f6aeSlum 				spc++;
2615e37f6aeSlum 			}
2627ec1563aSlum 			esc = 0;
2637ec1563aSlum 		}
26455a1b3e1Slum 		if (*p != '\t' && *p != ' ' && inquote == 0)
26555a1b3e1Slum 			lastchr = p;
266edf7cdfaSlum 
26717c1e8f1Slum 		if (pctr == 0) {
268bbed9a29Slum 			blkid++;
26917c1e8f1Slum 			expctr = 0;
27017c1e8f1Slum 			defnam = NULL;
27117c1e8f1Slum 		}
272e5166e5aSlum 		prechr = p;
273bbed9a29Slum 	}
274bbed9a29Slum 
275036c776aSlum 	if (pctr != 0) {
276036c776aSlum 		cleanup();
27781b9a829Slum 		return(dobeep_num("Opening and closing parentheses error line:",
27881b9a829Slum 		    lnm));
279036c776aSlum 	}
280036c776aSlum 	if (ret == FALSE)
281036c776aSlum 		cleanup();
282036c776aSlum 	else
283036c776aSlum 		clearexp();	/* leave lists but remove expressions */
284bbed9a29Slum 
285bbed9a29Slum 	return (ret);
286bbed9a29Slum }
287bbed9a29Slum 
28884dad9ebSlum 
28984dad9ebSlum static int
2905e37f6aeSlum parse(char *begp, const char *par1, const char *par2, int blkid, int expctr,
29141167f9aSlum     int elen, int ns)
29284dad9ebSlum {
29317c1e8f1Slum 	char    *regs;
29417c1e8f1Slum 	int 	 ret = FALSE;
29584dad9ebSlum 
29617c1e8f1Slum 	if (strncmp(begp, "define", 6) == 0) {
2975e37f6aeSlum 		ret = parsdef(begp, par1, par2, blkid, expctr, elen);
29817c1e8f1Slum 		if (ret == TRUE || ret == FALSE)
29917c1e8f1Slum 			return (ret);
30017c1e8f1Slum 	} else if (strncmp(begp, "list", 4) == 0)
3015e37f6aeSlum 		return(parsval(begp, par1, par2, blkid, expctr, elen));
30284dad9ebSlum 
30317c1e8f1Slum 	regs = "^exit$";
30417c1e8f1Slum 	if (doregex(regs, begp))
30517c1e8f1Slum 		return(exitinterpreter(NULL, NULL, FALSE));
30617c1e8f1Slum 
30717c1e8f1Slum 	/* mg function name regex */
30817c1e8f1Slum 	regs = "^[A-Za-z-]+$";
30917c1e8f1Slum         if (doregex(regs, begp))
31081b9a829Slum 		return(excline(begp, 0, 0));
31117c1e8f1Slum 
31217c1e8f1Slum 	/* Corner case 1 */
31317c1e8f1Slum 	if (strncmp(begp, "global-set-key ", 15) == 0)
31417c1e8f1Slum 		/* function name as 2nd param screws up multiarg. */
31581b9a829Slum 		return(excline(begp, 0, 0));
31617c1e8f1Slum 
31717c1e8f1Slum 	/* Corner case 2 */
31817c1e8f1Slum 	if (strncmp(begp, "define-key ", 11) == 0)
31917c1e8f1Slum 		/* function name as 3rd param screws up multiarg. */
32081b9a829Slum 		return(excline(begp, 0, 0));
32117c1e8f1Slum 
3225e37f6aeSlum 	return (parsexp(begp, par1, par2, blkid, expctr, elen));
32384dad9ebSlum }
32484dad9ebSlum 
325bbed9a29Slum static int
3265e37f6aeSlum parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr,
3275e37f6aeSlum     int elen)
328bbed9a29Slum {
329bbed9a29Slum 	char    *regs;
330bbed9a29Slum 
33117c1e8f1Slum 	if ((defnam == NULL) && (expctr != 1))
33281b9a829Slum 		return(dobeep_num("'define' incorrectly used line:", lnm));
3334937e3a7Slum 
334bbed9a29Slum         /* Does the line have a incorrect variable 'define' like: */
335bbed9a29Slum         /* (define i y z) */
33617c1e8f1Slum         regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$";
33717c1e8f1Slum         if (doregex(regs, begp))
33881b9a829Slum                 return(dobeep_num("Invalid use of define line:", lnm));
339bbed9a29Slum 
340bbed9a29Slum         /* Does the line have a single variable 'define' like: */
341bbed9a29Slum         /* (define i 0) */
3427b3a2786Slum         regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$";
34317c1e8f1Slum         if (doregex(regs, begp)) {
34417c1e8f1Slum 		if (par1 == &lp && par2 == &rp && expctr == 1)
3455e37f6aeSlum 			return(founddef(begp, blkid, expctr, 1, elen));
34681b9a829Slum 		return(dobeep_num("Invalid use of define line:", lnm));
34717c1e8f1Slum 	}
34817c1e8f1Slum 	/* Does the line have  '(define i(' */
34917c1e8f1Slum         regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$";
35017c1e8f1Slum         if (doregex(regs, begp)) {
35117c1e8f1Slum 		if (par1 == &lp && par2 == &lp && expctr == 1)
3525e37f6aeSlum                 	return(founddef(begp, blkid, expctr, 0, elen));
35381b9a829Slum 		return(dobeep_num("Invalid use of 'define' line:", lnm));
35417c1e8f1Slum 	}
35517c1e8f1Slum 	/* Does the line have  '(define (' */
35617c1e8f1Slum 	regs = "^define$";
35717c1e8f1Slum 	if (doregex(regs, begp)) {
35817c1e8f1Slum 		if (par1 == &lp && par2 == &lp && expctr == 1)
35917c1e8f1Slum 			return(foundfun(begp, expctr));
36081b9a829Slum 		return(dobeep_num("Invalid use of 'define' line:", lnm));
361bbed9a29Slum 	}
362bbed9a29Slum 
36317c1e8f1Slum 	return (ABORT);
36417c1e8f1Slum }
36517c1e8f1Slum 
3663c2c8acbSlum static int
3675e37f6aeSlum parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr,
3685e37f6aeSlum     int elen)
3693c2c8acbSlum {
370036c776aSlum 	char    *regs;
3713c2c8acbSlum 
37217c1e8f1Slum 	/* Does the line have 'list' */
37317c1e8f1Slum 	regs = "^list$";
37417c1e8f1Slum 	if (doregex(regs, begp))
37581b9a829Slum 		return(dobeep_num("Invalid use of list line:", lnm));
376228b1246Slum 
37717c1e8f1Slum         /* Does the line have a 'list' like: */
37817c1e8f1Slum         /* (list "a" "b") */
37917c1e8f1Slum         regs = "^list[ ]+.*$";
38017c1e8f1Slum         if (doregex(regs, begp)) {
38117c1e8f1Slum 		if (expctr == 1)
38281b9a829Slum 			return(dobeep_num("list with no-where to go.", lnm));
38317c1e8f1Slum 
38417c1e8f1Slum 		if (par1 == &lp && expctr > 1)
3855e37f6aeSlum 			return(foundlst(begp, blkid, expctr, elen));
38617c1e8f1Slum 
38781b9a829Slum 		return(dobeep_num("Invalid use of list line:", lnm));
38817c1e8f1Slum 	}
38917c1e8f1Slum 	return (FALSE);
39017c1e8f1Slum }
39117c1e8f1Slum 
39217c1e8f1Slum static int
3935e37f6aeSlum parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr,
3945e37f6aeSlum     int elen)
39517c1e8f1Slum {
39617c1e8f1Slum 	struct expentry *e1 = NULL;
39717c1e8f1Slum 	PF		 funcp;
398*479c151dSjsg 	char		*cmdp, *fendp, *valp, *fname, *funb = NULL;
39917c1e8f1Slum 	int		 numparams, ret;
40017c1e8f1Slum 
40117c1e8f1Slum 	cmdp = begp;
402bbed9a29Slum 	fendp = strchr(cmdp, ' ');
4033c2c8acbSlum 	*fendp = '\0';
40417c1e8f1Slum 
4053c2c8acbSlum 	/*
4063c2c8acbSlum 	 * If no extant mg command found, just return.
4073c2c8acbSlum 	 */
4083c2c8acbSlum 	if ((funcp = name_function(cmdp)) == NULL)
4093c2c8acbSlum 		return (dobeep_msgs("Unknown command:", cmdp));
4103c2c8acbSlum 
4113c2c8acbSlum 	numparams = numparams_function(funcp);
4123c2c8acbSlum 	if (numparams == 0)
4133c2c8acbSlum 		return (dobeep_msgs("Command takes no arguments:", cmdp));
4143c2c8acbSlum 
415089ec08bSlum 	if (numparams == -1)
416089ec08bSlum 		return (dobeep_msgs("Interactive command found:", cmdp));
417089ec08bSlum 
41817c1e8f1Slum 	if ((e1 = malloc(sizeof(struct expentry))) == NULL) {
41917c1e8f1Slum 		cleanup();
42017c1e8f1Slum 		return (dobeep_msg("malloc Error"));
42117c1e8f1Slum 	}
42217c1e8f1Slum 	TAILQ_INSERT_HEAD(&ehead, e1, eentry);
42317c1e8f1Slum 	if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) {
42417c1e8f1Slum 		cleanup();
42517c1e8f1Slum 		return(dobeep_msg("strndup error"));
42617c1e8f1Slum 	}
42717c1e8f1Slum 	cmdp = e1->fun;
42817c1e8f1Slum 	fname = e1->fun;
42917c1e8f1Slum 	e1->funbuf[0] = '\0';
43017c1e8f1Slum 	funb = e1->funbuf;
43117c1e8f1Slum 	e1->expctr = expctr;
43217c1e8f1Slum 	e1->blkid = blkid;
43317c1e8f1Slum 	/* need to think about these two */
43417c1e8f1Slum 	e1->par1 = par1;
43517c1e8f1Slum 	e1->par2 = par2;
436bbed9a29Slum 
43717c1e8f1Slum 	*fendp = ' ';
43817c1e8f1Slum 	valp = fendp + 1;
43917c1e8f1Slum 
44017c1e8f1Slum 	ret = expandvals(cmdp, valp, funb);
44117c1e8f1Slum 	if (!ret)
44217c1e8f1Slum 		return (ret);
44317c1e8f1Slum 
44417c1e8f1Slum 	return (multiarg(fname, funb, numparams));
44517c1e8f1Slum }
44617c1e8f1Slum 
44717c1e8f1Slum /*
44817c1e8f1Slum  * Pass a list of arguments to a function.
44917c1e8f1Slum  */
45017c1e8f1Slum static int
45117c1e8f1Slum multiarg(char *cmdp, char *argbuf, int numparams)
45217c1e8f1Slum {
45317c1e8f1Slum 	char	 excbuf[BUFSIZE];
45417c1e8f1Slum 	char	*argp, *p, *s = " ";
45517c1e8f1Slum 	char	*regs;
45617c1e8f1Slum 	int	 spc, numspc;
45717c1e8f1Slum 	int	 fin, inquote;
45817c1e8f1Slum 
4593c2c8acbSlum 	argp = argbuf;
46017c1e8f1Slum 	spc = 1; /* initially fake a space so we find first argument */
46117c1e8f1Slum 	numspc = fin = inquote = 0;
4623c2c8acbSlum 
463bbed9a29Slum 	for (p = argbuf; *p != '\0'; p++) {
464bbed9a29Slum 		if (*(p + 1) == '\0')
465bbed9a29Slum 			fin = 1;
466bbed9a29Slum 
467bbed9a29Slum 		if (*p != ' ') {
468036c776aSlum 			if (*p == '"') {
469036c776aSlum 				if (inquote == 1)
470036c776aSlum 					inquote = 0;
471036c776aSlum 				else
472036c776aSlum 					inquote = 1;
473036c776aSlum 			}
474bbed9a29Slum 			if (spc == 1)
47517c1e8f1Slum 				if ((numspc % numparams) == 0) {
476bbed9a29Slum 					argp = p;
47717c1e8f1Slum 				}
478bbed9a29Slum 			spc = 0;
4793c2c8acbSlum 		}
480036c776aSlum 		if ((*p == ' ' && inquote == 0) || fin) {
48117c1e8f1Slum 			if (spc == 1)/* || (numspc % numparams == 0))*/
4823c2c8acbSlum 				continue;
48317c1e8f1Slum 			if ((numspc % numparams) != (numparams - 1)) {
48417c1e8f1Slum 				numspc++;
48517c1e8f1Slum 				continue;
48617c1e8f1Slum 			}
487bbed9a29Slum 			if (*p == ' ') {
4883c2c8acbSlum 				*p = '\0';		/* terminate arg string */
489bbed9a29Slum 			}
4903c2c8acbSlum 			excbuf[0] = '\0';
491036c776aSlum 			regs = "[\"]+.*[\"]+";
492bbed9a29Slum 
49317c1e8f1Slum        			if (!doregex(regs, argp)) {
4948fa75880Slum 				const char *errstr;
4958fa75880Slum 
4965fb5aa3fSnaddy 				strtonum(argp, 0, INT_MAX, &errstr);
4978fa75880Slum 				if (errstr != NULL)
4988fa75880Slum 					return (dobeep_msgs("Var not found:",
4998fa75880Slum 					    argp));
5008fa75880Slum 			}
501036c776aSlum 
5023c2c8acbSlum 			if (strlcpy(excbuf, cmdp, sizeof(excbuf))
5033c2c8acbSlum 			    >= sizeof(excbuf))
5043c2c8acbSlum 				return (dobeep_msg("strlcpy error"));
5053c2c8acbSlum 			if (strlcat(excbuf, s, sizeof(excbuf))
5063c2c8acbSlum 			    >= sizeof(excbuf))
5073c2c8acbSlum 				return (dobeep_msg("strlcat error"));
5083c2c8acbSlum 			if (strlcat(excbuf, argp, sizeof(excbuf))
5093c2c8acbSlum 			    >= sizeof(excbuf))
5103c2c8acbSlum 				return (dobeep_msg("strlcat error"));
5113c2c8acbSlum 
51281b9a829Slum 			excline(excbuf, 0, 0);
513bbed9a29Slum 
514bbed9a29Slum 			if (fin)
515bbed9a29Slum 				break;
516bbed9a29Slum 
517bbed9a29Slum 			*p = ' ';		/* unterminate arg string */
51817c1e8f1Slum 			numspc++;
5193c2c8acbSlum 			spc = 1;
5203c2c8acbSlum 		}
5213c2c8acbSlum 	}
5223c2c8acbSlum 	return (TRUE);
5233c2c8acbSlum }
5243c2c8acbSlum 
5253c2c8acbSlum /*
5263c2c8acbSlum  * Is an item a value or a variable?
5273c2c8acbSlum  */
5283c2c8acbSlum static int
529bbed9a29Slum isvar(char **argp, char **varbuf, int sizof)
5303c2c8acbSlum {
5313c2c8acbSlum 	struct varentry *v1 = NULL;
5323c2c8acbSlum 
5333c2c8acbSlum 	if (SLIST_EMPTY(&varhead))
5343c2c8acbSlum 		return (FALSE);
5357bf68b76Slum #ifdef  MGLOG
536bbed9a29Slum 	mglog_isvar(*varbuf, *argp, sizof);
5377bf68b76Slum #endif
5383c2c8acbSlum 	SLIST_FOREACH(v1, &varhead, entry) {
5397ec1563aSlum 		if (strcmp(*argp, v1->v_name) == 0) {
5407ec1563aSlum 			(void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof);
5413c2c8acbSlum 			return (TRUE);
5423c2c8acbSlum 		}
5433c2c8acbSlum 	}
5443c2c8acbSlum 	return (FALSE);
5453c2c8acbSlum }
5463c2c8acbSlum 
54717c1e8f1Slum 
54817c1e8f1Slum static int
54917c1e8f1Slum foundfun(char *defstr, int expctr)
55017c1e8f1Slum {
55117c1e8f1Slum 	return (TRUE);
55217c1e8f1Slum }
55317c1e8f1Slum 
55417c1e8f1Slum static int
5555e37f6aeSlum foundlst(char *defstr, int blkid, int expctr, int elen)
55617c1e8f1Slum {
55717c1e8f1Slum 	char		*p;
55817c1e8f1Slum 
55917c1e8f1Slum 	p = strstr(defstr, " ");
56017c1e8f1Slum 	p = skipwhite(p);
56117c1e8f1Slum 	expandvals(NULL, p, defnam);
56217c1e8f1Slum 
56317c1e8f1Slum 	return (TRUE);
56417c1e8f1Slum }
56517c1e8f1Slum 
5663c2c8acbSlum /*
56717c1e8f1Slum  * 'define' strings follow the regex in parsdef().
5683c2c8acbSlum  */
5693c2c8acbSlum static int
5705e37f6aeSlum founddef(char *defstr, int blkid, int expctr, int hasval, int elen)
5713c2c8acbSlum {
5723c2c8acbSlum 	struct varentry *vt, *v1 = NULL;
573bbed9a29Slum 	char		*p, *vnamep, *vendp = NULL, *valp;
574036c776aSlum 
575bbed9a29Slum 	p = strstr(defstr, " ");        /* move to first ' ' char.    */
5763c2c8acbSlum 	vnamep = skipwhite(p);		/* find first char of var name. */
5773c2c8acbSlum 	vendp = vnamep;
5783c2c8acbSlum 
57917c1e8f1Slum 	/* now find the end of the define/list name */
5803c2c8acbSlum 	while (1) {
5813c2c8acbSlum 		++vendp;
582bbed9a29Slum 		if (*vendp == ' ')
5833c2c8acbSlum 			break;
5843c2c8acbSlum 	}
5853c2c8acbSlum 	*vendp = '\0';
586bbed9a29Slum 
5873c2c8acbSlum 	/*
58817c1e8f1Slum 	 * Check list name is not an existing mg function.
5893c2c8acbSlum 	 */
5903c2c8acbSlum 	if (name_function(vnamep) != NULL)
5913c2c8acbSlum 		return(dobeep_msgs("Variable/function name clash:", vnamep));
5923c2c8acbSlum 
5933c2c8acbSlum 	if (!SLIST_EMPTY(&varhead)) {
5943c2c8acbSlum 		SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
5957ec1563aSlum 			if (strcmp(vnamep, v1->v_name) == 0)
5963c2c8acbSlum 				SLIST_REMOVE(&varhead, v1, varentry, entry);
5973c2c8acbSlum 		}
5983c2c8acbSlum 	}
5993c2c8acbSlum 	if ((v1 = malloc(sizeof(struct varentry))) == NULL)
6003c2c8acbSlum 		return (ABORT);
6013c2c8acbSlum 	SLIST_INSERT_HEAD(&varhead, v1, entry);
6027ec1563aSlum 	if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL)
6033c2c8acbSlum 		return(dobeep_msg("strndup error"));
6047ec1563aSlum 	vnamep = v1->v_name;
6057ec1563aSlum 	v1->v_count = 0;
6067ec1563aSlum 	v1->v_vals = NULL;
6077ec1563aSlum 	v1->v_buf[0] = '\0';
6083c2c8acbSlum 
6097ec1563aSlum 	defnam = v1->v_buf;
61017c1e8f1Slum 
61117c1e8f1Slum 	if (hasval) {
61217c1e8f1Slum 		valp = skipwhite(vendp + 1);
61317c1e8f1Slum 
61417c1e8f1Slum 		expandvals(NULL, valp, defnam);
61517c1e8f1Slum 		defnam = NULL;
61617c1e8f1Slum 	}
61717c1e8f1Slum 	*vendp = ' ';
61817c1e8f1Slum 	return (TRUE);
61917c1e8f1Slum }
62017c1e8f1Slum 
62117c1e8f1Slum 
62217c1e8f1Slum static int
62317c1e8f1Slum expandvals(char *cmdp, char *valp, char *bp)
62417c1e8f1Slum {
62517c1e8f1Slum 	char	 excbuf[BUFSIZE], argbuf[BUFSIZE];
62617c1e8f1Slum 	char	 contbuf[BUFSIZE], varbuf[BUFSIZE];
62717c1e8f1Slum 	char	*argp, *endp, *p, *v, *s = " ";
62817c1e8f1Slum 	char	*regs;
62917c1e8f1Slum 	int	 spc, cnt;
6305fb5aa3fSnaddy 	int	 sizof, fin, inquote;
63117c1e8f1Slum 
63217c1e8f1Slum 	/* now find the first argument */
63317c1e8f1Slum 	p = skipwhite(valp);
63417c1e8f1Slum 
63517c1e8f1Slum 	if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
63617c1e8f1Slum 		return (dobeep_msg("strlcpy error"));
63717c1e8f1Slum 	argp = argbuf;
63817c1e8f1Slum 	spc = 1; /* initially fake a space so we find first argument */
6395fb5aa3fSnaddy 	fin = inquote = cnt = spc = 0;
64017c1e8f1Slum 
64117c1e8f1Slum 	for (p = argbuf; *p != '\0'; p++) {
64217c1e8f1Slum 		if (*(p + 1) == '\0')
64317c1e8f1Slum 			fin = 1;
64417c1e8f1Slum 
64517c1e8f1Slum 		if (*p != ' ') {
64617c1e8f1Slum 			if (*p == '"') {
64717c1e8f1Slum 				if (inquote == 1)
64817c1e8f1Slum 					inquote = 0;
64917c1e8f1Slum 				else
65017c1e8f1Slum 					inquote = 1;
65117c1e8f1Slum 			}
6523c2c8acbSlum 			if (spc == 1)
65317c1e8f1Slum 				argp = p;
6543c2c8acbSlum 			spc = 0;
6553c2c8acbSlum 		}
65617c1e8f1Slum 		if ((*p == ' ' && inquote == 0) || fin) {
65717c1e8f1Slum 			if (spc == 1)
65817c1e8f1Slum 				continue;
65917c1e8f1Slum 			/* terminate arg string */
66017c1e8f1Slum 			if (*p == ' ') {
66117c1e8f1Slum 				*p = '\0';
6623c2c8acbSlum 			}
66317c1e8f1Slum 			endp = p + 1;
66417c1e8f1Slum 			excbuf[0] = '\0';
66517c1e8f1Slum 			varbuf[0] = '\0';
66617c1e8f1Slum 			contbuf[0] = '\0';
66717c1e8f1Slum 			sizof = sizeof(varbuf);
66817c1e8f1Slum 			v = varbuf;
66917c1e8f1Slum 			regs = "[\"]+.*[\"]+";
67017c1e8f1Slum        			if (doregex(regs, argp))
67117c1e8f1Slum 				;			/* found quotes */
67217c1e8f1Slum 			else if (isvar(&argp, &v, sizof)) {
6733c2c8acbSlum 
67417c1e8f1Slum 				(void)(strlcat(varbuf, " ",
67517c1e8f1Slum                                     sizof) >= sizof);
67617c1e8f1Slum 
67717c1e8f1Slum 				*p = ' ';
67817c1e8f1Slum 				(void)(strlcpy(contbuf, endp,
67917c1e8f1Slum 				    sizeof(contbuf)) >= sizeof(contbuf));
68017c1e8f1Slum 
68117c1e8f1Slum 				(void)(strlcat(varbuf, contbuf,
68217c1e8f1Slum 				    sizof) >= sizof);
68317c1e8f1Slum 
68417c1e8f1Slum 				argbuf[0] = ' ';
68517c1e8f1Slum 				argbuf[1] = '\0';
68617c1e8f1Slum 				(void)(strlcat(argbuf, varbuf,
68717c1e8f1Slum 				    sizof) >= sizof);
68817c1e8f1Slum 
68917c1e8f1Slum 				p = argp = argbuf;
69017c1e8f1Slum 				spc = 1;
69117c1e8f1Slum 				fin = 0;
69217c1e8f1Slum 				continue;
69317c1e8f1Slum 			} else {
69417c1e8f1Slum 				const char *errstr;
69517c1e8f1Slum 
6965fb5aa3fSnaddy 				strtonum(argp, 0, INT_MAX, &errstr);
69717c1e8f1Slum 				if (errstr != NULL)
69817c1e8f1Slum 					return (dobeep_msgs("Var not found:",
69917c1e8f1Slum 					    argp));
70017c1e8f1Slum 			}
701fefadbf1Slum #ifdef  MGLOG
70217c1e8f1Slum         mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE);
703fefadbf1Slum #endif
70417c1e8f1Slum 			if (*bp != '\0') {
70517c1e8f1Slum 				if (strlcat(bp, s, BUFSIZE) >= BUFSIZE)
70617c1e8f1Slum 					return (dobeep_msg("strlcat error"));
70717c1e8f1Slum 			}
70817c1e8f1Slum 			if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) {
70917c1e8f1Slum 				return (dobeep_msg("strlcat error"));
71017c1e8f1Slum 			}
7117ec1563aSlum /*			v1->v_count++;*/
7123c2c8acbSlum 
71317c1e8f1Slum 			if (fin)
71417c1e8f1Slum 				break;
71517c1e8f1Slum 
71617c1e8f1Slum 			*p = ' ';		/* unterminate arg string */
71717c1e8f1Slum 			spc = 1;
71817c1e8f1Slum 		}
71917c1e8f1Slum 	}
7203c2c8acbSlum 	return (TRUE);
7213c2c8acbSlum }
7223c2c8acbSlum 
7233c2c8acbSlum /*
724bbed9a29Slum  * Finished with buffer evaluation, so clean up any vars.
725bbed9a29Slum  * Perhaps keeps them in mg even after use,...
7263c2c8acbSlum  */
7277ec1563aSlum /*static int
7283c2c8acbSlum clearvars(void)
7293c2c8acbSlum {
7303c2c8acbSlum 	struct varentry	*v1 = NULL;
7313c2c8acbSlum 
7323c2c8acbSlum 	while (!SLIST_EMPTY(&varhead)) {
7333c2c8acbSlum 		v1 = SLIST_FIRST(&varhead);
7343c2c8acbSlum 		SLIST_REMOVE_HEAD(&varhead, entry);
7357ec1563aSlum 		free(v1->v_name);
7363c2c8acbSlum 		free(v1);
7373c2c8acbSlum 	}
7383c2c8acbSlum 	return (FALSE);
7393c2c8acbSlum }
7407ec1563aSlum */
7413c2c8acbSlum /*
742bbed9a29Slum  * Finished with block evaluation, so clean up any expressions.
7433c2c8acbSlum  */
744bbed9a29Slum static void
745bbed9a29Slum clearexp(void)
7463c2c8acbSlum {
747bbed9a29Slum 	struct expentry	*e1 = NULL;
748e5364bf1Slum 
7494937e3a7Slum 	while (!TAILQ_EMPTY(&ehead)) {
7504937e3a7Slum 		e1 = TAILQ_FIRST(&ehead);
7514937e3a7Slum 		TAILQ_REMOVE(&ehead, e1, eentry);
75217c1e8f1Slum 		free(e1->fun);
753bbed9a29Slum 		free(e1);
754e5364bf1Slum 	}
755bbed9a29Slum 	return;
7563c2c8acbSlum }
757228b1246Slum 
758228b1246Slum /*
759036c776aSlum  * Cleanup before leaving.
760036c776aSlum  */
761036c776aSlum void
762036c776aSlum cleanup(void)
763036c776aSlum {
76417c1e8f1Slum 	defnam = NULL;
76517c1e8f1Slum 
766036c776aSlum 	clearexp();
7677ec1563aSlum /*	clearvars();*/
768036c776aSlum }
769036c776aSlum 
770036c776aSlum /*
771228b1246Slum  * Test a string against a regular expression.
772228b1246Slum  */
773bbed9a29Slum static int
774228b1246Slum doregex(char *r, char *e)
775228b1246Slum {
776228b1246Slum 	regex_t  regex_buff;
777228b1246Slum 
778228b1246Slum 	if (regcomp(&regex_buff, r, REG_EXTENDED)) {
779228b1246Slum 		regfree(&regex_buff);
78081b9a829Slum 		return(dobeep_num("Regex compilation error line:", lnm));
781228b1246Slum 	}
782228b1246Slum 	if (!regexec(&regex_buff, e, 0, NULL, 0)) {
783228b1246Slum 		regfree(&regex_buff);
784228b1246Slum 		return(TRUE);
785228b1246Slum 	}
786e5364bf1Slum 	regfree(&regex_buff);
787e5364bf1Slum 	return(FALSE);
788228b1246Slum }
789ef122b21Slum 
790ef122b21Slum /*
791ef122b21Slum  * Display a message so it is apparent that this is the method which stopped
792ef122b21Slum  * execution.
793ef122b21Slum  */
794ef122b21Slum static int
79517c1e8f1Slum exitinterpreter(char *ptr, char *dobuf, int dosiz)
796ef122b21Slum {
797ef122b21Slum 	cleanup();
798ef122b21Slum 	if (batch == 0)
799ef122b21Slum 		return(dobeep_msg("Interpreter exited via exit command."));
800ef122b21Slum 	return(FALSE);
801ef122b21Slum }
80217c1e8f1Slum 
80317c1e8f1Slum /*
80417c1e8f1Slum  * All code below commented out (until end of file).
80517c1e8f1Slum  *
80617c1e8f1Slum  * Need to think about how interpreter functions are done.
80717c1e8f1Slum  * Probably don't have a choice with string-append().
80817c1e8f1Slum 
80917c1e8f1Slum static int 	 getenvironmentvariable(char *, char *, int);
81017c1e8f1Slum static int	 stringappend(char *, char *, int);
81117c1e8f1Slum 
81217c1e8f1Slum typedef int	 (*PFI)(char *, char *, int);
81317c1e8f1Slum 
81417c1e8f1Slum 
81517c1e8f1Slum struct ifunmap {
81617c1e8f1Slum 	PFI		 fn_funct;
81717c1e8f1Slum 	const char 	*fn_name;
81817c1e8f1Slum 	struct ifunmap	*fn_next;
81917c1e8f1Slum };
82017c1e8f1Slum static struct ifunmap *ifuns;
82117c1e8f1Slum 
82217c1e8f1Slum static struct ifunmap ifunctnames[] = {
82317c1e8f1Slum 	{exitinterpreter, "exit"},
82417c1e8f1Slum 	{getenvironmentvariable, "get-environment-variable"},
82517c1e8f1Slum 	{stringappend, "string-append"},
82617c1e8f1Slum 	{NULL, NULL}
82717c1e8f1Slum };
82817c1e8f1Slum 
82917c1e8f1Slum void
83017c1e8f1Slum ifunmap_init(void)
83117c1e8f1Slum {
83217c1e8f1Slum 	struct ifunmap *fn;
83317c1e8f1Slum 
83417c1e8f1Slum 	for (fn = ifunctnames; fn->fn_name != NULL; fn++) {
83517c1e8f1Slum 		fn->fn_next = ifuns;
83617c1e8f1Slum 		ifuns = fn;
83717c1e8f1Slum 	}
83817c1e8f1Slum }
83917c1e8f1Slum 
84017c1e8f1Slum PFI
84117c1e8f1Slum name_ifun(const char *ifname)
84217c1e8f1Slum {
84317c1e8f1Slum 	struct ifunmap 	*fn;
84417c1e8f1Slum 
84517c1e8f1Slum 	for (fn = ifuns; fn != NULL; fn = fn->fn_next) {
84617c1e8f1Slum 		if (strcmp(fn->fn_name, ifname) == 0)
84717c1e8f1Slum 			return (fn->fn_funct);
84817c1e8f1Slum 	}
84917c1e8f1Slum 
85017c1e8f1Slum 	return (NULL);
85117c1e8f1Slum }
85217c1e8f1Slum 
85317c1e8f1Slum 
85417c1e8f1Slum int
85517c1e8f1Slum dofunc(char **ifname, char **tmpbuf, int sizof)
85617c1e8f1Slum {
85717c1e8f1Slum 	PFI 	 fnc;
85817c1e8f1Slum 	char	*p, *tmp;
85917c1e8f1Slum 
86017c1e8f1Slum 	p = strstr(*ifname, " ");
86117c1e8f1Slum 	*p = '\0';
86217c1e8f1Slum 
86317c1e8f1Slum 	fnc = name_ifun(*ifname);
86417c1e8f1Slum 	if (fnc == NULL)
86517c1e8f1Slum 		return (FALSE);
86617c1e8f1Slum 
86717c1e8f1Slum 	*p = ' ';
86817c1e8f1Slum 
86917c1e8f1Slum 	tmp = *tmpbuf;
87017c1e8f1Slum 
87117c1e8f1Slum 	fnc(p, tmp, sizof);
87217c1e8f1Slum 
87317c1e8f1Slum 	return (TRUE);
87417c1e8f1Slum }
87517c1e8f1Slum 
87617c1e8f1Slum static int
87717c1e8f1Slum getenvironmentvariable(char *ptr, char *dobuf, int dosiz)
87817c1e8f1Slum {
87917c1e8f1Slum 	char		*t;
88017c1e8f1Slum 	char		*tmp;
88117c1e8f1Slum 	const char	*q = "\"";
88217c1e8f1Slum 
88317c1e8f1Slum 	t = skipwhite(ptr);
88417c1e8f1Slum 
88517c1e8f1Slum 	if (t[0] == *q || t[strlen(t) - 1] == *q)
88617c1e8f1Slum 		return (dobeep_msgs("Please remove '\"' around:", t));
88717c1e8f1Slum 	if ((tmp = getenv(t)) == NULL || *tmp == '\0')
88817c1e8f1Slum 		return(dobeep_msgs("Envar not found:", t));
88917c1e8f1Slum 
89017c1e8f1Slum 	dobuf[0] = '\0';
89117c1e8f1Slum 	if (strlcat(dobuf, q, dosiz) >= dosiz)
89217c1e8f1Slum 		return (dobeep_msg("strlcat error"));
89317c1e8f1Slum 	if (strlcat(dobuf, tmp, dosiz) >= dosiz)
89417c1e8f1Slum 		return (dobeep_msg("strlcat error"));
89517c1e8f1Slum 	if (strlcat(dobuf, q, dosiz) >= dosiz)
89617c1e8f1Slum 		return (dobeep_msg("strlcat error"));
89717c1e8f1Slum 
89817c1e8f1Slum 	return (TRUE);
89917c1e8f1Slum }
90017c1e8f1Slum 
90117c1e8f1Slum static int
90217c1e8f1Slum stringappend(char *ptr, char *dobuf, int dosiz)
90317c1e8f1Slum {
90417c1e8f1Slum 	char		 varbuf[BUFSIZE], funbuf[BUFSIZE];
90517c1e8f1Slum 	char            *p, *f, *v, *vendp;
90617c1e8f1Slum 	int		 sizof, fin = 0;
90717c1e8f1Slum 
90817c1e8f1Slum 	varbuf[0] = funbuf[0] = '\0';
90917c1e8f1Slum 	f = funbuf;
91017c1e8f1Slum 	v = varbuf;
91117c1e8f1Slum 	sizof = sizeof(varbuf);
91217c1e8f1Slum 	*dobuf = '\0';
91317c1e8f1Slum 
91417c1e8f1Slum 	p = skipwhite(ptr);
91517c1e8f1Slum 
91617c1e8f1Slum 	while (*p != '\0') {
91717c1e8f1Slum 		vendp = p;
91817c1e8f1Slum 		while (1) {
91917c1e8f1Slum 			if (*vendp == ' ') {
92017c1e8f1Slum 				break;
92117c1e8f1Slum 			} else if (*vendp == '\0') {
92217c1e8f1Slum 				fin = 1;
92317c1e8f1Slum 				break;
92417c1e8f1Slum 			}
92517c1e8f1Slum 			++vendp;
92617c1e8f1Slum 		}
92717c1e8f1Slum         	*vendp = '\0';
92817c1e8f1Slum 
92917c1e8f1Slum 		if (isvar(&p, &v, sizof)) {
93017c1e8f1Slum 			if (v[0] == '"' && v[strlen(v) - 1] == '"' ) {
93117c1e8f1Slum 				v[strlen(v) - 1] = '\0';
93217c1e8f1Slum 				v = v + 1;
93317c1e8f1Slum 			}
93417c1e8f1Slum 			if (strlcat(f, v, sizof) >= sizof)
93517c1e8f1Slum 				return (dobeep_msg("strlcat error"));
93617c1e8f1Slum 		} else {
93717c1e8f1Slum 			if (p[0] == '"' && p[strlen(p) - 1] == '"' ) {
93817c1e8f1Slum 				p[strlen(p) - 1] = '\0';
93917c1e8f1Slum 				p = p + 1;
94017c1e8f1Slum 			}
94117c1e8f1Slum 			if (strlcat(f, p, sizof) >= sizof)
94217c1e8f1Slum 				return (dobeep_msg("strlcat error"));
94317c1e8f1Slum 		}
94417c1e8f1Slum 		if (fin)
94517c1e8f1Slum 			break;
94617c1e8f1Slum 		vendp++;
94717c1e8f1Slum 		if (*vendp == '\0')
94817c1e8f1Slum 			break;
94917c1e8f1Slum 		p = skipwhite(vendp);
95017c1e8f1Slum 	}
95117c1e8f1Slum 
95217c1e8f1Slum 	(void)snprintf(dobuf, dosiz, "\"%s\"", f);
95317c1e8f1Slum 
95417c1e8f1Slum 	return (TRUE);
95517c1e8f1Slum }
95617c1e8f1Slum 
95717c1e8f1Slum Index: main.c
95817c1e8f1Slum ===================================================================
95917c1e8f1Slum RCS file: /cvs/src/usr.bin/mg/main.c,v
96017c1e8f1Slum retrieving revision 1.89
96117c1e8f1Slum diff -u -p -u -p -r1.89 main.c
96217c1e8f1Slum --- main.c      20 Mar 2021 09:00:49 -0000      1.89
96317c1e8f1Slum +++ main.c      12 Apr 2021 17:58:52 -0000
96417c1e8f1Slum @@ -133,10 +133,12 @@ main(int argc, char **argv)
96517c1e8f1Slum                 extern void grep_init(void);
96617c1e8f1Slum                 extern void cmode_init(void);
96717c1e8f1Slum                 extern void dired_init(void);
96817c1e8f1Slum +               extern void ifunmap_init(void);
96917c1e8f1Slum 
97017c1e8f1Slum                 dired_init();
97117c1e8f1Slum                 grep_init();
97217c1e8f1Slum                 cmode_init();
97317c1e8f1Slum +               ifunmap_init();
97417c1e8f1Slum         }
97517c1e8f1Slum 
97617c1e8f1Slum 
97717c1e8f1Slum */
978