[BACK]Return to interpreter.c CVS log [TXT][DIR] Up to [local] / src / usr.bin / mg

Annotation of src/usr.bin/mg/interpreter.c, Revision 1.5

1.5     ! lum         1: /*      $OpenBSD: interpreter.c,v 1.4 2019/07/20 06:00:16 lum Exp $    */
1.1       lum         2: /*
                      3:  * This file is in the public domain.
                      4:  *
                      5:  * Author: Mark Lumsden <mark@showcomplex.com>
                      6:  */
                      7:
                      8: /*
                      9:  * This file attempts to add some 'scripting' functionality into mg.
                     10:  *
                     11:  * The initial goal is to give mg the ability to use it's existing functions
                     12:  * and structures in a linked-up way. Hopefully resulting in user definable
                     13:  * functions. The syntax is 'scheme' like but currently it is not a scheme
                     14:  * interpreter.
                     15:  *
                     16:  * At the moment there is no manual page reference to this file. The code below
                     17:  * is liable to change, so use at your own risk!
                     18:  *
                     19:  * If you do want to do some testing, you can add some lines to your .mg file
                     20:  * like:
                     21:  *
                     22:  * 1. Give multiple arguments to a function that usually would accept only one:
1.4       lum        23:  * (find-file a.txt b.txt. c.txt)
1.1       lum        24:  *
                     25:  * 2. Define a list:
                     26:  * (define myfiles(list d.txt e.txt))
                     27:  *
                     28:  * 3. Use the previously defined list:
                     29:  * (find-file myfiles)
                     30:  *
                     31:  * To do:
                     32:  * 1. multiline parsing - currently only single lines supported.
                     33:  * 2. parsing for '(' and ')' throughout whole string and evaluate correctly.
                     34:  * 3. conditional execution.
                     35:  * 4. define single value variables (define i 0)
                     36:  * 5. deal with quotes around a string: "x x"
                     37:  * 6. oh so many things....
                     38:  * [...]
                     39:  * n. implement user definable functions.
                     40:  */
                     41: #include <sys/queue.h>
                     42: #include <regex.h>
                     43: #include <signal.h>
                     44: #include <stdio.h>
                     45: #include <stdlib.h>
                     46: #include <string.h>
                     47:
                     48: #include "def.h"
                     49: #include "funmap.h"
                     50:
1.2       lum        51: #ifdef  MGLOG
                     52: #include "kbd.h"
                     53: #include "log.h"
                     54: #endif
                     55:
1.1       lum        56: static int      multiarg(char *);
                     57: static int      isvar(char **, char **, int);
                     58: static int      foundvar(char *);
                     59: static int      foundlist(char *);
                     60:
                     61:
                     62: /*
                     63:  * Structure for variables during buffer evaluation.
                     64:  */
                     65: struct varentry {
                     66:        SLIST_ENTRY(varentry) entry;
                     67:        char    *name;
                     68:        char    *vals;
                     69:        int      count;
                     70: };
                     71: SLIST_HEAD(vlisthead, varentry) varhead = SLIST_HEAD_INITIALIZER(varhead);
                     72:
                     73: /*
                     74:  * Pass a list of arguments to a function.
                     75:  */
                     76: static int
                     77: multiarg(char *funstr)
                     78: {
                     79:        regex_t  regex_buff;
                     80:        PF       funcp;
                     81:        char     excbuf[BUFSIZE], argbuf[BUFSIZE], *contbuf, tmpbuf[BUFSIZE];
                     82:        char    *cmdp, *argp, *fendp, *endp, *p, *t, *s = " ";
                     83:        int      singlecmd = 0, spc, numparams, numspc;
1.3       lum        84:        int      inlist, foundlst = 0, eolst, rpar, sizof, fin;
1.1       lum        85:
                     86:        contbuf = NULL;
                     87:        endp = strrchr(funstr, ')');
                     88:        if (endp == NULL) {
                     89:                ewprintf("No closing parenthesis found");
                     90:                return(FALSE);
                     91:        }
                     92:        p = endp + 1;
                     93:        if (*p != '\0')
                     94:                *p = '\0';
                     95:        /* we now know that string starts with '(' and ends with ')' */
                     96:        if (regcomp(&regex_buff, "^[(][\t ]*[)]$", REG_EXTENDED)) {
                     97:                regfree(&regex_buff);
                     98:                return (dobeep_msg("Could not compile regex"));
                     99:        }
                    100:        if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
                    101:                regfree(&regex_buff);
                    102:                return (dobeep_msg("No command found"));
                    103:        }
                    104:        /* currently there are no mg commands that don't have a letter */
                    105:        if (regcomp(&regex_buff, "^[(][\t ]*[A-Za-z-]+[\t ]*[)]$",
                    106:            REG_EXTENDED)) {
                    107:                regfree(&regex_buff);
                    108:                return (dobeep_msg("Could not compile regex"));
                    109:        }
                    110:        if (!regexec(&regex_buff, funstr, 0, NULL, 0))
                    111:                singlecmd = 1;
                    112:
                    113:        regfree(&regex_buff);
                    114:        p = funstr + 1;         /* move past first '(' char.    */
                    115:        cmdp = skipwhite(p);    /* find first char of command.  */
                    116:
                    117:        if (singlecmd) {
                    118:                /* remove ')', then check for spaces at the end */
                    119:                cmdp[strlen(cmdp) - 1] = '\0';
                    120:                if ((fendp = strchr(cmdp, ' ')) != NULL)
                    121:                        *fendp = '\0';
                    122:                else if ((fendp = strchr(cmdp, '\t')) != NULL)
                    123:                        *fendp = '\0';
                    124:                return(excline(cmdp));
                    125:        }
                    126:        if ((fendp = strchr(cmdp, ' ')) == NULL)
                    127:                fendp = strchr(cmdp, '\t');
                    128:
                    129:        *fendp = '\0';
                    130:        /*
                    131:         * If no extant mg command found, just return.
                    132:         */
                    133:        if ((funcp = name_function(cmdp)) == NULL)
                    134:                return (dobeep_msgs("Unknown command: ", cmdp));
                    135:
                    136:        numparams = numparams_function(funcp);
                    137:        if (numparams == 0)
                    138:                return (dobeep_msgs("Command takes no arguments: ", cmdp));
                    139:
                    140:        /* now find the first argument */
                    141:        p = fendp + 1;
                    142:        p = skipwhite(p);
                    143:        if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
                    144:                return (dobeep_msg("strlcpy error"));
                    145:        argp = argbuf;
                    146:        numspc = spc = 1; /* initially fake a space so we find first argument */
