Changeset View
Changeset View
Standalone View
Standalone View
contrib/mg/interpreter.c
- This file was added.
/* $OpenBSD: interpreter.c,v 1.32 2021/05/12 11:13:23 lum Exp $ */ | |||||
/* | |||||
* This file is in the public domain. | |||||
* | |||||
* Author: Mark Lumsden <mark@showcomplex.com> | |||||
*/ | |||||
/* | |||||
* This file attempts to add some 'scripting' functionality into mg. | |||||
* | |||||
* The initial goal is to give mg the ability to use it's existing functions | |||||
* and structures in a linked-up way. Hopefully resulting in user definable | |||||
* functions. The syntax is 'scheme' like but currently it is not a scheme | |||||
* interpreter. | |||||
* | |||||
* At the moment there is no manual page reference to this file. The code below | |||||
* is liable to change, so use at your own risk! | |||||
* | |||||
* If you do want to do some testing, you can add some lines to your .mg file | |||||
* like: | |||||
* | |||||
* 1. Give multiple arguments to a function that usually would accept only one: | |||||
* (find-file "a.txt" "b.txt" "c.txt") | |||||
* | |||||
* 2. Define a single value variable: | |||||
* (define myfile "d.txt") | |||||
* | |||||
* 3. Define a list: | |||||
* (define myfiles(list "e.txt" "f.txt")) | |||||
* | |||||
* 4. Use the previously defined variable or list: | |||||
* (find-file myfiles) | |||||
* | |||||
* To do: | |||||
* 1. multiline parsing - currently only single lines supported. | |||||
* 2. parsing for '(' and ')' throughout whole string and evaluate correctly. | |||||
* 3. conditional execution. | |||||
* 4. have memory allocated dynamically for variable values. | |||||
* 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-] | |||||
* at the moment. | |||||
* 6. display line numbers with parsing errors. | |||||
* 7. oh so many things.... | |||||
* [...] | |||||
* n. implement user definable functions. | |||||
* | |||||
* Notes: | |||||
* - Currently calls to excline() from this file have the line length and | |||||
* line number set to zero. | |||||
* That's because excline() uses '\0' as the end of line indicator | |||||
* and only the call to foundparen() within excline() uses excline's 2nd | |||||
* and 3rd arguments. | |||||
* Importantly, any lines sent to there from here will not be | |||||
* coming back here. | |||||
*/ | |||||
#include <sys/queue.h> | |||||
#include <ctype.h> | |||||
#include <limits.h> | |||||
#include <regex.h> | |||||
#include <signal.h> | |||||
#include <stdio.h> | |||||
#include <stdlib.h> | |||||
#include <string.h> | |||||
#include "def.h" | |||||
#include "funmap.h" | |||||
#ifdef MGLOG | |||||
#include "kbd.h" | |||||
#include "log.h" | |||||
#endif | |||||
static int multiarg(char *, char *, int); | |||||
static int isvar(char **, char **, int); | |||||
/*static int dofunc(char **, char **, int);*/ | |||||
static int founddef(char *, int, int, int, int); | |||||
static int foundlst(char *, int, int, int); | |||||
static int expandvals(char *, char *, char *); | |||||
static int foundfun(char *, int); | |||||
static int doregex(char *, char *); | |||||
static void clearexp(void); | |||||
static int parse(char *, const char *, const char *, int, int, int, int); | |||||
static int parsdef(char *, const char *, const char *, int, int, int); | |||||
static int parsval(char *, const char *, const char *, int, int, int); | |||||
static int parsexp(char *, const char *, const char *, int, int, int); | |||||
static int exitinterpreter(char *, char *, int); | |||||
TAILQ_HEAD(exphead, expentry) ehead; | |||||
struct expentry { | |||||
TAILQ_ENTRY(expentry) eentry; | |||||
char *fun; /* The 1st string found between parens. */ | |||||
char funbuf[BUFSIZE]; | |||||
const char *par1; /* Parenthesis at start of string */ | |||||
const char *par2; /* Parenthesis at end of string */ | |||||
int expctr; /* An incremental counter:+1 for each exp */ | |||||
int blkid; /* Which block are we in? */ | |||||
}; | |||||
/* | |||||
* Structure for scheme keywords. | |||||
*/ | |||||
#define NUMSCHKEYS 4 | |||||
#define MAXLENSCHKEYS 17 /* 17 = longest keyword (16) + 1 */ | |||||
char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] = | |||||
{ | |||||
"define", | |||||
"list", | |||||
"if", | |||||
"lambda" | |||||
}; | |||||
static const char lp = '('; | |||||
static const char rp = ')'; | |||||
static char *defnam = NULL; | |||||
static int lnm; | |||||
/* | |||||
* Line has a '(' as the first non-white char. | |||||
* Do some very basic parsing of line. | |||||
* Multi-line not supported at the moment, To do. | |||||
*/ | |||||
int | |||||
foundparen(char *funstr, int llen, int lnum) | |||||
{ | |||||
const char *lrp = NULL; | |||||
char *p, *begp = NULL, *endp = NULL, *prechr; | |||||
char *lastchr = NULL; | |||||
int i, ret, pctr, expctr, blkid, inquote, esc; | |||||
int elen, spc, ns; | |||||
pctr = expctr = inquote = esc = elen = spc = ns = 0; | |||||
blkid = 1; | |||||
lnm = lnum; | |||||
/* | |||||
* load expressions into a list called 'expentry', to be processd | |||||
* when all are obtained. | |||||
* Not really live code at the moment. Just part of the process of | |||||
* working out what needs to be done. | |||||
*/ | |||||
TAILQ_INIT(&ehead); | |||||
/* | |||||
* Check for blocks of code with opening and closing (). | |||||
* One block = (cmd p a r a m) | |||||
* Two blocks = (cmd p a r a m s)(hola) | |||||
* Two blocks = (cmd p a r (list a m s))(hola) | |||||
* Only single line at moment, but more for multiline. | |||||
*/ | |||||
p = funstr; | |||||
for (i = 0; i < llen; ++i, p++) { | |||||
if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') { | |||||
if (*p == ')') | |||||
return(dobeep_num("Extra ')' found on line:", | |||||
lnm)); | |||||
return(dobeep_num("Error line:", lnm)); | |||||
} | |||||
if (begp != NULL) | |||||
elen++; | |||||
if (*p == '\\') { | |||||
esc = 1; | |||||
} else if (*p == '(') { | |||||
if (lastchr != NULL && *lastchr == '(') | |||||
return(dobeep_num("Multiple consecutive "\ | |||||
"left parantheses line", lnm)); | |||||
if (inquote == 0) { | |||||
if (begp != NULL) { | |||||
if (*prechr == ' ') | |||||
ns--; | |||||
if (endp == NULL) | |||||
*p = '\0'; | |||||
else | |||||
*endp = '\0'; | |||||
ret = parse(begp, lrp, &lp, blkid, | |||||
++expctr, elen - spc, ns); | |||||
if (!ret) { | |||||
cleanup(); | |||||
return(ret); | |||||
} | |||||
elen = 0; | |||||
} | |||||
lrp = &lp; | |||||
begp = endp = NULL; | |||||
pctr++; | |||||
} else if (inquote != 1) { | |||||
cleanup(); | |||||
return(dobeep_num("Opening and closing quote "\ | |||||
"char error line:", lnm)); | |||||
} | |||||
esc = spc = 0; | |||||
} else if (*p == ')') { | |||||
if (lastchr != NULL && *lastchr == '(') | |||||
return(dobeep_num("Empty parenthesis "\ | |||||
"not supported line", lnm)); | |||||
if (inquote == 0) { | |||||
if (begp != NULL) { | |||||
if (*prechr == ' ') | |||||
ns--; | |||||
if (endp == NULL) | |||||
*p = '\0'; | |||||
else | |||||
*endp = '\0'; | |||||
ret = parse(begp, lrp, &rp, blkid, | |||||
++expctr, elen - spc, ns); | |||||
if (!ret) { | |||||
cleanup(); | |||||
return(ret); | |||||
} | |||||
elen = 0; | |||||
} | |||||
lrp = &rp; | |||||
begp = endp = NULL; | |||||
pctr--; | |||||
} else if (inquote != 1) { | |||||
cleanup(); | |||||
return(dobeep_num("Opening and closing quote "\ | |||||
"char error line:", lnm)); | |||||
} | |||||
esc = spc = 0; | |||||
} else if (*p != ' ' && *p != '\t') { | |||||
if (begp == NULL) { | |||||
begp = p; | |||||
if (*begp == '"' || isdigit(*begp)) | |||||
return(dobeep_num("First char of "\ | |||||
"expression error line:", lnm)); | |||||
} | |||||
if (*p == '"') { | |||||
if (inquote == 0 && esc == 0) { | |||||
if (*prechr != ' ' && *prechr != '\t') | |||||
return(dobeep_num("Parse error"\ | |||||
" line:", lnm)); | |||||
inquote++; | |||||
} else if (inquote > 0 && esc == 1) | |||||
esc = 0; | |||||
else | |||||
inquote--; | |||||
} else if (*prechr == '"' && inquote == 0) { | |||||
return(dobeep_num("Parse error line:", lnm)); | |||||
} | |||||
endp = NULL; | |||||
spc = 0; | |||||
} else if (endp == NULL && (*p == ' ' || *p == '\t')) { | |||||
if (inquote == 0) { | |||||
*p = ' '; | |||||
endp = p; | |||||
spc++; | |||||
if (begp != NULL) | |||||
ns++; | |||||
} | |||||
esc = 0; | |||||
} else if (*p == '\t' || *p == ' ') { | |||||
if (inquote == 0) { | |||||
*p = ' '; | |||||
spc++; | |||||
} | |||||
esc = 0; | |||||
} | |||||
if (*p != '\t' && *p != ' ' && inquote == 0) | |||||
lastchr = p; | |||||
if (pctr == 0) { | |||||
blkid++; | |||||
expctr = 0; | |||||
defnam = NULL; | |||||
} | |||||
prechr = p; | |||||
} | |||||
if (pctr != 0) { | |||||
cleanup(); | |||||
return(dobeep_num("Opening and closing parentheses error line:", | |||||
lnm)); | |||||
} | |||||
if (ret == FALSE) | |||||
cleanup(); | |||||
else | |||||
clearexp(); /* leave lists but remove expressions */ | |||||
return (ret); | |||||
} | |||||
static int | |||||
parse(char *begp, const char *par1, const char *par2, int blkid, int expctr, | |||||
int elen, int ns) | |||||
{ | |||||
char *regs; | |||||
int ret = FALSE; | |||||
if (strncmp(begp, "define", 6) == 0) { | |||||
ret = parsdef(begp, par1, par2, blkid, expctr, elen); | |||||
if (ret == TRUE || ret == FALSE) | |||||
return (ret); | |||||
} else if (strncmp(begp, "list", 4) == 0) | |||||
return(parsval(begp, par1, par2, blkid, expctr, elen)); | |||||
regs = "^exit$"; | |||||
if (doregex(regs, begp)) | |||||
return(exitinterpreter(NULL, NULL, FALSE)); | |||||
/* mg function name regex */ | |||||
regs = "^[A-Za-z-]+$"; | |||||
if (doregex(regs, begp)) | |||||
return(excline(begp, 0, 0)); | |||||
/* Corner case 1 */ | |||||
if (strncmp(begp, "global-set-key ", 15) == 0) | |||||
/* function name as 2nd param screws up multiarg. */ | |||||
return(excline(begp, 0, 0)); | |||||
/* Corner case 2 */ | |||||
if (strncmp(begp, "define-key ", 11) == 0) | |||||
/* function name as 3rd param screws up multiarg. */ | |||||
return(excline(begp, 0, 0)); | |||||
return (parsexp(begp, par1, par2, blkid, expctr, elen)); | |||||
} | |||||
static int | |||||
parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr, | |||||
int elen) | |||||
{ | |||||
char *regs; | |||||
if ((defnam == NULL) && (expctr != 1)) | |||||
return(dobeep_num("'define' incorrectly used line:", lnm)); | |||||
/* Does the line have a incorrect variable 'define' like: */ | |||||
/* (define i y z) */ | |||||
regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$"; | |||||
if (doregex(regs, begp)) | |||||
return(dobeep_num("Invalid use of define line:", lnm)); | |||||
/* Does the line have a single variable 'define' like: */ | |||||
/* (define i 0) */ | |||||
regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$"; | |||||
if (doregex(regs, begp)) { | |||||
if (par1 == &lp && par2 == &rp && expctr == 1) | |||||
return(founddef(begp, blkid, expctr, 1, elen)); | |||||
return(dobeep_num("Invalid use of define line:", lnm)); | |||||
} | |||||
/* Does the line have '(define i(' */ | |||||
regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$"; | |||||
if (doregex(regs, begp)) { | |||||
if (par1 == &lp && par2 == &lp && expctr == 1) | |||||
return(founddef(begp, blkid, expctr, 0, elen)); | |||||
return(dobeep_num("Invalid use of 'define' line:", lnm)); | |||||
} | |||||
/* Does the line have '(define (' */ | |||||
regs = "^define$"; | |||||
if (doregex(regs, begp)) { | |||||
if (par1 == &lp && par2 == &lp && expctr == 1) | |||||
return(foundfun(begp, expctr)); | |||||
return(dobeep_num("Invalid use of 'define' line:", lnm)); | |||||
} | |||||
return (ABORT); | |||||
} | |||||
static int | |||||
parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr, | |||||
int elen) | |||||
{ | |||||
char *regs; | |||||
/* Does the line have 'list' */ | |||||
regs = "^list$"; | |||||
if (doregex(regs, begp)) | |||||
return(dobeep_num("Invalid use of list line:", lnm)); | |||||
/* Does the line have a 'list' like: */ | |||||
/* (list "a" "b") */ | |||||
regs = "^list[ ]+.*$"; | |||||
if (doregex(regs, begp)) { | |||||
if (expctr == 1) | |||||
return(dobeep_num("list with no-where to go.", lnm)); | |||||
if (par1 == &lp && expctr > 1) | |||||
return(foundlst(begp, blkid, expctr, elen)); | |||||
return(dobeep_num("Invalid use of list line:", lnm)); | |||||
} | |||||
return (FALSE); | |||||
} | |||||
static int | |||||
parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr, | |||||
int elen) | |||||
{ | |||||
struct expentry *e1 = NULL; | |||||
PF funcp; | |||||
char *cmdp, *fendp, *valp, *fname, *funb = NULL;; | |||||
int numparams, ret; | |||||
cmdp = begp; | |||||
fendp = strchr(cmdp, ' '); | |||||
*fendp = '\0'; | |||||
/* | |||||
* If no extant mg command found, just return. | |||||
*/ | |||||
if ((funcp = name_function(cmdp)) == NULL) | |||||
return (dobeep_msgs("Unknown command: ", cmdp)); | |||||
numparams = numparams_function(funcp); | |||||
if (numparams == 0) | |||||
return (dobeep_msgs("Command takes no arguments:", cmdp)); | |||||
if (numparams == -1) | |||||
return (dobeep_msgs("Interactive command found:", cmdp)); | |||||
if ((e1 = malloc(sizeof(struct expentry))) == NULL) { | |||||
cleanup(); | |||||
return (dobeep_msg("malloc Error")); | |||||
} | |||||
TAILQ_INSERT_HEAD(&ehead, e1, eentry); | |||||
if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) { | |||||
cleanup(); | |||||
return(dobeep_msg("strndup error")); | |||||
} | |||||
cmdp = e1->fun; | |||||
fname = e1->fun; | |||||
e1->funbuf[0] = '\0'; | |||||
funb = e1->funbuf; | |||||
e1->expctr = expctr; | |||||
e1->blkid = blkid; | |||||
/* need to think about these two */ | |||||
e1->par1 = par1; | |||||
e1->par2 = par2; | |||||
*fendp = ' '; | |||||
valp = fendp + 1; | |||||
ret = expandvals(cmdp, valp, funb); | |||||
if (!ret) | |||||
return (ret); | |||||
return (multiarg(fname, funb, numparams)); | |||||
} | |||||
/* | |||||
* Pass a list of arguments to a function. | |||||
*/ | |||||
static int | |||||
multiarg(char *cmdp, char *argbuf, int numparams) | |||||
{ | |||||
char excbuf[BUFSIZE]; | |||||
char *argp, *p, *s = " "; | |||||
char *regs; | |||||
int spc, numspc; | |||||
int fin, inquote; | |||||
argp = argbuf; | |||||
spc = 1; /* initially fake a space so we find first argument */ | |||||
numspc = fin = inquote = 0; | |||||
for (p = argbuf; *p != '\0'; p++) { | |||||
if (*(p + 1) == '\0') | |||||
fin = 1; | |||||
if (*p != ' ') { | |||||
if (*p == '"') { | |||||
if (inquote == 1) | |||||
inquote = 0; | |||||
else | |||||
inquote = 1; | |||||
} | |||||
if (spc == 1) | |||||
if ((numspc % numparams) == 0) { | |||||
argp = p; | |||||
} | |||||
spc = 0; | |||||
} | |||||
if ((*p == ' ' && inquote == 0) || fin) { | |||||
if (spc == 1)/* || (numspc % numparams == 0))*/ | |||||
continue; | |||||
if ((numspc % numparams) != (numparams - 1)) { | |||||
numspc++; | |||||
continue; | |||||
} | |||||
if (*p == ' ') { | |||||
*p = '\0'; /* terminate arg string */ | |||||
} | |||||
excbuf[0] = '\0'; | |||||
regs = "[\"]+.*[\"]+"; | |||||
if (!doregex(regs, argp)) { | |||||
const char *errstr; | |||||
int iters; | |||||
iters = strtonum(argp, 0, INT_MAX, &errstr); | |||||
if (errstr != NULL) | |||||
return (dobeep_msgs("Var not found:", | |||||
argp)); | |||||
} | |||||
if (strlcpy(excbuf, cmdp, sizeof(excbuf)) | |||||
>= sizeof(excbuf)) | |||||
return (dobeep_msg("strlcpy error")); | |||||
if (strlcat(excbuf, s, sizeof(excbuf)) | |||||
>= sizeof(excbuf)) | |||||
return (dobeep_msg("strlcat error")); | |||||
if (strlcat(excbuf, argp, sizeof(excbuf)) | |||||
>= sizeof(excbuf)) | |||||
return (dobeep_msg("strlcat error")); | |||||
excline(excbuf, 0, 0); | |||||
if (fin) | |||||
break; | |||||
*p = ' '; /* unterminate arg string */ | |||||
numspc++; | |||||
spc = 1; | |||||
} | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Is an item a value or a variable? | |||||
*/ | |||||
static int | |||||
isvar(char **argp, char **varbuf, int sizof) | |||||
{ | |||||
struct varentry *v1 = NULL; | |||||
if (SLIST_EMPTY(&varhead)) | |||||
return (FALSE); | |||||
#ifdef MGLOG | |||||
mglog_isvar(*varbuf, *argp, sizof); | |||||
#endif | |||||
SLIST_FOREACH(v1, &varhead, entry) { | |||||
if (strcmp(*argp, v1->v_name) == 0) { | |||||
(void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof); | |||||
return (TRUE); | |||||
} | |||||
} | |||||
return (FALSE); | |||||
} | |||||
static int | |||||
foundfun(char *defstr, int expctr) | |||||
{ | |||||
return (TRUE); | |||||
} | |||||
static int | |||||
foundlst(char *defstr, int blkid, int expctr, int elen) | |||||
{ | |||||
char *p; | |||||
p = strstr(defstr, " "); | |||||
p = skipwhite(p); | |||||
expandvals(NULL, p, defnam); | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* 'define' strings follow the regex in parsdef(). | |||||
*/ | |||||
static int | |||||
founddef(char *defstr, int blkid, int expctr, int hasval, int elen) | |||||
{ | |||||
struct varentry *vt, *v1 = NULL; | |||||
char *p, *vnamep, *vendp = NULL, *valp; | |||||
p = strstr(defstr, " "); /* move to first ' ' char. */ | |||||
vnamep = skipwhite(p); /* find first char of var name. */ | |||||
vendp = vnamep; | |||||
/* now find the end of the define/list name */ | |||||
while (1) { | |||||
++vendp; | |||||
if (*vendp == ' ') | |||||
break; | |||||
} | |||||
*vendp = '\0'; | |||||
/* | |||||
* Check list name is not an existing mg function. | |||||
*/ | |||||
if (name_function(vnamep) != NULL) | |||||
return(dobeep_msgs("Variable/function name clash:", vnamep)); | |||||
if (!SLIST_EMPTY(&varhead)) { | |||||
SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) { | |||||
if (strcmp(vnamep, v1->v_name) == 0) | |||||
SLIST_REMOVE(&varhead, v1, varentry, entry); | |||||
} | |||||
} | |||||
if ((v1 = malloc(sizeof(struct varentry))) == NULL) | |||||
return (ABORT); | |||||
SLIST_INSERT_HEAD(&varhead, v1, entry); | |||||
if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL) | |||||
return(dobeep_msg("strndup error")); | |||||
vnamep = v1->v_name; | |||||
v1->v_count = 0; | |||||
v1->v_vals = NULL; | |||||
v1->v_buf[0] = '\0'; | |||||
defnam = v1->v_buf; | |||||
if (hasval) { | |||||
valp = skipwhite(vendp + 1); | |||||
expandvals(NULL, valp, defnam); | |||||
defnam = NULL; | |||||
} | |||||
*vendp = ' '; | |||||
return (TRUE); | |||||
} | |||||
static int | |||||
expandvals(char *cmdp, char *valp, char *bp) | |||||
{ | |||||
char excbuf[BUFSIZE], argbuf[BUFSIZE]; | |||||
char contbuf[BUFSIZE], varbuf[BUFSIZE]; | |||||
char *argp, *endp, *p, *v, *s = " "; | |||||
char *regs; | |||||
int spc, cnt; | |||||
int inlist, sizof, fin, inquote; | |||||
/* now find the first argument */ | |||||
p = skipwhite(valp); | |||||
if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf)) | |||||
return (dobeep_msg("strlcpy error")); | |||||
argp = argbuf; | |||||
spc = 1; /* initially fake a space so we find first argument */ | |||||
inlist = fin = inquote = cnt = spc = 0; | |||||
for (p = argbuf; *p != '\0'; p++) { | |||||
if (*(p + 1) == '\0') | |||||
fin = 1; | |||||
if (*p != ' ') { | |||||
if (*p == '"') { | |||||
if (inquote == 1) | |||||
inquote = 0; | |||||
else | |||||
inquote = 1; | |||||
} | |||||
if (spc == 1) | |||||
argp = p; | |||||
spc = 0; | |||||
} | |||||
if ((*p == ' ' && inquote == 0) || fin) { | |||||
if (spc == 1) | |||||
continue; | |||||
/* terminate arg string */ | |||||
if (*p == ' ') { | |||||
*p = '\0'; | |||||
} | |||||
endp = p + 1; | |||||
excbuf[0] = '\0'; | |||||
varbuf[0] = '\0'; | |||||
contbuf[0] = '\0'; | |||||
sizof = sizeof(varbuf); | |||||
v = varbuf; | |||||
regs = "[\"]+.*[\"]+"; | |||||
if (doregex(regs, argp)) | |||||
; /* found quotes */ | |||||
else if (isvar(&argp, &v, sizof)) { | |||||
(void)(strlcat(varbuf, " ", | |||||
sizof) >= sizof); | |||||
*p = ' '; | |||||
(void)(strlcpy(contbuf, endp, | |||||
sizeof(contbuf)) >= sizeof(contbuf)); | |||||
(void)(strlcat(varbuf, contbuf, | |||||
sizof) >= sizof); | |||||
argbuf[0] = ' '; | |||||
argbuf[1] = '\0'; | |||||
(void)(strlcat(argbuf, varbuf, | |||||
sizof) >= sizof); | |||||
p = argp = argbuf; | |||||
spc = 1; | |||||
fin = 0; | |||||
continue; | |||||
} else { | |||||
const char *errstr; | |||||
int iters; | |||||
iters = strtonum(argp, 0, INT_MAX, &errstr); | |||||
if (errstr != NULL) | |||||
return (dobeep_msgs("Var not found:", | |||||
argp)); | |||||
} | |||||
#ifdef MGLOG | |||||
mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE); | |||||
#endif | |||||
if (*bp != '\0') { | |||||
if (strlcat(bp, s, BUFSIZE) >= BUFSIZE) | |||||
return (dobeep_msg("strlcat error")); | |||||
} | |||||
if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) { | |||||
return (dobeep_msg("strlcat error")); | |||||
} | |||||
/* v1->v_count++;*/ | |||||
if (fin) | |||||
break; | |||||
*p = ' '; /* unterminate arg string */ | |||||
spc = 1; | |||||
} | |||||
} | |||||
return (TRUE); | |||||
} | |||||
/* | |||||
* Finished with buffer evaluation, so clean up any vars. | |||||
* Perhaps keeps them in mg even after use,... | |||||
*/ | |||||
/*static int | |||||
clearvars(void) | |||||
{ | |||||
struct varentry *v1 = NULL; | |||||
while (!SLIST_EMPTY(&varhead)) { | |||||
v1 = SLIST_FIRST(&varhead); | |||||
SLIST_REMOVE_HEAD(&varhead, entry); | |||||
free(v1->v_name); | |||||
free(v1); | |||||
} | |||||
return (FALSE); | |||||
} | |||||
*/ | |||||
/* | |||||
* Finished with block evaluation, so clean up any expressions. | |||||
*/ | |||||
static void | |||||
clearexp(void) | |||||
{ | |||||
struct expentry *e1 = NULL; | |||||
while (!TAILQ_EMPTY(&ehead)) { | |||||
e1 = TAILQ_FIRST(&ehead); | |||||
TAILQ_REMOVE(&ehead, e1, eentry); | |||||
free(e1->fun); | |||||
free(e1); | |||||
} | |||||
return; | |||||
} | |||||
/* | |||||
* Cleanup before leaving. | |||||
*/ | |||||
void | |||||
cleanup(void) | |||||
{ | |||||
defnam = NULL; | |||||
clearexp(); | |||||
/* clearvars();*/ | |||||
} | |||||
/* | |||||
* Test a string against a regular expression. | |||||
*/ | |||||
static int | |||||
doregex(char *r, char *e) | |||||
{ | |||||
regex_t regex_buff; | |||||
if (regcomp(®ex_buff, r, REG_EXTENDED)) { | |||||
regfree(®ex_buff); | |||||
return(dobeep_num("Regex compilation error line:", lnm)); | |||||
} | |||||
if (!regexec(®ex_buff, e, 0, NULL, 0)) { | |||||
regfree(®ex_buff); | |||||
return(TRUE); | |||||
} | |||||
regfree(®ex_buff); | |||||
return(FALSE); | |||||
} | |||||
/* | |||||
* Display a message so it is apparent that this is the method which stopped | |||||
* execution. | |||||
*/ | |||||
static int | |||||
exitinterpreter(char *ptr, char *dobuf, int dosiz) | |||||
{ | |||||
cleanup(); | |||||
if (batch == 0) | |||||
return(dobeep_msg("Interpreter exited via exit command.")); | |||||
return(FALSE); | |||||
} | |||||
/* | |||||
* All code below commented out (until end of file). | |||||
* | |||||
* Need to think about how interpreter functions are done. | |||||
* Probably don't have a choice with string-append(). | |||||
static int getenvironmentvariable(char *, char *, int); | |||||
static int stringappend(char *, char *, int); | |||||
typedef int (*PFI)(char *, char *, int); | |||||
struct ifunmap { | |||||
PFI fn_funct; | |||||
const char *fn_name; | |||||
struct ifunmap *fn_next; | |||||
}; | |||||
static struct ifunmap *ifuns; | |||||
static struct ifunmap ifunctnames[] = { | |||||
{exitinterpreter, "exit"}, | |||||
{getenvironmentvariable, "get-environment-variable"}, | |||||
{stringappend, "string-append"}, | |||||
{NULL, NULL} | |||||
}; | |||||
void | |||||
ifunmap_init(void) | |||||
{ | |||||
struct ifunmap *fn; | |||||
for (fn = ifunctnames; fn->fn_name != NULL; fn++) { | |||||
fn->fn_next = ifuns; | |||||
ifuns = fn; | |||||
} | |||||
} | |||||
PFI | |||||
name_ifun(const char *ifname) | |||||
{ | |||||
struct ifunmap *fn; | |||||
for (fn = ifuns; fn != NULL; fn = fn->fn_next) { | |||||
if (strcmp(fn->fn_name, ifname) == 0) | |||||
return (fn->fn_funct); | |||||
} | |||||
return (NULL); | |||||
} | |||||
int | |||||
dofunc(char **ifname, char **tmpbuf, int sizof) | |||||
{ | |||||
PFI fnc; | |||||
char *p, *tmp; | |||||
p = strstr(*ifname, " "); | |||||
*p = '\0'; | |||||
fnc = name_ifun(*ifname); | |||||
if (fnc == NULL) | |||||
return (FALSE); | |||||
*p = ' '; | |||||
tmp = *tmpbuf; | |||||
fnc(p, tmp, sizof); | |||||
return (TRUE); | |||||
} | |||||
static int | |||||
getenvironmentvariable(char *ptr, char *dobuf, int dosiz) | |||||
{ | |||||
char *t; | |||||
char *tmp; | |||||
const char *q = "\""; | |||||
t = skipwhite(ptr); | |||||
if (t[0] == *q || t[strlen(t) - 1] == *q) | |||||
return (dobeep_msgs("Please remove '\"' around:", t)); | |||||
if ((tmp = getenv(t)) == NULL || *tmp == '\0') | |||||
return(dobeep_msgs("Envar not found:", t)); | |||||
dobuf[0] = '\0'; | |||||
if (strlcat(dobuf, q, dosiz) >= dosiz) | |||||
return (dobeep_msg("strlcat error")); | |||||
if (strlcat(dobuf, tmp, dosiz) >= dosiz) | |||||
return (dobeep_msg("strlcat error")); | |||||
if (strlcat(dobuf, q, dosiz) >= dosiz) | |||||
return (dobeep_msg("strlcat error")); | |||||
return (TRUE); | |||||
} | |||||
static int | |||||
stringappend(char *ptr, char *dobuf, int dosiz) | |||||
{ | |||||
char varbuf[BUFSIZE], funbuf[BUFSIZE]; | |||||
char *p, *f, *v, *vendp; | |||||
int sizof, fin = 0; | |||||
varbuf[0] = funbuf[0] = '\0'; | |||||
f = funbuf; | |||||
v = varbuf; | |||||
sizof = sizeof(varbuf); | |||||
*dobuf = '\0'; | |||||
p = skipwhite(ptr); | |||||
while (*p != '\0') { | |||||
vendp = p; | |||||
while (1) { | |||||
if (*vendp == ' ') { | |||||
break; | |||||
} else if (*vendp == '\0') { | |||||
fin = 1; | |||||
break; | |||||
} | |||||
++vendp; | |||||
} | |||||
*vendp = '\0'; | |||||
if (isvar(&p, &v, sizof)) { | |||||
if (v[0] == '"' && v[strlen(v) - 1] == '"' ) { | |||||
v[strlen(v) - 1] = '\0'; | |||||
v = v + 1; | |||||
} | |||||
if (strlcat(f, v, sizof) >= sizof) | |||||
return (dobeep_msg("strlcat error")); | |||||
} else { | |||||
if (p[0] == '"' && p[strlen(p) - 1] == '"' ) { | |||||
p[strlen(p) - 1] = '\0'; | |||||
p = p + 1; | |||||
} | |||||
if (strlcat(f, p, sizof) >= sizof) | |||||
return (dobeep_msg("strlcat error")); | |||||
} | |||||
if (fin) | |||||
break; | |||||
vendp++; | |||||
if (*vendp == '\0') | |||||
break; | |||||
p = skipwhite(vendp); | |||||
} | |||||
(void)snprintf(dobuf, dosiz, "\"%s\"", f); | |||||
return (TRUE); | |||||
} | |||||
Index: main.c | |||||
=================================================================== | |||||
RCS file: /cvs/src/usr.bin/mg/main.c,v | |||||
retrieving revision 1.89 | |||||
diff -u -p -u -p -r1.89 main.c | |||||
--- main.c 20 Mar 2021 09:00:49 -0000 1.89 | |||||
+++ main.c 12 Apr 2021 17:58:52 -0000 | |||||
@@ -133,10 +133,12 @@ main(int argc, char **argv) | |||||
extern void grep_init(void); | |||||
extern void cmode_init(void); | |||||
extern void dired_init(void); | |||||
+ extern void ifunmap_init(void); | |||||
dired_init(); | |||||
grep_init(); | |||||
cmode_init(); | |||||
+ ifunmap_init(); | |||||
} | |||||
*/ |