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