1.3       lum       147:        inlist = eolst = fin = rpar = 0;
1.1       lum       148:
1.3       lum       149:        for (p = argp; fin == 0; p++) {
1.2       lum       150: #ifdef  MGLOG
1.3       lum       151:                mglog_execbuf("", excbuf, argbuf, argp, eolst, inlist, cmdp,
1.2       lum       152:                    p, contbuf);
                    153: #endif
1.1       lum       154:                if (foundlst) {
                    155:                        foundlst = 0;
                    156:                        p--;    /* otherwise 1st arg is missed from list. */
                    157:                }
1.3       lum       158:                if (*p == ')') {
                    159:                        rpar = 1;
                    160:                        *p = '\0';
                    161:                }
                    162:                if (*p == ' ' || *p == '\t' || *p == '\0') {
1.1       lum       163:                        if (spc == 1)
                    164:                                continue;
                    165:                        if (spc == 0 && (numspc % numparams == 0)) {
1.3       lum       166:                                if (*p == '\0')
                    167:                                        eolst = 1;
1.1       lum       168:                                else
1.3       lum       169:                                        eolst = 0;
1.1       lum       170:                                *p = '\0';      /* terminate arg string */
                    171:                                endp = p + 1;
                    172:                                excbuf[0] = '\0';
                    173:                                /* Is arg a var? */
                    174:                                if (!inlist) {
                    175:                                        sizof = sizeof(tmpbuf);
                    176:                                        t = tmpbuf;
                    177:                                        if (isvar(&argp, &t, sizof)) {
1.4       lum       178:                                                if ((contbuf = strndup(endp,
                    179:                                                    BUFSIZE)) == NULL)
                    180:                                                        return(FALSE);
1.1       lum       181:                                                *p = ' ';
                    182:                                                (void)(strlcpy(argbuf, tmpbuf,
                    183:                                                    sizof) >= sizof);
                    184:                                                p = argp = argbuf;
                    185:                                                spc = 1;
                    186:                                                foundlst = inlist = 1;
                    187:                                                continue;
                    188:                                        }
                    189:                                }
                    190:                                if (strlcpy(excbuf, cmdp, sizeof(excbuf))
                    191:                                     >= sizeof(excbuf))
                    192:                                        return (dobeep_msg("strlcpy error"));
                    193:                                if (strlcat(excbuf, s, sizeof(excbuf))
                    194:                                    >= sizeof(excbuf))
                    195:                                        return (dobeep_msg("strlcat error"));
                    196:                                if (strlcat(excbuf, argp, sizeof(excbuf))
                    197:                                    >= sizeof(excbuf))
                    198:                                        return (dobeep_msg("strlcat error"));
                    199:
                    200:                                excline(excbuf);
1.2       lum       201: #ifdef  MGLOG
                    202:                                mglog_execbuf("  ", excbuf, argbuf, argp,
1.3       lum       203:                                    eolst, inlist, cmdp, p, contbuf);
1.2       lum       204: #endif
1.1       lum       205:                                *p = ' ';       /* so 'for' loop can continue */
1.3       lum       206:                                if (eolst) {
1.1       lum       207:                                        if (contbuf != NULL) {
                    208:                                                (void)strlcpy(argbuf, contbuf,
                    209:                                                    sizeof(argbuf));
1.5     ! lum       210:                                                free(contbuf);
1.1       lum       211:                                                contbuf = NULL;
                    212:                                                p = argp = argbuf;
                    213:                                                foundlst = 1;
                    214:                                                inlist = 0;
1.3       lum       215:                                                if (rpar)
                    216:                                                        fin = 1;
1.1       lum       217:                                                continue;
                    218:                                        }
                    219:                                        spc = 1;
                    220:                                        inlist = 0;
                    221:                                }
1.3       lum       222:                                if (eolst && rpar)
                    223:                                        fin = 1;
1.1       lum       224:                        }
                    225:                        numspc++;
                    226:                        spc = 1;
                    227:                } else {
                    228:                        if (spc == 1)
                    229:                                if ((numparams == 1) ||
                    230:                                    ((numspc + 1) % numparams) == 0)
                    231:                                        argp = p;
                    232:                        spc = 0;
                    233:                }
                    234:        }
                    235:        return (TRUE);
                    236: }
                    237:
                    238:
                    239: /*
                    240:  * Is an item a value or a variable?
                    241:  */
                    242: static int
                    243: isvar(char **argp, char **tmpbuf, int sizof)
                    244: {
                    245:        struct varentry *v1 = NULL;
                    246:
                    247:        if (SLIST_EMPTY(&varhead))
                    248:                return (FALSE);
1.2       lum       249: #ifdef  MGLOG
                    250:        mglog_isvar(*tmpbuf, *argp, sizof);
                    251: #endif
1.1       lum       252:        SLIST_FOREACH(v1, &varhead, entry) {
                    253:                if (strcmp(*argp, v1->name) == 0) {
                    254:                        (void)(strlcpy(*tmpbuf, v1->vals, sizof) >= sizof);
                    255:                        return (TRUE);
                    256:                }
                    257:        }
                    258:        return (FALSE);
                    259: }
                    260:
                    261:
                    262: /*
                    263:  * The (define string _must_ adhere to the regex in foundparen.
                    264:  * This is not the correct way to do parsing but it does highlight
                    265:  * the issues.
                    266:  */
                    267: static int
                    268: foundlist(char *defstr)
                    269: {
                    270:        struct varentry *vt, *v1 = NULL;
                    271:        const char       e[1] = "e", t[1] = "t";
1.3       lum       272:        char            *p, *vnamep, *vendp = NULL, *valp, *o;
1.1       lum       273:        int              spc;
                    274:
                    275:
                    276:        p = defstr + 1;         /* move past first '(' char.    */
                    277:        p = skipwhite(p);       /* find first char of 'define'. */
                    278:        p = strstr(p, e);       /* find first 'e' in 'define'.  */
                    279:        p = strstr(++p, e);     /* find second 'e' in 'define'. */
                    280:        p++;                    /* move past second 'e'.        */
                    281:        vnamep = skipwhite(p);  /* find first char of var name. */
                    282:        vendp = vnamep;
                    283:
                    284:        /* now find the end of the list name */
                    285:        while (1) {
                    286:                ++vendp;
                    287:                if (*vendp == '(' || *vendp == ' ' || *vendp == '\t')
                    288:                        break;
                    289:        }
                    290:        *vendp = '\0';
                    291:        /*
                    292:         * Check list name is not an existing function.
                    293:         * Although could this be allowed? Shouldn't context dictate?
                    294:         */
                    295:        if (name_function(vnamep) != NULL)
                    296:                return(dobeep_msgs("Variable/function name clash:", vnamep));
                    297:
                    298:        p = ++vendp;
                    299:        p = strstr(p, t);       /* find 't' in 'list'.  */
                    300:        valp = skipwhite(++p);  /* find first value     */
                    301:        /*
                    302:         * Now we have the name of the list starting at 'vnamep',
                    303:         * and the first value is at 'valp', record the details
                    304:         * in a linked list. But first remove variable, if existing already.
                    305:         */
                    306:        if (!SLIST_EMPTY(&varhead)) {
                    307:                SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
                    308:                        if (strcmp(vnamep, v1->name) == 0)
                    309:                                SLIST_REMOVE(&varhead, v1, varentry, entry);
                    310:                }
                    311:        }
                    312:        if ((v1 = malloc(sizeof(struct varentry))) == NULL)
                    313:                return (ABORT);
                    314:        SLIST_INSERT_HEAD(&varhead, v1, entry);
                    315:        if ((v1->name = strndup(vnamep, BUFSIZE)) == NULL)
                    316:                return(dobeep_msg("strndup error"));
                    317:        v1->count = 0;
                    318:        vendp = NULL;
