diff options
author | 2019-07-18 05:57:48 +0000 | |
---|---|---|
committer | 2019-07-18 05:57:48 +0000 | |
commit | 3c2c8acbe21f84173ac9cf30292b7fe7fc2ede61 (patch) | |
tree | 01361735a7554dbf8a1a50efa3fbebef7331808d | |
parent | replace ether_{cmp,is_eq,is_zero} with the new ones in netinet/if_ether.h (diff) | |
download | wireguard-openbsd-3c2c8acbe21f84173ac9cf30292b7fe7fc2ede61.tar.xz wireguard-openbsd-3c2c8acbe21f84173ac9cf30292b7fe7fc2ede61.zip |
Add some more basic interpreter functionality to mg. Needs a lot more
work, including a proper parser.
-rw-r--r-- | usr.bin/mg/Makefile | 4 | ||||
-rw-r--r-- | usr.bin/mg/def.h | 8 | ||||
-rw-r--r-- | usr.bin/mg/extend.c | 114 | ||||
-rw-r--r-- | usr.bin/mg/interpreter.c | 395 |
4 files changed, 407 insertions, 114 deletions
diff --git a/usr.bin/mg/Makefile b/usr.bin/mg/Makefile index daa9eae0498..fc3899772ec 100644 --- a/usr.bin/mg/Makefile +++ b/usr.bin/mg/Makefile @@ -1,4 +1,4 @@ -# $OpenBSD: Makefile,v 1.34 2019/06/10 06:52:44 lum Exp $ +# $OpenBSD: Makefile,v 1.35 2019/07/18 05:57:48 lum Exp $ PROG= mg @@ -22,7 +22,7 @@ SRCS= autoexec.c basic.c bell.c buffer.c cinfo.c dir.c display.c \ # # More or less standalone extensions. # -SRCS+= cmode.c cscope.c dired.c grep.c tags.c +SRCS+= cmode.c cscope.c dired.c grep.c interpreter.c tags.c # # -DMGLOG source file. diff --git a/usr.bin/mg/def.h b/usr.bin/mg/def.h index 506567298e8..78a8147fb27 100644 --- a/usr.bin/mg/def.h +++ b/usr.bin/mg/def.h @@ -1,4 +1,4 @@ -/* $OpenBSD: def.h,v 1.163 2019/07/17 18:18:37 lum Exp $ */ +/* $OpenBSD: def.h,v 1.164 2019/07/18 05:57:48 lum Exp $ */ /* This file is in the public domain. */ @@ -98,6 +98,7 @@ typedef int (*PF)(int, int); /* generally useful type */ #define MAX_TOKEN 64 +#define BUFSIZE 128 /* Size of line contents in extend.c */ /* * Previously from sysdef.h */ @@ -580,6 +581,7 @@ int evalbuffer(int, int); int evalfile(int, int); int load(const char *); int excline(char *); +char *skipwhite(char *); /* help.c X */ int desckey(int, int); @@ -715,6 +717,10 @@ int dobeep_msgs(const char *, const char *); int dobeep_msg(const char *); void dobeep(void); +/* interpreter.c */ +int foundparen(char *); +int clearvars(void); + /* * Externals. */ diff --git a/usr.bin/mg/extend.c b/usr.bin/mg/extend.c index af750c200f3..477e6cd9172 100644 --- a/usr.bin/mg/extend.c +++ b/usr.bin/mg/extend.c @@ -1,4 +1,4 @@ -/* $OpenBSD: extend.c,v 1.69 2019/07/17 18:43:47 lum Exp $ */ +/* $OpenBSD: extend.c,v 1.70 2019/07/18 05:57:48 lum Exp $ */ /* This file is in the public domain. */ /* @@ -26,11 +26,9 @@ static int remap(KEYMAP *, int, PF, KEYMAP *); static KEYMAP *reallocmap(KEYMAP *); static void fixmap(KEYMAP *, KEYMAP *, KEYMAP *); static int dobind(KEYMAP *, const char *, int); -static char *skipwhite(char *); static char *parsetoken(char *); static int bindkey(KEYMAP **, const char *, KCHAR *, int); -#define BUFSIZE 128 /* * Insert a string, mainly for use from macros (created by selfinsert). */ @@ -673,112 +671,6 @@ load(const char *fname) } /* - * Line has a '(' as the first non-white char. - */ -static int -multiarg(char *funstr) -{ - regex_t regex_buff; - PF funcp; - char excbuf[BUFSIZE]; - char *cmdp, *argp, *fendp, *endp, *p, *s = " "; - int singlecmd = 0, spc, numparams, numspc; - - endp = strrchr(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 (regcomp(®ex_buff, "^[(][\t ]*[)]$", REG_EXTENDED)) { - regfree(®ex_buff); - return (dobeep_msg("Could not compile regex")); - } - if (!regexec(®ex_buff, funstr, 0, NULL, 0)) { - regfree(®ex_buff); - return (dobeep_msg("No command found")); - } - /* currently there are no mg commands that don't have a letter */ - if (regcomp(®ex_buff, "^[(][\t ]*[A-Za-z-]+[\t ]*[)]$", - REG_EXTENDED)) { - regfree(®ex_buff); - return (dobeep_msg("Could not compile regex")); - } - if (!regexec(®ex_buff, funstr, 0, NULL, 0)) - singlecmd = 1; - - regfree(®ex_buff); - 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'; - /* - * If no extant mg command found, line could be a (define of some kind. - * Since no defines exist at the moment, just return. - */ - if ((funcp = name_function(cmdp)) == NULL) - return (dobeep_msgs("Unknown command: %s", cmdp)); - - numparams = numparams_function(funcp); - if (numparams == 0) - return (dobeep_msgs("Command takes no arguments: %s", cmdp)); - - /* now find the first argument */ - p = fendp + 1; - argp = skipwhite(p); - numspc = spc = 1; /* initially fake a space so we find first argument */ - - for (p = argp; *p != '\0'; p++) { - if (*p == ' ' || *p == '\t' || *p == ')') { - if (spc == 1) - continue; - if (spc == 0 && (numspc % numparams == 0)) { - *p = '\0'; /* terminate arg string */ - excbuf[0] = '\0'; - if (strlcpy(excbuf, cmdp, sizeof(excbuf)) - >= sizeof(excbuf)) - return (dobeep_msg("strlcpy error")); - - if (strlcat(excbuf, s, sizeof(excbuf)) - >= sizeof(excbuf)) - return (dobeep_msg("strlcpy error")); - - if (strlcat(excbuf, argp, sizeof(excbuf)) - >= sizeof(excbuf)) - return (dobeep_msg("strlcpy error")); - - excline(excbuf); - *p = ' '; /* so 'for' loop can continue */ - } - numspc++; - spc = 1; - } else { - if (spc == 1) - if ((numparams == 1) || - ((numspc + 1) % numparams) == 0) - argp = p; - spc = 0; - } - } - return (TRUE); -} - -/* * excline - run a line from a load file or eval-expression. */ int @@ -809,7 +701,7 @@ excline(char *line) if (*funcp == '\0') return (TRUE); /* No error on blank lines */ if (*funcp == '(') - return (multiarg(funcp)); + return (foundparen(funcp)); line = parsetoken(funcp); if (*line != '\0') { *line++ = '\0'; @@ -1007,7 +899,7 @@ cleanup: /* * a pair of utility functions for the above */ -static char * +char * skipwhite(char *s) { while (*s == ' ' || *s == '\t') diff --git a/usr.bin/mg/interpreter.c b/usr.bin/mg/interpreter.c new file mode 100644 index 00000000000..19ed02ef4cb --- /dev/null +++ b/usr.bin/mg/interpreter.c @@ -0,0 +1,395 @@ +/* $OpenBSD: interpreter.c,v 1.1 2019/07/18 05:57:48 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-fine a.txt b.txt. c.txt) + * + * 2. Define a list: + * (define myfiles(list d.txt e.txt)) + * + * 3. Use the previously defined 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. define single value variables (define i 0) + * 5. deal with quotes around a string: "x x" + * 6. oh so many things.... + * [...] + * n. implement user definable functions. + */ +#include <sys/queue.h> +#include <regex.h> +#include <signal.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> + +#include "def.h" +#include "funmap.h" + +static int multiarg(char *); +static int isvar(char **, char **, int); +static int foundvar(char *); +static int foundlist(char *); + + +/* + * Structure for variables during buffer evaluation. + */ +struct varentry { + SLIST_ENTRY(varentry) entry; + char *name; + char *vals; + int count; +}; +SLIST_HEAD(vlisthead, varentry) varhead = SLIST_HEAD_INITIALIZER(varhead); + +/* + * Pass a list of arguments to a function. + */ +static int +multiarg(char *funstr) +{ + regex_t regex_buff; + PF funcp; + char excbuf[BUFSIZE], argbuf[BUFSIZE], *contbuf, tmpbuf[BUFSIZE]; + char *cmdp, *argp, *fendp, *endp, *p, *t, *s = " "; + int singlecmd = 0, spc, numparams, numspc; + int inlist, foundlst = 0, last, sizof; + + contbuf = NULL; + endp = strrchr(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 (regcomp(®ex_buff, "^[(][\t ]*[)]$", REG_EXTENDED)) { + regfree(®ex_buff); + return (dobeep_msg("Could not compile regex")); + } + if (!regexec(®ex_buff, funstr, 0, NULL, 0)) { + regfree(®ex_buff); + return (dobeep_msg("No command found")); + } + /* currently there are no mg commands that don't have a letter */ + if (regcomp(®ex_buff, "^[(][\t ]*[A-Za-z-]+[\t ]*[)]$", + REG_EXTENDED)) { + regfree(®ex_buff); + return (dobeep_msg("Could not compile regex")); + } + if (!regexec(®ex_buff, funstr, 0, NULL, 0)) + singlecmd = 1; + + regfree(®ex_buff); + 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'; + /* + * 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)); + + /* now find the first argument */ + p = fendp + 1; + p = skipwhite(p); + if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf)) + return (dobeep_msg("strlcpy error")); + argp = argbuf; + numspc = spc = 1; /* initially fake a space so we find first argument */ + inlist = last = 0; + + for (p = argp; *p != '\0'; p++) { + if (foundlst) { + foundlst = 0; + p--; /* otherwise 1st arg is missed from list. */ + } + if (*p == ' ' || *p == '\t' || *p == ')') { + if (spc == 1) + continue; + if (spc == 0 && (numspc % numparams == 0)) { + if (*p == ')') + last = 1; + else + last = 0; + *p = '\0'; /* terminate arg string */ + endp = p + 1; + excbuf[0] = '\0'; + /* Is arg a var? */ + if (!inlist) { + if ((contbuf = strndup(endp, BUFSIZE)) + == NULL) + return(FALSE); + sizof = sizeof(tmpbuf); + t = tmpbuf; + if (isvar(&argp, &t, sizof)) { + *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); + *p = ' '; /* so 'for' loop can continue */ + if (last) { + if (contbuf != NULL) { + (void)strlcpy(argbuf, contbuf, + sizeof(argbuf)); + contbuf = NULL; + p = argp = argbuf; + foundlst = 1; + inlist = 0; + continue; + } + spc = 1; + inlist = 0; + } + } + numspc++; + spc = 1; + } else { + if (spc == 1) + if ((numparams == 1) || + ((numspc + 1) % numparams) == 0) + argp = p; + spc = 0; + } + } + return (TRUE); +} + + +/* + * Is an item a value or a variable? + */ +static int +isvar(char **argp, char **tmpbuf, int sizof) +{ + struct varentry *v1 = NULL; + + if (SLIST_EMPTY(&varhead)) + return (FALSE); + + SLIST_FOREACH(v1, &varhead, entry) { + if (strcmp(*argp, v1->name) == 0) { + (void)(strlcpy(*tmpbuf, v1->vals, sizof) >= sizof); + return (TRUE); + } + } + return (FALSE); +} + + +/* + * The (define string _must_ adhere to the regex in foundparen. + * This is not the correct way to do parsing but it does highlight + * the issues. + */ +static int +foundlist(char *defstr) +{ + struct varentry *vt, *v1 = NULL; + const char e[1] = "e", t[1] = "t"; + char *p, *vnamep, *vendp = NULL, *valp; + int spc; + + + p = defstr + 1; /* move past first '(' char. */ + p = skipwhite(p); /* find first char of 'define'. */ + 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; + + /* now find the end of the list name */ + while (1) { + ++vendp; + if (*vendp == '(' || *vendp == ' ' || *vendp == '\t') + break; + } + *vendp = '\0'; + /* + * Check list name is not an existing function. + * Although could this be allowed? Shouldn't context dictate? + */ + if (name_function(vnamep) != NULL) + return(dobeep_msgs("Variable/function name clash:", vnamep)); + + p = ++vendp; + p = strstr(p, t); /* find 't' in 'list'. */ + valp = skipwhite(++p); /* find first value */ + /* + * Now we have the name of the list starting at 'vnamep', + * and the first value is at 'valp', record the details + * in a linked list. But first remove variable, if existing already. + */ + if (!SLIST_EMPTY(&varhead)) { + SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) { + if (strcmp(vnamep, v1->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->name = strndup(vnamep, BUFSIZE)) == NULL) + return(dobeep_msg("strndup error")); + v1->count = 0; + vendp = NULL; + + /* initially fake a space so we find first value */ + spc = 1; + /* now loop through values in list value string while counting them */ + for (p = valp; *p != '\0'; p++) { + if (*p == ' ' || *p == '\t') { + if (spc == 0) + vendp = p; + spc = 1; + } else if (*p == ')') { + vendp = ++p; /* currently need ')' */ + break; + } else { + if (spc == 1) + v1->count++; + spc = 0; + } + } + *vendp = '\0'; + if ((v1->vals = strndup(valp, BUFSIZE)) == NULL) + return(dobeep_msg("strndup error")); + + return (TRUE); +} + + +/* + * to do + */ +static int +foundvar(char *funstr) +{ + ewprintf("to do"); + return (TRUE); +} + +/* + * Finished with evaluation, so clean up any vars. + */ +int +clearvars(void) +{ + struct varentry *v1 = NULL; + + while (!SLIST_EMPTY(&varhead)) { + v1 = SLIST_FIRST(&varhead); + SLIST_REMOVE_HEAD(&varhead, entry); + free(v1->vals); + free(v1->name); + free(v1); + } + return (FALSE); +} + +/* + * Line has a '(' as the first non-white char. + * Do some very basic parsing of line with '(' as the first character. + * Multi-line not supported at the moment, To do. + */ +int +foundparen(char *funstr) +{ + regex_t regex_buff; + char *regs; + + /* 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 (regcomp(®ex_buff, regs, REG_EXTENDED)) { + regfree(®ex_buff); + return(dobeep_msg("Could not compile regex")); + } + if (!regexec(®ex_buff, funstr, 0, NULL, 0)) { + regfree(®ex_buff); + return(foundlist(funstr)); + } + /* Does the line have a single variable 'define' like: */ + /* (define i 0) */ + regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[^\t (]+[\t ]*[)]"; + if (regcomp(®ex_buff, regs, REG_EXTENDED)) { + regfree(®ex_buff); + return(dobeep_msg("Could not compile regex")); + } + if (!regexec(®ex_buff, funstr, 0, NULL, 0)) { + regfree(®ex_buff); + return(foundvar(funstr)); + } + /* Does the line have an unrecognised 'define' */ + regs = "^[(][\t ]*define[\t ]+"; + if (regcomp(®ex_buff, regs, REG_EXTENDED)) { + regfree(®ex_buff); + return(dobeep_msg("Could not compile regex")); + } + if (!regexec(®ex_buff, funstr, 0, NULL, 0)) { + regfree(®ex_buff); + return(dobeep_msg("Invalid use of define")); + } + regfree(®ex_buff); + return(multiarg(funstr)); +} |