Annotation of src/usr.bin/mg/interpreter.c, Revision 1.29
1.29 ! lum 1: /* $OpenBSD: interpreter.c,v 1.28 2021/05/07 07:42:20 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.11 lum 23: * (find-file "a.txt" "b.txt" "c.txt")
1.1 lum 24: *
1.7 lum 25: * 2. Define a single value variable:
1.11 lum 26: * (define myfile "d.txt")
1.1 lum 27: *
1.7 lum 28: * 3. Define a list:
1.11 lum 29: * (define myfiles(list "e.txt" "f.txt"))
1.7 lum 30: *
31: * 4. Use the previously defined variable or list:
1.1 lum 32: * (find-file myfiles)
33: *
34: * To do:
35: * 1. multiline parsing - currently only single lines supported.
36: * 2. parsing for '(' and ')' throughout whole string and evaluate correctly.
37: * 3. conditional execution.
1.23 lum 38: * 4. have memory allocated dynamically for variable values.
1.19 lum 39: * 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-]
1.25 lum 40: * at the moment.
41: * 6. display line numbers with parsing errors.
42: * 7. oh so many things....
1.1 lum 43: * [...]
44: * n. implement user definable functions.
1.11 lum 45: *
1.14 lum 46: * Notes:
1.27 lum 47: * - Currently calls to excline() from this file have the line length and
48: * line number set to zero.
49: * That's because excline() uses '\0' as the end of line indicator
1.14 lum 50: * and only the call to foundparen() within excline() uses excline's 2nd
1.27 lum 51: * and 3rd arguments.
52: * Importantly, any lines sent to there from here will not be
1.14 lum 53: * coming back here.
1.1 lum 54: */
55: #include <sys/queue.h>
1.13 lum 56:
57: #include <limits.h>
1.1 lum 58: #include <regex.h>
59: #include <signal.h>
60: #include <stdio.h>
61: #include <stdlib.h>
62: #include <string.h>
63:
64: #include "def.h"
65: #include "funmap.h"
66:
1.2 lum 67: #ifdef MGLOG
68: #include "kbd.h"
69: #include "log.h"
70: #endif
71:
1.21 lum 72: static int multiarg(char *, char *, int);
1.1 lum 73: static int isvar(char **, char **, int);
1.21 lum 74: /*static int dofunc(char **, char **, int);*/
1.28 lum 75: static int founddef(char *, int, int, int, int);
76: static int foundlst(char *, int, int, int);
1.21 lum 77: static int expandvals(char *, char *, char *);
78: static int foundfun(char *, int);
1.8 lum 79: static int doregex(char *, char *);
1.10 lum 80: static void clearexp(void);
1.28 lum 81: static int parse(char *, const char *, const char *, int, int, int);
82: static int parsdef(char *, const char *, const char *, int, int, int);
83: static int parsval(char *, const char *, const char *, int, int, int);
84: static int parsexp(char *, const char *, const char *, int, int, int);
1.21 lum 85:
86: static int exitinterpreter(char *, char *, int);
1.10 lum 87:
1.12 lum 88: TAILQ_HEAD(exphead, expentry) ehead;
1.10 lum 89: struct expentry {
1.12 lum 90: TAILQ_ENTRY(expentry) eentry;
1.21 lum 91: char *fun; /* The 1st string found between parens. */
92: char funbuf[BUFSIZE];
93: const char *par1; /* Parenthesis at start of string */
94: const char *par2; /* Parenthesis at end of string */
1.10 lum 95: int expctr; /* An incremental counter:+1 for each exp */
96: int blkid; /* Which block are we in? */
97: };
1.1 lum 98:
99: /*
1.12 lum 100: * Structure for scheme keywords.
101: */
102: #define NUMSCHKEYS 4
103: #define MAXLENSCHKEYS 17 /* 17 = longest keyword (16) + 1 */
104:
105: char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] =
106: {
107: "define",
108: "list",
109: "if",
110: "lambda"
111: };
112:
1.27 lum 113: static const char lp = '(';
114: static const char rp = ')';
115: static char *defnam = NULL;
116: static int lnm;
1.12 lum 117:
118: /*
1.10 lum 119: * Line has a '(' as the first non-white char.
120: * Do some very basic parsing of line.
121: * Multi-line not supported at the moment, To do.
122: */
123: int
1.27 lum 124: foundparen(char *funstr, int llen, int lnum)
1.10 lum 125: {
1.21 lum 126: const char *lrp = NULL;
1.25 lum 127: char *p, *begp = NULL, *endp = NULL, *regs, *prechr;
1.23 lum 128: int i, ret, pctr, expctr, blkid, inquote, esc;
1.28 lum 129: int elen, spc;
1.10 lum 130:
1.28 lum 131: pctr = expctr = inquote = esc = elen = spc = 0;
1.10 lum 132: blkid = 1;
1.27 lum 133: lnm = lnum;
1.10 lum 134:
135: /*
136: * Currently can't do () or (( at the moment,
137: * just drop out - stops a segv. TODO.
138: */
139: regs = "[(]+[\t ]*[)]+";
140: if (doregex(regs, funstr))
1.27 lum 141: return(dobeep_num("Empty lists not supported at moment line",
142: lnm));
1.10 lum 143: regs = "[(]+[\t ]*[(]+";
144: if (doregex(regs, funstr))
1.27 lum 145: return(dobeep_num("Multiple consecutive left parantheses "\
146: "found line:", lnm));
1.10 lum 147: /*
148: * load expressions into a list called 'expentry', to be processd
149: * when all are obtained.
150: * Not really live code at the moment. Just part of the process of
151: * working out what needs to be done.
152: */
1.12 lum 153: TAILQ_INIT(&ehead);
154:
1.17 lum 155: /*
156: * Check for blocks of code with opening and closing ().
157: * One block = (cmd p a r a m)
158: * Two blocks = (cmd p a r a m s)(hola)
159: * Two blocks = (cmd p a r (list a m s))(hola)
160: * Only single line at moment, but more for multiline.
161: */
162: p = funstr;
163:
1.18 lum 164: for (i = 0; i < llen; ++i, p++) {
1.26 lum 165: if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') {
166: if (*p == ')')
1.27 lum 167: return(dobeep_num("Extra ')' found on line:",
168: lnm));
169: return(dobeep_num("Error line:", lnm));
1.26 lum 170: }
1.28 lum 171: if (begp != NULL)
172: elen++;
173:
1.23 lum 174: if (*p == '\\') {
175: esc = 1;
176: } else if (*p == '(') {
1.24 lum 177: if (inquote == 0) {
178: if (begp != NULL) {
179: if (endp == NULL)
180: *p = '\0';
181: else
182: *endp = '\0';
183:
184: ret = parse(begp, lrp, &lp, blkid,
1.28 lum 185: ++expctr, elen - spc);
1.24 lum 186: if (!ret) {
187: cleanup();
188: return(ret);
189: }
1.28 lum 190: elen = 0;
1.24 lum 191: }
192: lrp = &lp;
193: begp = endp = NULL;
194: pctr++;
195: } else if (inquote != 1) {
1.21 lum 196: cleanup();
1.27 lum 197: return(dobeep_num("Opening and closing quote "\
198: "char error line:", lnm));
1.21 lum 199: }
1.28 lum 200: esc = spc = 0;
1.10 lum 201: } else if (*p == ')') {
1.24 lum 202: if (inquote == 0) {
203: if (begp != NULL) {
204: if (endp == NULL)
205: *p = '\0';
206: else
207: *endp = '\0';
208:
209: ret = parse(begp, lrp, &rp, blkid,
1.28 lum 210: ++expctr, elen - spc);
1.24 lum 211: if (!ret) {
212: cleanup();
213: return(ret);
214: }
1.28 lum 215: elen = 0;
1.24 lum 216: }
217: lrp = &rp;
218: begp = endp = NULL;
219: pctr--;
220: } else if (inquote != 1) {
1.11 lum 221: cleanup();
1.27 lum 222: return(dobeep_num("Opening and closing quote "\
223: "char error line:", lnm));
1.11 lum 224: }
1.28 lum 225: esc = spc = 0;
1.10 lum 226: } else if (*p != ' ' && *p != '\t') {
1.29 ! lum 227: if (begp == NULL) {
1.18 lum 228: begp = p;
1.29 ! lum 229: if (*begp == '"')
! 230: return(dobeep_num("First char of "\
! 231: "expression error line:", lnm));
! 232: }
1.11 lum 233: if (*p == '"') {
1.25 lum 234: if (inquote == 0 && esc == 0) {
235: if (*prechr != ' ' && *prechr != '\t')
1.27 lum 236: return(dobeep_num("Parse error"\
237: " line:", lnm));
1.23 lum 238: inquote++;
1.25 lum 239: } else if (inquote > 0 && esc == 1)
1.23 lum 240: esc = 0;
1.11 lum 241: else
1.23 lum 242: inquote--;
1.25 lum 243: } else if (*prechr == '"' && inquote == 0) {
1.27 lum 244: return(dobeep_num("Parse error line:", lnm));
1.11 lum 245: }
1.10 lum 246: endp = NULL;
1.28 lum 247: spc = 0;
1.18 lum 248: } else if (endp == NULL && (*p == ' ' || *p == '\t')) {
1.28 lum 249: if (inquote == 0) {
250: *p = ' ';
251: endp = p;
252: spc++;
253: }
1.23 lum 254: esc = 0;
1.28 lum 255: } else if (*p == '\t' || *p == ' ') {
256: if (inquote == 0) {
1.11 lum 257: *p = ' ';
1.28 lum 258: spc++;
259: }
1.23 lum 260: esc = 0;
261: }
1.15 lum 262:
1.21 lum 263: if (pctr == 0) {
1.10 lum 264: blkid++;
1.21 lum 265: expctr = 0;
266: defnam = NULL;
267: }
1.25 lum 268: prechr = p;
1.10 lum 269: }
270:
1.11 lum 271: if (pctr != 0) {
272: cleanup();
1.27 lum 273: return(dobeep_num("Opening and closing parentheses error line:",
274: lnm));
1.11 lum 275: }
276: if (ret == FALSE)
277: cleanup();
278: else
279: clearexp(); /* leave lists but remove expressions */
1.10 lum 280:
281: return (ret);
1.17 lum 282: }
283:
284:
285: static int
1.28 lum 286: parse(char *begp, const char *par1, const char *par2, int blkid, int expctr,
287: int elen)
1.17 lum 288: {
1.21 lum 289: char *regs;
290: int ret = FALSE;
291:
292: if (strncmp(begp, "define", 6) == 0) {
1.28 lum 293: ret = parsdef(begp, par1, par2, blkid, expctr, elen);
1.21 lum 294: if (ret == TRUE || ret == FALSE)
295: return (ret);
296: } else if (strncmp(begp, "list", 4) == 0)
1.28 lum 297: return(parsval(begp, par1, par2, blkid, expctr, elen));
1.21 lum 298:
299: regs = "^exit$";
300: if (doregex(regs, begp))
301: return(exitinterpreter(NULL, NULL, FALSE));
1.17 lum 302:
1.21 lum 303: /* mg function name regex */
304: regs = "^[A-Za-z-]+$";
305: if (doregex(regs, begp))
1.27 lum 306: return(excline(begp, 0, 0));
1.21 lum 307:
308: /* Corner case 1 */
309: if (strncmp(begp, "global-set-key ", 15) == 0)
310: /* function name as 2nd param screws up multiarg. */
1.27 lum 311: return(excline(begp, 0, 0));
1.21 lum 312:
313: /* Corner case 2 */
314: if (strncmp(begp, "define-key ", 11) == 0)
315: /* function name as 3rd param screws up multiarg. */
1.27 lum 316: return(excline(begp, 0, 0));
1.17 lum 317:
1.28 lum 318: return (parsexp(begp, par1, par2, blkid, expctr, elen));
1.10 lum 319: }
320:
321: static int
1.28 lum 322: parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr,
323: int elen)
1.10 lum 324: {
325: char *regs;
326:
1.21 lum 327: if ((defnam == NULL) && (expctr != 1))
1.27 lum 328: return(dobeep_num("'define' incorrectly used line:", lnm));
1.12 lum 329:
1.10 lum 330: /* Does the line have a incorrect variable 'define' like: */
331: /* (define i y z) */
1.21 lum 332: regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$";
333: if (doregex(regs, begp))
1.27 lum 334: return(dobeep_num("Invalid use of define line:", lnm));
1.10 lum 335:
336: /* Does the line have a single variable 'define' like: */
337: /* (define i 0) */
1.19 lum 338: regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$";
1.21 lum 339: if (doregex(regs, begp)) {
340: if (par1 == &lp && par2 == &rp && expctr == 1)
1.28 lum 341: return(founddef(begp, blkid, expctr, 1, elen));
1.27 lum 342: return(dobeep_num("Invalid use of define line:", lnm));
1.21 lum 343: }
344: /* Does the line have '(define i(' */
345: regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$";
346: if (doregex(regs, begp)) {
347: if (par1 == &lp && par2 == &lp && expctr == 1)
1.28 lum 348: return(founddef(begp, blkid, expctr, 0, elen));
1.27 lum 349: return(dobeep_num("Invalid use of 'define' line:", lnm));
1.21 lum 350: }
351: /* Does the line have '(define (' */
352: regs = "^define$";
353: if (doregex(regs, begp)) {
354: if (par1 == &lp && par2 == &lp && expctr == 1)
355: return(foundfun(begp, expctr));
1.27 lum 356: return(dobeep_num("Invalid use of 'define' line:", lnm));
1.21 lum 357: }
358:
359: return (ABORT);
360: }
361:
362: static int
1.28 lum 363: parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr,
364: int elen)
1.21 lum 365: {
366: char *regs;
1.10 lum 367:
1.21 lum 368: /* Does the line have 'list' */
369: regs = "^list$";
370: if (doregex(regs, begp))
1.27 lum 371: return(dobeep_num("Invalid use of list line:", lnm));
1.21 lum 372:
373: /* Does the line have a 'list' like: */
374: /* (list "a" "b") */
375: regs = "^list[ ]+.*$";
376: if (doregex(regs, begp)) {
377: if (expctr == 1)
1.27 lum 378: return(dobeep_num("list with no-where to go.", lnm));
1.10 lum 379:
1.21 lum 380: if (par1 == &lp && expctr > 1)
1.28 lum 381: return(foundlst(begp, blkid, expctr, elen));
1.16 lum 382:
1.27 lum 383: return(dobeep_num("Invalid use of list line:", lnm));
1.21 lum 384: }
385: return (FALSE);
1.10 lum 386: }
387:
1.1 lum 388: static int
1.28 lum 389: parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr,
390: int elen)
1.1 lum 391: {
1.21 lum 392: struct expentry *e1 = NULL;
393: PF funcp;
394: char *cmdp, *fendp, *valp, *fname, *funb = NULL;;
395: int numparams, ret;
1.1 lum 396:
1.21 lum 397: cmdp = begp;
1.10 lum 398: fendp = strchr(cmdp, ' ');
1.1 lum 399: *fendp = '\0';
1.21 lum 400:
1.1 lum 401: /*
402: * If no extant mg command found, just return.
403: */
404: if ((funcp = name_function(cmdp)) == NULL)
405: return (dobeep_msgs("Unknown command: ", cmdp));
406:
407: numparams = numparams_function(funcp);
408: if (numparams == 0)
1.21 lum 409: return (dobeep_msgs("Command takes no arguments:", cmdp));
1.22 lum 410:
411: if (numparams == -1)
412: return (dobeep_msgs("Interactive command found:", cmdp));
1.21 lum 413:
414: if ((e1 = malloc(sizeof(struct expentry))) == NULL) {
415: cleanup();
416: return (dobeep_msg("malloc Error"));
417: }
418: TAILQ_INSERT_HEAD(&ehead, e1, eentry);
419: if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) {
420: cleanup();
421: return(dobeep_msg("strndup error"));
422: }
423: cmdp = e1->fun;
424: fname = e1->fun;
425: e1->funbuf[0] = '\0';
426: funb = e1->funbuf;
427: e1->expctr = expctr;
428: e1->blkid = blkid;
429: /* need to think about these two */
430: e1->par1 = par1;
431: e1->par2 = par2;
432:
433: *fendp = ' ';
434: valp = fendp + 1;
435:
436: ret = expandvals(cmdp, valp, funb);
437: if (!ret)
438: return (ret);
439:
440: return (multiarg(fname, funb, numparams));
441: }
1.1 lum 442:
1.21 lum 443: /*
444: * Pass a list of arguments to a function.
445: */
446: static int
447: multiarg(char *cmdp, char *argbuf, int numparams)
448: {
449: char excbuf[BUFSIZE];
450: char *argp, *p, *s = " ";
451: char *regs;
452: int spc, numspc;
453: int fin, inquote;
1.10 lum 454:
1.1 lum 455: argp = argbuf;
1.21 lum 456: spc = 1; /* initially fake a space so we find first argument */
457: numspc = fin = inquote = 0;
1.10 lum 458:
459: for (p = argbuf; *p != '\0'; p++) {
460: if (*(p + 1) == '\0')
461: fin = 1;
1.1 lum 462:
1.10 lum 463: if (*p != ' ') {
1.11 lum 464: if (*p == '"') {
465: if (inquote == 1)
466: inquote = 0;
467: else
468: inquote = 1;
469: }
1.10 lum 470: if (spc == 1)
1.21 lum 471: if ((numspc % numparams) == 0) {
472: argp = p;
473: }
1.10 lum 474: spc = 0;
1.1 lum 475: }
1.11 lum 476: if ((*p == ' ' && inquote == 0) || fin) {
1.21 lum 477: if (spc == 1)/* || (numspc % numparams == 0))*/
478: continue;
479: if ((numspc % numparams) != (numparams - 1)) {
480: numspc++;
1.1 lum 481: continue;
1.21 lum 482: }
1.10 lum 483: if (*p == ' ') {
1.11 lum 484: *p = '\0'; /* terminate arg string */
1.10 lum 485: }
486: excbuf[0] = '\0';
1.11 lum 487: regs = "[\"]+.*[\"]+";
1.10 lum 488:
1.21 lum 489: if (!doregex(regs, argp)) {
1.13 lum 490: const char *errstr;
491: int iters;
492:
493: iters = strtonum(argp, 0, INT_MAX, &errstr);
494: if (errstr != NULL)
495: return (dobeep_msgs("Var not found:",
496: argp));
497: }
1.11 lum 498:
1.10 lum 499: if (strlcpy(excbuf, cmdp, sizeof(excbuf))
500: >= sizeof(excbuf))
501: return (dobeep_msg("strlcpy error"));
502: if (strlcat(excbuf, s, sizeof(excbuf))
503: >= sizeof(excbuf))
504: return (dobeep_msg("strlcat error"));
505: if (strlcat(excbuf, argp, sizeof(excbuf))
506: >= sizeof(excbuf))
507: return (dobeep_msg("strlcat error"));
508:
1.27 lum 509: excline(excbuf, 0, 0);
1.10 lum 510:
511: if (fin)
512: break;
513:
514: *p = ' '; /* unterminate arg string */
1.21 lum 515: numspc++;
1.1 lum 516: spc = 1;
517: }
518: }
519: return (TRUE);
520: }
521:
522: /*
523: * Is an item a value or a variable?
524: */
525: static int
1.10 lum 526: isvar(char **argp, char **varbuf, int sizof)
1.1 lum 527: {
528: struct varentry *v1 = NULL;
529:
530: if (SLIST_EMPTY(&varhead))
531: return (FALSE);
1.2 lum 532: #ifdef MGLOG
1.10 lum 533: mglog_isvar(*varbuf, *argp, sizof);
1.2 lum 534: #endif
1.1 lum 535: SLIST_FOREACH(v1, &varhead, entry) {
1.23 lum 536: if (strcmp(*argp, v1->v_name) == 0) {
537: (void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof);
1.1 lum 538: return (TRUE);
539: }
540: }
541: return (FALSE);
542: }
543:
1.21 lum 544:
545: static int
546: foundfun(char *defstr, int expctr)
547: {
548: return (TRUE);
549: }
550:
551: static int
1.28 lum 552: foundlst(char *defstr, int blkid, int expctr, int elen)
1.21 lum 553: {
554: char *p;
555:
556: p = strstr(defstr, " ");
557: p = skipwhite(p);
558: expandvals(NULL, p, defnam);
559:
560: return (TRUE);
561: }
562:
1.1 lum 563: /*
1.21 lum 564: * 'define' strings follow the regex in parsdef().
1.1 lum 565: */
566: static int
1.28 lum 567: founddef(char *defstr, int blkid, int expctr, int hasval, int elen)
1.1 lum 568: {
569: struct varentry *vt, *v1 = NULL;
1.10 lum 570: char *p, *vnamep, *vendp = NULL, *valp;
1.11 lum 571:
1.10 lum 572: p = strstr(defstr, " "); /* move to first ' ' char. */
1.11 lum 573: vnamep = skipwhite(p); /* find first char of var name. */
1.1 lum 574: vendp = vnamep;
575:
1.21 lum 576: /* now find the end of the define/list name */
1.1 lum 577: while (1) {
578: ++vendp;
1.10 lum 579: if (*vendp == ' ')
1.1 lum 580: break;
581: }
582: *vendp = '\0';
1.10 lum 583:
1.1 lum 584: /*
1.21 lum 585: * Check list name is not an existing mg function.
1.1 lum 586: */
587: if (name_function(vnamep) != NULL)
588: return(dobeep_msgs("Variable/function name clash:", vnamep));
589:
590: if (!SLIST_EMPTY(&varhead)) {
591: SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) {
1.23 lum 592: if (strcmp(vnamep, v1->v_name) == 0)
1.1 lum 593: SLIST_REMOVE(&varhead, v1, varentry, entry);
594: }
595: }
596: if ((v1 = malloc(sizeof(struct varentry))) == NULL)
597: return (ABORT);
598: SLIST_INSERT_HEAD(&varhead, v1, entry);
1.23 lum 599: if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL)
1.1 lum 600: return(dobeep_msg("strndup error"));
1.23 lum 601: vnamep = v1->v_name;
602: v1->v_count = 0;
603: v1->v_vals = NULL;
604: v1->v_buf[0] = '\0';
1.21 lum 605:
1.23 lum 606: defnam = v1->v_buf;
1.21 lum 607:
608: if (hasval) {
609: valp = skipwhite(vendp + 1);
610:
611: expandvals(NULL, valp, defnam);
612: defnam = NULL;
613: }
614: *vendp = ' ';
615: return (TRUE);
616: }
617:
618:
619: static int
620: expandvals(char *cmdp, char *valp, char *bp)
621: {
622: char excbuf[BUFSIZE], argbuf[BUFSIZE];
623: char contbuf[BUFSIZE], varbuf[BUFSIZE];
624: char *argp, *endp, *p, *v, *s = " ";
625: char *regs;
626: int spc, cnt;
627: int inlist, sizof, fin, inquote;
628:
629: /* now find the first argument */
630: p = skipwhite(valp);
631:
632: if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf))
633: return (dobeep_msg("strlcpy error"));
634: argp = argbuf;
635: spc = 1; /* initially fake a space so we find first argument */
636: inlist = fin = inquote = cnt = spc = 0;
637:
638: for (p = argbuf; *p != '\0'; p++) {
639: if (*(p + 1) == '\0')
640: fin = 1;
641:
642: if (*p != ' ') {
643: if (*p == '"') {
644: if (inquote == 1)
645: inquote = 0;
646: else
647: inquote = 1;
648: }
1.1 lum 649: if (spc == 1)
1.21 lum 650: argp = p;
1.1 lum 651: spc = 0;
652: }
1.21 lum 653: if ((*p == ' ' && inquote == 0) || fin) {
654: if (spc == 1)
655: continue;
656: /* terminate arg string */
657: if (*p == ' ') {
658: *p = '\0';
659: }
660: endp = p + 1;
661: excbuf[0] = '\0';
662: varbuf[0] = '\0';
663: contbuf[0] = '\0';
664: sizof = sizeof(varbuf);
665: v = varbuf;
666: regs = "[\"]+.*[\"]+";
667: if (doregex(regs, argp))
668: ; /* found quotes */
669: else if (isvar(&argp, &v, sizof)) {
670:
671: (void)(strlcat(varbuf, " ",
672: sizof) >= sizof);
673:
674: *p = ' ';
675: (void)(strlcpy(contbuf, endp,
676: sizeof(contbuf)) >= sizeof(contbuf));
677:
678: (void)(strlcat(varbuf, contbuf,
679: sizof) >= sizof);
680:
681: argbuf[0] = ' ';
682: argbuf[1] = '\0';
683: (void)(strlcat(argbuf, varbuf,
684: sizof) >= sizof);
685:
686: p = argp = argbuf;
687: spc = 1;
688: fin = 0;
689: continue;
690: } else {
691: const char *errstr;
692: int iters;
1.1 lum 693:
1.21 lum 694: iters = strtonum(argp, 0, INT_MAX, &errstr);
695: if (errstr != NULL)
696: return (dobeep_msgs("Var not found:",
697: argp));
698: }
1.7 lum 699: #ifdef MGLOG
1.21 lum 700: mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE);
1.7 lum 701: #endif
1.21 lum 702: if (*bp != '\0') {
703: if (strlcat(bp, s, BUFSIZE) >= BUFSIZE)
704: return (dobeep_msg("strlcat error"));
705: }
706: if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) {
707: return (dobeep_msg("strlcat error"));
708: }
1.23 lum 709: /* v1->v_count++;*/
1.21 lum 710:
711: if (fin)
712: break;
1.1 lum 713:
1.21 lum 714: *p = ' '; /* unterminate arg string */
715: spc = 1;
716: }
717: }
1.1 lum 718: return (TRUE);
719: }
720:
721: /*
1.10 lum 722: * Finished with buffer evaluation, so clean up any vars.
723: * Perhaps keeps them in mg even after use,...
1.1 lum 724: */
1.23 lum 725: /*static int
1.1 lum 726: clearvars(void)
727: {
728: struct varentry *v1 = NULL;
729:
730: while (!SLIST_EMPTY(&varhead)) {
731: v1 = SLIST_FIRST(&varhead);
732: SLIST_REMOVE_HEAD(&varhead, entry);
1.23 lum 733: free(v1->v_name);
1.1 lum 734: free(v1);
735: }
736: return (FALSE);
737: }
1.23 lum 738: */
1.1 lum 739: /*
1.10 lum 740: * Finished with block evaluation, so clean up any expressions.
1.1 lum 741: */
1.10 lum 742: static void
743: clearexp(void)
1.1 lum 744: {
1.10 lum 745: struct expentry *e1 = NULL;
1.9 lum 746:
1.12 lum 747: while (!TAILQ_EMPTY(&ehead)) {
748: e1 = TAILQ_FIRST(&ehead);
749: TAILQ_REMOVE(&ehead, e1, eentry);
1.21 lum 750: free(e1->fun);
1.10 lum 751: free(e1);
1.9 lum 752: }
1.10 lum 753: return;
1.11 lum 754: }
755:
756: /*
757: * Cleanup before leaving.
758: */
759: void
760: cleanup(void)
761: {
1.21 lum 762: defnam = NULL;
763:
1.11 lum 764: clearexp();
1.23 lum 765: /* clearvars();*/
1.8 lum 766: }
767:
768: /*
769: * Test a string against a regular expression.
770: */
1.10 lum 771: static int
1.8 lum 772: doregex(char *r, char *e)
773: {
774: regex_t regex_buff;
775:
776: if (regcomp(®ex_buff, r, REG_EXTENDED)) {
1.1 lum 777: regfree(®ex_buff);
1.27 lum 778: return(dobeep_num("Regex compilation error line:", lnm));
1.1 lum 779: }
1.8 lum 780: if (!regexec(®ex_buff, e, 0, NULL, 0)) {
781: regfree(®ex_buff);
782: return(TRUE);
1.1 lum 783: }
1.9 lum 784: regfree(®ex_buff);
1.16 lum 785: return(FALSE);
786: }
787:
788: /*
789: * Display a message so it is apparent that this is the method which stopped
790: * execution.
791: */
792: static int
1.21 lum 793: exitinterpreter(char *ptr, char *dobuf, int dosiz)
1.16 lum 794: {
795: cleanup();
796: if (batch == 0)
797: return(dobeep_msg("Interpreter exited via exit command."));
1.9 lum 798: return(FALSE);
1.1 lum 799: }
1.21 lum 800:
801: /*
802: * All code below commented out (until end of file).
803: *
804: * Need to think about how interpreter functions are done.
805: * Probably don't have a choice with string-append().
806:
807: static int getenvironmentvariable(char *, char *, int);
808: static int stringappend(char *, char *, int);
809:
810: typedef int (*PFI)(char *, char *, int);
811:
812:
813: struct ifunmap {
814: PFI fn_funct;
815: const char *fn_name;
816: struct ifunmap *fn_next;
817: };
818: static struct ifunmap *ifuns;
819:
820: static struct ifunmap ifunctnames[] = {
821: {exitinterpreter, "exit"},
822: {getenvironmentvariable, "get-environment-variable"},
823: {stringappend, "string-append"},
824: {NULL, NULL}
825: };
826:
827: void
828: ifunmap_init(void)
829: {
830: struct ifunmap *fn;
831:
832: for (fn = ifunctnames; fn->fn_name != NULL; fn++) {
833: fn->fn_next = ifuns;
834: ifuns = fn;
835: }
836: }
837:
838: PFI
839: name_ifun(const char *ifname)
840: {
841: struct ifunmap *fn;
842:
843: for (fn = ifuns; fn != NULL; fn = fn->fn_next) {
844: if (strcmp(fn->fn_name, ifname) == 0)
845: return (fn->fn_funct);
846: }
847:
848: return (NULL);
849: }
850:
851:
852: int
853: dofunc(char **ifname, char **tmpbuf, int sizof)
854: {
855: PFI fnc;
856: char *p, *tmp;
857:
858: p = strstr(*ifname, " ");
859: *p = '\0';
860:
861: fnc = name_ifun(*ifname);
862: if (fnc == NULL)
863: return (FALSE);
864:
865: *p = ' ';
866:
867: tmp = *tmpbuf;
868:
869: fnc(p, tmp, sizof);
870:
871: return (TRUE);
872: }
873:
874: static int
875: getenvironmentvariable(char *ptr, char *dobuf, int dosiz)
876: {
877: char *t;
878: char *tmp;
879: const char *q = "\"";
880:
881: t = skipwhite(ptr);
882:
883: if (t[0] == *q || t[strlen(t) - 1] == *q)
884: return (dobeep_msgs("Please remove '\"' around:", t));
885: if ((tmp = getenv(t)) == NULL || *tmp == '\0')
886: return(dobeep_msgs("Envar not found:", t));
887:
888: dobuf[0] = '\0';
889: if (strlcat(dobuf, q, dosiz) >= dosiz)
890: return (dobeep_msg("strlcat error"));
891: if (strlcat(dobuf, tmp, dosiz) >= dosiz)
892: return (dobeep_msg("strlcat error"));
893: if (strlcat(dobuf, q, dosiz) >= dosiz)
894: return (dobeep_msg("strlcat error"));
895:
896: return (TRUE);
897: }
898:
899: static int
900: stringappend(char *ptr, char *dobuf, int dosiz)
901: {
902: char varbuf[BUFSIZE], funbuf[BUFSIZE];
903: char *p, *f, *v, *vendp;
904: int sizof, fin = 0;
905:
906: varbuf[0] = funbuf[0] = '\0';
907: f = funbuf;
908: v = varbuf;
909: sizof = sizeof(varbuf);
910: *dobuf = '\0';
911:
912: p = skipwhite(ptr);
913:
914: while (*p != '\0') {
915: vendp = p;
916: while (1) {
917: if (*vendp == ' ') {
918: break;
919: } else if (*vendp == '\0') {
920: fin = 1;
921: break;
922: }
923: ++vendp;
924: }
925: *vendp = '\0';
926:
927: if (isvar(&p, &v, sizof)) {
928: if (v[0] == '"' && v[strlen(v) - 1] == '"' ) {
929: v[strlen(v) - 1] = '\0';
930: v = v + 1;
931: }
932: if (strlcat(f, v, sizof) >= sizof)
933: return (dobeep_msg("strlcat error"));
934: } else {
935: if (p[0] == '"' && p[strlen(p) - 1] == '"' ) {
936: p[strlen(p) - 1] = '\0';
937: p = p + 1;
938: }
939: if (strlcat(f, p, sizof) >= sizof)
940: return (dobeep_msg("strlcat error"));
941: }
942: if (fin)
943: break;
944: vendp++;
945: if (*vendp == '\0')
946: break;
947: p = skipwhite(vendp);
948: }
949:
950: (void)snprintf(dobuf, dosiz, "\"%s\"", f);
951:
952: return (TRUE);
953: }
954:
955: Index: main.c
956: ===================================================================
957: RCS file: /cvs/src/usr.bin/mg/main.c,v
958: retrieving revision 1.89
959: diff -u -p -u -p -r1.89 main.c
960: --- main.c 20 Mar 2021 09:00:49 -0000 1.89
961: +++ main.c 12 Apr 2021 17:58:52 -0000
962: @@ -133,10 +133,12 @@ main(int argc, char **argv)
963: extern void grep_init(void);
964: extern void cmode_init(void);
965: extern void dired_init(void);
966: + extern void ifunmap_init(void);
967:
968: dired_init();
969: grep_init();
970: cmode_init();
971: + ifunmap_init();
972: }
973:
974:
975: */