1.3       lum       319:
1.1       lum       320:        /* initially fake a space so we find first value */
                    321:        spc = 1;
                    322:        /* now loop through values in list value string while counting them */
                    323:        for (p = valp; *p != '\0'; p++) {
                    324:                if (*p == ' ' || *p == '\t') {
                    325:                        if (spc == 0)
                    326:                                vendp = p;
                    327:                        spc = 1;
                    328:                } else if (*p == ')') {
1.3       lum       329:                        o = p - 1;
                    330:                        if (*o != ' ' && *o != '\t')
                    331:                                vendp = p;
1.1       lum       332:                        break;
                    333:                } else {
                    334:                        if (spc == 1)
                    335:                                v1->count++;
                    336:                        spc = 0;
                    337:                }
                    338:        }
                    339:        *vendp = '\0';
                    340:        if ((v1->vals = strndup(valp, BUFSIZE)) == NULL)
                    341:                return(dobeep_msg("strndup error"));
                    342:
                    343:        return (TRUE);
                    344: }
                    345:
                    346:
                    347: /*
                    348:  * to do
                    349:  */
                    350: static int
                    351: foundvar(char *funstr)
                    352: {
                    353:        ewprintf("to do");
                    354:        return (TRUE);
                    355: }
                    356:
                    357: /*
                    358:  * Finished with evaluation, so clean up any vars.
                    359:  */
                    360: int
                    361: clearvars(void)
                    362: {
                    363:        struct varentry *v1 = NULL;
                    364:
                    365:        while (!SLIST_EMPTY(&varhead)) {
                    366:                v1 = SLIST_FIRST(&varhead);
                    367:                SLIST_REMOVE_HEAD(&varhead, entry);
                    368:                free(v1->vals);
                    369:                free(v1->name);
                    370:                free(v1);
                    371:        }
                    372:        return (FALSE);
                    373: }
                    374:
                    375: /*
                    376:  * Line has a '(' as the first non-white char.
                    377:  * Do some very basic parsing of line with '(' as the first character.
                    378:  * Multi-line not supported at the moment, To do.
                    379:  */
                    380: int
                    381: foundparen(char *funstr)
                    382: {
                    383:        regex_t  regex_buff;
                    384:        char    *regs;
                    385:
                    386:        /* Does the line have a list 'define' like: */
                    387:        /* (define alist(list 1 2 3 4)) */
                    388:        regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[(][\t ]*list[\t ]+"\
                    389:                "[^\t ]+.*[)][\t ]*[)]";
                    390:        if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
                    391:                regfree(&regex_buff);
                    392:                return(dobeep_msg("Could not compile regex"));
                    393:        }
                    394:        if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
                    395:                regfree(&regex_buff);
                    396:                return(foundlist(funstr));
                    397:        }
                    398:        /* Does the line have a single variable 'define' like: */
                    399:        /* (define i 0) */
                    400:        regs = "^[(][\t ]*define[\t ]+[^\t (]+[\t ]*[^\t (]+[\t ]*[)]";
                    401:        if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
                    402:                regfree(&regex_buff);
                    403:                return(dobeep_msg("Could not compile regex"));
                    404:        }
                    405:        if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
                    406:                regfree(&regex_buff);
                    407:                return(foundvar(funstr));
                    408:        }
                    409:        /* Does the line have an unrecognised 'define' */
                    410:        regs = "^[(][\t ]*define[\t ]+";
                    411:        if (regcomp(&regex_buff, regs, REG_EXTENDED)) {
                    412:                regfree(&regex_buff);
                    413:                return(dobeep_msg("Could not compile regex"));
                    414:        }
                    415:        if (!regexec(&regex_buff, funstr, 0, NULL, 0)) {
                    416:                regfree(&regex_buff);
                    417:                return(dobeep_msg("Invalid use of define"));
                    418:        }
                    419:        regfree(&regex_buff);
                    420:        return(multiarg(funstr));
                    421: }