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