version 1.9, 2021/03/08 20:01:43 |
version 1.10, 2021/03/20 19:39:30 |
|
|
* 1. multiline parsing - currently only single lines supported. |
* 1. multiline parsing - currently only single lines supported. |
* 2. parsing for '(' and ')' throughout whole string and evaluate correctly. |
* 2. parsing for '(' and ')' throughout whole string and evaluate correctly. |
* 3. conditional execution. |
* 3. conditional execution. |
* 4. define single value variables (define i 0) |
* 4. deal with quotes around a string: "x x" |
* 5. deal with quotes around a string: "x x" |
* 5. oh so many things.... |
* 6. oh so many things.... |
|
* [...] |
* [...] |
* n. implement user definable functions. |
* n. implement user definable functions. |
*/ |
*/ |
|
|
static int isvar(char **, char **, int); |
static int isvar(char **, char **, int); |
static int foundvar(char *); |
static int foundvar(char *); |
static int doregex(char *, char *); |
static int doregex(char *, char *); |
|
static int parseexp(char *); |
|
static void clearexp(void); |
|
|
|
struct expentry { |
|
SLIST_ENTRY(expentry) eentry; |
|
char *exp; /* The string found between paraenthesis. */ |
|
int par1; /* Parenthesis at start of string (=1 */ |
|
int par2; /* Parenthesis at end of string )=2 */ |
|
int expctr; /* An incremental counter:+1 for each exp */ |
|
int blkid; /* Which block are we in? */ |
|
}; |
|
SLIST_HEAD(elisthead, expentry) exphead = SLIST_HEAD_INITIALIZER(exphead); |
|
|
/* |
/* |
* Structure for variables during buffer evaluation. |
* Structure for variables during buffer evaluation. |
*/ |
*/ |
|
|
SLIST_HEAD(vlisthead, varentry) varhead = SLIST_HEAD_INITIALIZER(varhead); |
SLIST_HEAD(vlisthead, varentry) varhead = SLIST_HEAD_INITIALIZER(varhead); |
|
|
/* |
/* |
|
* 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) |
|
{ |
|
struct expentry *e1 = NULL; |
|
char *p, *valp, *endp = NULL, *regs; |
|
char expbuf[BUFSIZE], tmpbuf[BUFSIZE]; |
|
int ret, pctr, fndstart, expctr, blkid, fndchr, fndend; |
|
|
|
pctr = fndstart = expctr = fndchr = fndend = 0; |
|
blkid = 1; |
|
/* |
|
* 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; |
|
|
|
/* |
|
* Currently can't do () or (( at the moment, |
|
* just drop out - stops a segv. TODO. |
|
*/ |
|
regs = "[(]+[\t ]*[)]+"; |
|
if (doregex(regs, funstr)) |
|
return(dobeep_msg("Empty lists not supported at moment")); |
|
regs = "[(]+[\t ]*[(]+"; |
|
if (doregex(regs, funstr)) |
|
return(dobeep_msg("Multiple left parantheses found")); |
|
/* |
|
* 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. |
|
*/ |
|
while (*p != '\0') { |
|
if (*p == '(') { |
|
if (fndstart == 1) { |
|
if (endp == NULL) |
|
*p = '\0'; |
|
else |
|
*endp = '\0'; |
|
e1->par2 = 1; |
|
if ((e1->exp = strndup(valp, BUFSIZE)) == NULL) |
|
return(dobeep_msg("strndup error")); |
|
} |
|
if ((e1 = malloc(sizeof(struct expentry))) == NULL) |
|
return (dobeep_msg("malloc Error")); |
|
SLIST_INSERT_HEAD(&exphead, e1, eentry); |
|
e1->exp = NULL; |
|
e1->expctr = ++expctr; |
|
e1->blkid = blkid; |
|
e1->par1 = 1; |
|
fndstart = 1; |
|
fndend = 0; |
|
fndchr = 0; |
|
endp = NULL; |
|
pctr++; |
|
} else if (*p == ')') { |
|
if (endp == NULL) |
|
*p = '\0'; |
|
else |
|
*endp = '\0'; |
|
if ((e1->exp = strndup(valp, BUFSIZE)) == NULL) |
|
return(dobeep_msg("strndup error")); |
|
fndstart = 0; |
|
pctr--; |
|
} else if (*p != ' ' && *p != '\t') { |
|
if (fndchr == 0) { |
|
valp = p; |
|
fndchr = 1; |
|
} |
|
fndend = 0; |
|
endp = NULL; |
|
} else if (fndend == 0 && (*p == ' ' || *p == '\t')) { |
|
*p = ' '; |
|
fndend = 1; |
|
endp = p; |
|
} else if (*p == '\t') /* need to check not between "" */ |
|
*p = ' '; |
|
if (pctr == 0) |
|
blkid++; |
|
p++; |
|
} |
|
expbuf[0] = tmpbuf[0] = '\0'; |
|
|
|
/* |
|
* Join expressions together for the moment, to progess. |
|
* This needs to be totally redone and |
|
* iterate in-to-out, evaluating as we go. Eventually. |
|
*/ |
|
SLIST_FOREACH(e1, &exphead, eentry) { |
|
if (strlcpy(tmpbuf, expbuf, sizeof(tmpbuf)) >= sizeof(tmpbuf)) |
|
return (dobeep_msg("strlcpy error")); |
|
expbuf[0] = '\0'; |
|
if (strlcpy(expbuf, e1->exp, sizeof(expbuf)) >= sizeof(expbuf)) |
|
return (dobeep_msg("strlcat error")); |
|
if (*tmpbuf != '\0') |
|
if (strlcat(expbuf, " ", sizeof(expbuf)) >= |
|
sizeof(expbuf)) |
|
return (dobeep_msg("strlcat error")); |
|
if (strlcat(expbuf, tmpbuf, sizeof(expbuf)) >= sizeof(expbuf)) |
|
return (dobeep_msg("strlcat error")); |
|
#ifdef MGLOG |
|
mglog_misc("exp|%s|\n", e1->exp); |
|
#endif |
|
} |
|
if (pctr != 0) { |
|
clearexp(); |
|
return(dobeep_msg("Opening and closing parentheses error")); |
|
} |
|
|
|
ret = parseexp(expbuf); |
|
clearexp(); |
|
|
|
return (ret); |
|
} |
|
|
|
/* |
|
* At the moment, only paring list defines. Much more to do. |
|
*/ |
|
static int |
|
parseexp(char *funstr) |
|
{ |
|
char *regs; |
|
|
|
/* Does the line have a list 'define' like: */ |
|
/* (define alist(list 1 2 3 4)) */ |
|
regs = "^define[ ]+.*[ ]+list[ ]+.*[ ]*"; |
|
if (doregex(regs, funstr)) |
|
return(foundvar(funstr)); |
|
|
|
/* Does the line have a incorrect variable 'define' like: */ |
|
/* (define i y z) */ |
|
regs = "^define[ ]+.*[ ]+.*[ ]+.*$"; |
|
if (doregex(regs, funstr)) |
|
return(dobeep_msg("Invalid use of define.")); |
|
|
|
/* Does the line have a single variable 'define' like: */ |
|
/* (define i 0) */ |
|
regs = "^define[ ]+.*[ ]+.*$"; |
|
if (doregex(regs, funstr)) |
|
return(foundvar(funstr)); |
|
|
|
/* Does the line have an unrecognised 'define' */ |
|
regs = "^define[\t ]+"; |
|
if (doregex(regs, funstr)) |
|
return(dobeep_msg("Invalid use of define")); |
|
|
|
return(multiarg(funstr)); |
|
} |
|
|
|
/* |
* Pass a list of arguments to a function. |
* Pass a list of arguments to a function. |
*/ |
*/ |
static int |
static int |
multiarg(char *funstr) |
multiarg(char *funstr) |
{ |
{ |
PF funcp; |
PF funcp; |
char excbuf[BUFSIZE], argbuf[BUFSIZE], *contbuf, tmpbuf[BUFSIZE]; |
char excbuf[BUFSIZE], argbuf[BUFSIZE]; |
char *cmdp, *argp, *fendp, *endp, *p, *t, *s = " "; |
char contbuf[BUFSIZE], varbuf[BUFSIZE]; |
int singlecmd = 0, spc, numparams, numspc; |
char *cmdp = NULL, *argp, *fendp = NULL, *endp, *p, *v, *s = " "; |
int inlist, foundlst = 0, eolst, rpar, sizof, fin; |
int spc, numparams, numspc; |
|
int inlist, sizof, fin; |
|
|
contbuf = NULL; |
if (doregex("^[A-Za-z-]+$", funstr)) |
endp = strrchr(funstr, ')'); |
return(excline(funstr)); |
if (endp == NULL) { |
|
ewprintf("No closing parenthesis found"); |
|
return(FALSE); |
|
} |
|
p = endp + 1; |
|
if (*p != '\0') |
|
*p = '\0'; |
|
/* we now know that string starts with '(' and ends with ')' */ |
|
if (doregex("^[(][\t ]*[)]$", funstr)) |
|
return(dobeep_msg("No command found")); |
|
|
|
if (doregex("^[(][\t ]*[A-Za-z-]+[\t ]*[)]$", funstr)) |
cmdp = funstr; |
singlecmd = 1; |
fendp = strchr(cmdp, ' '); |
|
|
p = funstr + 1; /* move past first '(' char. */ |
|
cmdp = skipwhite(p); /* find first char of command. */ |
|
|
|
if (singlecmd) { |
|
/* remove ')', then check for spaces at the end */ |
|
cmdp[strlen(cmdp) - 1] = '\0'; |
|
if ((fendp = strchr(cmdp, ' ')) != NULL) |
|
*fendp = '\0'; |
|
else if ((fendp = strchr(cmdp, '\t')) != NULL) |
|
*fendp = '\0'; |
|
return(excline(cmdp)); |
|
} |
|
if ((fendp = strchr(cmdp, ' ')) == NULL) |
|
fendp = strchr(cmdp, '\t'); |
|
|
|
*fendp = '\0'; |
*fendp = '\0'; |
/* |
/* |
* If no extant mg command found, just return. |
* If no extant mg command found, just return. |
|
|
return (dobeep_msgs("Command takes no arguments: ", cmdp)); |
return (dobeep_msgs("Command takes no arguments: ", cmdp)); |
|
|
/* now find the first argument */ |
/* now find the first argument */ |
if (fendp) |
p = fendp + 1; |
p = fendp + 1; |
|
else |
|
p = ""; |
|
p = skipwhite(p); |
p = skipwhite(p); |
|
|
if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf)) |
if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf)) |
return (dobeep_msg("strlcpy error")); |
return (dobeep_msg("strlcpy error")); |
argp = argbuf; |
argp = argbuf; |
numspc = spc = 1; /* initially fake a space so we find first argument */ |
numspc = spc = 1; /* initially fake a space so we find first argument */ |
inlist = eolst = fin = rpar = 0; |
inlist = fin = 0; |
|
|
for (p = argp; fin == 0; p++) { |
for (p = argbuf; *p != '\0'; p++) { |
#ifdef MGLOG |
if (*(p + 1) == '\0') |
mglog_execbuf("", excbuf, argbuf, argp, eolst, inlist, cmdp, |
fin = 1; |
p, contbuf); |
|
#endif |
if (*p != ' ') { |
if (foundlst) { |
if (spc == 1) |
foundlst = 0; |
argp = p; |
p--; /* otherwise 1st arg is missed from list. */ |
spc = 0; |
} |
} |
if (*p == ')') { |
if (*p == ' ' || fin) { |
rpar = 1; |
|
*p = '\0'; |
|
} |
|
if (*p == ' ' || *p == '\t' || *p == '\0') { |
|
if (spc == 1) |
if (spc == 1) |
continue; |
continue; |
if (spc == 0 && (numspc % numparams == 0)) { |
|
if (*p == '\0') |
|
eolst = 1; |
|
else |
|
eolst = 0; |
|
*p = '\0'; /* terminate arg string */ |
|
endp = p + 1; |
|
excbuf[0] = '\0'; |
|
/* Is arg a var? */ |
|
if (!inlist) { |
|
sizof = sizeof(tmpbuf); |
|
t = tmpbuf; |
|
if (isvar(&argp, &t, sizof)) { |
|
if ((contbuf = strndup(endp, |
|
BUFSIZE)) == NULL) |
|
return(FALSE); |
|
*p = ' '; |
|
(void)(strlcpy(argbuf, tmpbuf, |
|
sizof) >= sizof); |
|
p = argp = argbuf; |
|
spc = 1; |
|
foundlst = inlist = 1; |
|
continue; |
|
} |
|
} |
|
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); |
if (*p == ' ') { |
#ifdef MGLOG |
*p = '\0'; /* terminate arg string */ |
mglog_execbuf(" ", excbuf, argbuf, argp, |
} |
eolst, inlist, cmdp, p, contbuf); |
endp = p + 1; |
#endif |
excbuf[0] = '\0'; |
*p = ' '; /* so 'for' loop can continue */ |
varbuf[0] = '\0'; |
if (eolst) { |
contbuf[0] = '\0'; |
if (contbuf != NULL) { |
sizof = sizeof(varbuf); |
(void)strlcpy(argbuf, contbuf, |
v = varbuf; |
sizeof(argbuf)); |
if (isvar(&argp, &v, sizof)) { |
free(contbuf); |
(void)(strlcat(varbuf, " ", |
contbuf = NULL; |
sizof) >= sizof); |
p = argp = argbuf; |
|
foundlst = 1; |
*p = ' '; |
inlist = 0; |
|
if (rpar) |
(void)(strlcpy(contbuf, endp, |
fin = 1; |
sizeof(contbuf)) >= sizeof(contbuf)); |
continue; |
|
} |
(void)(strlcat(varbuf, contbuf, |
spc = 1; |
sizof) >= sizof); |
inlist = 0; |
|
|
(void)(strlcpy(argbuf, varbuf, |
|
sizof) >= sizof); |
|
|
|
p = argp = argbuf; |
|
while (*p != ' ') { |
|
if (*p == '\0') |
|
break; |
|
p++; |
} |
} |
if (eolst && rpar) |
*p = '\0'; |
fin = 1; |
spc = 1; |
|
fin = 0; |
} |
} |
numspc++; |
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); |
|
|
|
if (fin) |
|
break; |
|
|
|
*p = ' '; /* unterminate arg string */ |
spc = 1; |
spc = 1; |
} else { |
|
if (spc == 1) |
|
if ((numparams == 1) || |
|
((numspc + 1) % numparams) == 0) |
|
argp = p; |
|
spc = 0; |
|
} |
} |
} |
} |
return (TRUE); |
return (TRUE); |
|
|
* Is an item a value or a variable? |
* Is an item a value or a variable? |
*/ |
*/ |
static int |
static int |
isvar(char **argp, char **tmpbuf, int sizof) |
isvar(char **argp, char **varbuf, int sizof) |
{ |
{ |
struct varentry *v1 = NULL; |
struct varentry *v1 = NULL; |
|
|
if (SLIST_EMPTY(&varhead)) |
if (SLIST_EMPTY(&varhead)) |
return (FALSE); |
return (FALSE); |
#ifdef MGLOG |
#ifdef MGLOG |
mglog_isvar(*tmpbuf, *argp, sizof); |
mglog_isvar(*varbuf, *argp, sizof); |
#endif |
#endif |
SLIST_FOREACH(v1, &varhead, entry) { |
SLIST_FOREACH(v1, &varhead, entry) { |
if (strcmp(*argp, v1->name) == 0) { |
if (strcmp(*argp, v1->name) == 0) { |
(void)(strlcpy(*tmpbuf, v1->vals, sizof) >= sizof); |
(void)(strlcpy(*varbuf, v1->vals, sizof) >= sizof); |
return (TRUE); |
return (TRUE); |
} |
} |
} |
} |
|
|
|
|
|
|
/* |
/* |
* The (define string _must_ adhere to the regex in foundparen. |
* The define string _must_ adhere to the regex in parsexp(). |
* This is not the correct way to do parsing but it does highlight |
* This is not the correct way to do parsing but it does highlight |
* the issues. |
* the issues. |
*/ |
*/ |
|
|
foundvar(char *defstr) |
foundvar(char *defstr) |
{ |
{ |
struct varentry *vt, *v1 = NULL; |
struct varentry *vt, *v1 = NULL; |
const char e[2] = "e", t[2] = "t"; |
const char t[2] = "t"; |
char *p, *vnamep, *vendp = NULL, *valp, *o; |
char *p, *vnamep, *vendp = NULL, *valp; |
int spc, foundlist = 0; |
int spc; |
|
|
p = defstr + 1; /* move past first '(' char. */ |
p = strstr(defstr, " "); /* move to first ' ' char. */ |
p = skipwhite(p); /* find first char of 'define'. */ |
vnamep = skipwhite(p); /* find first char of var name. */ |
p = strstr(p, e); /* find first 'e' in 'define'. */ |
|
p = strstr(++p, e); /* find second 'e' in 'define'. */ |
|
p++; /* move past second 'e'. */ |
|
vnamep = skipwhite(p); /* find first char of var name. */ |
|
vendp = vnamep; |
vendp = vnamep; |
|
|
/* now find the end of the list name */ |
/* now find the end of the list name */ |
while (1) { |
while (1) { |
++vendp; |
++vendp; |
if (*vendp == '(') { |
if (*vendp == ' ') |
foundlist = 1; |
|
break; |
break; |
} else if (*vendp == ' ' || *vendp == '\t') |
|
break; |
|
} |
} |
*vendp = '\0'; |
*vendp = '\0'; |
|
|
/* |
/* |
* Check list name is not an existing function. |
* Check list name is not an existing function. |
* Although could this be allowed? Shouldn't context dictate? |
* Although could this be allowed? Shouldn't context dictate? |
|
|
|
|
p = ++vendp; |
p = ++vendp; |
p = skipwhite(p); |
p = skipwhite(p); |
if (foundlist) { |
|
|
if ((*p == 'l') && (*(p + 1) == 'i') && (*(p + 2) == 's')) { |
p = strstr(p, t); /* find 't' in 'list'. */ |
p = strstr(p, t); /* find 't' in 'list'. */ |
valp = skipwhite(++p); /* find first value */ |
valp = skipwhite(++p); /* find first value */ |
} else |
} else |
|
|
spc = 1; |
spc = 1; |
/* now loop through values in list value string while counting them */ |
/* now loop through values in list value string while counting them */ |
for (p = valp; *p != '\0'; p++) { |
for (p = valp; *p != '\0'; p++) { |
if (*p == ' ' || *p == '\t') { |
if (*p != ' ' && *p != '\t') { |
if (spc == 0) |
|
vendp = p; |
|
spc = 1; |
|
} else if (*p == ')') { |
|
o = p - 1; |
|
if (*o != ' ' && *o != '\t') |
|
vendp = p; |
|
break; |
|
} else { |
|
if (spc == 1) |
if (spc == 1) |
v1->count++; |
v1->count++; |
spc = 0; |
spc = 0; |
} |
} |
} |
} |
if (vendp) |
|
*vendp = '\0'; |
|
|
|
if ((v1->vals = strndup(valp, BUFSIZE)) == NULL) |
if ((v1->vals = strndup(valp, BUFSIZE)) == NULL) |
return(dobeep_msg("strndup error")); |
return(dobeep_msg("strndup error")); |
|
|
#ifdef MGLOG |
#ifdef MGLOG |
mglog_misc("var:%s\t#items:%d\tvals:%s\n", vnamep, v1->count, v1->vals); |
mglog_misc("var:%s\t#items:%d\tvals:|%s|\n", vnamep, v1->count, v1->vals); |
#endif |
#endif |
|
|
return (TRUE); |
return (TRUE); |
} |
} |
|
|
/* |
/* |
* Finished with evaluation, so clean up any vars. |
* Finished with buffer evaluation, so clean up any vars. |
|
* Perhaps keeps them in mg even after use,... |
*/ |
*/ |
int |
int |
clearvars(void) |
clearvars(void) |
|
|
} |
} |
|
|
/* |
/* |
* Line has a '(' as the first non-white char. |
* Finished with block evaluation, so clean up any expressions. |
* Do some very basic parsing of line with '(' as the first character. |
|
* Multi-line not supported at the moment, To do. |
|
*/ |
*/ |
int |
static void |
foundparen(char *funstr) |
clearexp(void) |
{ |
{ |
char *regs, *p; |
struct expentry *e1 = NULL; |
int pctr; |
|
|
|
pctr = 0; |
while (!SLIST_EMPTY(&exphead)) { |
|
e1 = SLIST_FIRST(&exphead); |
/* |
SLIST_REMOVE_HEAD(&exphead, eentry); |
* Check for blocks of code with opening and closing (). |
free(e1->exp); |
* One block = (cmd p a r a m) |
free(e1); |
* 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; |
|
while (*p != '\0') { |
|
if (*p == '(') { |
|
pctr++; |
|
} else if (*p == ')') { |
|
pctr--; |
|
} |
|
p++; |
|
} |
} |
if (pctr != 0) |
return; |
return(dobeep_msg("Opening and closing parentheses error")); |
|
|
|
/* Does the line have a list 'define' like: */ |
|
/* (define alist(list 1 2 3 4)) */ |
|
regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[(][\t ]*list[\t ]+"\ |
|
"[^\t ]+.*[)][\t ]*[)]"; |
|
if (doregex(regs, funstr)) |
|
return(foundvar(funstr)); |
|
|
|
/* Does the line have a single variable 'define' like: */ |
|
/* (define i 0) */ |
|
regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[^\t (]+[\t ]*[)]"; |
|
if (doregex(regs, funstr)) |
|
return(foundvar(funstr)); |
|
|
|
/* Does the line have an unrecognised 'define' */ |
|
regs = "^[(][\t ]*define[\t ]+"; |
|
if (doregex(regs, funstr)) |
|
return(dobeep_msg("Invalid use of define")); |
|
|
|
return(multiarg(funstr)); |
|
} |
} |
|
|
/* |
/* |
* Test a string against a regular expression. |
* Test a string against a regular expression. |
*/ |
*/ |
int |
static int |
doregex(char *r, char *e) |
doregex(char *r, char *e) |
{ |
{ |
regex_t regex_buff; |
regex_t regex_buff; |