1*479c151dSjsg /* $OpenBSD: interpreter.c,v 1.36 2024/09/20 02:00:46 jsg Exp $ */ 23c2c8acbSlum /* 33c2c8acbSlum * This file is in the public domain. 43c2c8acbSlum * 53c2c8acbSlum * Author: Mark Lumsden <mark@showcomplex.com> 63c2c8acbSlum */ 73c2c8acbSlum 83c2c8acbSlum /* 93c2c8acbSlum * This file attempts to add some 'scripting' functionality into mg. 103c2c8acbSlum * 117b1feaf2Sguenther * The initial goal is to give mg the ability to use its existing functions 123c2c8acbSlum * and structures in a linked-up way. Hopefully resulting in user definable 133c2c8acbSlum * functions. The syntax is 'scheme' like but currently it is not a scheme 143c2c8acbSlum * interpreter. 153c2c8acbSlum * 163c2c8acbSlum * At the moment there is no manual page reference to this file. The code below 173c2c8acbSlum * is liable to change, so use at your own risk! 183c2c8acbSlum * 193c2c8acbSlum * If you do want to do some testing, you can add some lines to your .mg file 203c2c8acbSlum * like: 213c2c8acbSlum * 223c2c8acbSlum * 1. Give multiple arguments to a function that usually would accept only one: 23036c776aSlum * (find-file "a.txt" "b.txt" "c.txt") 243c2c8acbSlum * 25fefadbf1Slum * 2. Define a single value variable: 26036c776aSlum * (define myfile "d.txt") 273c2c8acbSlum * 28fefadbf1Slum * 3. Define a list: 29036c776aSlum * (define myfiles(list "e.txt" "f.txt")) 30fefadbf1Slum * 31fefadbf1Slum * 4. Use the previously defined variable or list: 323c2c8acbSlum * (find-file myfiles) 333c2c8acbSlum * 343c2c8acbSlum * To do: 353c2c8acbSlum * 1. multiline parsing - currently only single lines supported. 363c2c8acbSlum * 2. parsing for '(' and ')' throughout whole string and evaluate correctly. 373c2c8acbSlum * 3. conditional execution. 387ec1563aSlum * 4. have memory allocated dynamically for variable values. 397b3a2786Slum * 5. do symbol names need more complex regex patterns? [A-Za-z][.0-9_A-Z+a-z-] 407b3a2786Slum * at the moment. 41e5166e5aSlum * 6. display line numbers with parsing errors. 42e5166e5aSlum * 7. oh so many things.... 433c2c8acbSlum * [...] 443c2c8acbSlum * n. implement user definable functions. 45036c776aSlum * 4693960a54Slum * Notes: 4781b9a829Slum * - Currently calls to excline() from this file have the line length and 4881b9a829Slum * line number set to zero. 4981b9a829Slum * That's because excline() uses '\0' as the end of line indicator 5093960a54Slum * and only the call to foundparen() within excline() uses excline's 2nd 5181b9a829Slum * and 3rd arguments. 5281b9a829Slum * Importantly, any lines sent to there from here will not be 5393960a54Slum * coming back here. 543c2c8acbSlum */ 553c2c8acbSlum #include <sys/queue.h> 568fa75880Slum 5742033fc5Slum #include <ctype.h> 588fa75880Slum #include <limits.h> 593c2c8acbSlum #include <regex.h> 603c2c8acbSlum #include <signal.h> 613c2c8acbSlum #include <stdio.h> 623c2c8acbSlum #include <stdlib.h> 633c2c8acbSlum #include <string.h> 643c2c8acbSlum 653c2c8acbSlum #include "def.h" 663c2c8acbSlum #include "funmap.h" 673c2c8acbSlum 687bf68b76Slum #ifdef MGLOG 697bf68b76Slum #include "kbd.h" 707bf68b76Slum #include "log.h" 717bf68b76Slum #endif 727bf68b76Slum 7317c1e8f1Slum static int multiarg(char *, char *, int); 743c2c8acbSlum static int isvar(char **, char **, int); 7517c1e8f1Slum /*static int dofunc(char **, char **, int);*/ 765e37f6aeSlum static int founddef(char *, int, int, int, int); 775e37f6aeSlum static int foundlst(char *, int, int, int); 7817c1e8f1Slum static int expandvals(char *, char *, char *); 7917c1e8f1Slum static int foundfun(char *, int); 80228b1246Slum static int doregex(char *, char *); 81bbed9a29Slum static void clearexp(void); 8241167f9aSlum static int parse(char *, const char *, const char *, int, int, int, int); 835e37f6aeSlum static int parsdef(char *, const char *, const char *, int, int, int); 845e37f6aeSlum static int parsval(char *, const char *, const char *, int, int, int); 855e37f6aeSlum static int parsexp(char *, const char *, const char *, int, int, int); 8617c1e8f1Slum 8717c1e8f1Slum static int exitinterpreter(char *, char *, int); 88bbed9a29Slum 894937e3a7Slum TAILQ_HEAD(exphead, expentry) ehead; 90bbed9a29Slum struct expentry { 914937e3a7Slum TAILQ_ENTRY(expentry) eentry; 9217c1e8f1Slum char *fun; /* The 1st string found between parens. */ 9317c1e8f1Slum char funbuf[BUFSIZE]; 9417c1e8f1Slum const char *par1; /* Parenthesis at start of string */ 9517c1e8f1Slum const char *par2; /* Parenthesis at end of string */ 96bbed9a29Slum int expctr; /* An incremental counter:+1 for each exp */ 97bbed9a29Slum int blkid; /* Which block are we in? */ 98bbed9a29Slum }; 993c2c8acbSlum 1003c2c8acbSlum /* 1014937e3a7Slum * Structure for scheme keywords. 1024937e3a7Slum */ 1034937e3a7Slum #define NUMSCHKEYS 4 1044937e3a7Slum #define MAXLENSCHKEYS 17 /* 17 = longest keyword (16) + 1 */ 1054937e3a7Slum 1064937e3a7Slum char scharkey[NUMSCHKEYS][MAXLENSCHKEYS] = 1074937e3a7Slum { 1084937e3a7Slum "define", 1094937e3a7Slum "list", 1104937e3a7Slum "if", 1114937e3a7Slum "lambda" 1124937e3a7Slum }; 1134937e3a7Slum 11481b9a829Slum static const char lp = '('; 11581b9a829Slum static const char rp = ')'; 11681b9a829Slum static char *defnam = NULL; 11781b9a829Slum static int lnm; 1184937e3a7Slum 1194937e3a7Slum /* 120bbed9a29Slum * Line has a '(' as the first non-white char. 121bbed9a29Slum * Do some very basic parsing of line. 122bbed9a29Slum * Multi-line not supported at the moment, To do. 123bbed9a29Slum */ 124bbed9a29Slum int 12581b9a829Slum foundparen(char *funstr, int llen, int lnum) 126bbed9a29Slum { 12717c1e8f1Slum const char *lrp = NULL; 12855a1b3e1Slum char *p, *begp = NULL, *endp = NULL, *prechr; 12955a1b3e1Slum char *lastchr = NULL; 1307ec1563aSlum int i, ret, pctr, expctr, blkid, inquote, esc; 13141167f9aSlum int elen, spc, ns; 132bbed9a29Slum 13341167f9aSlum pctr = expctr = inquote = esc = elen = spc = ns = 0; 134bbed9a29Slum blkid = 1; 13581b9a829Slum lnm = lnum; 136bbed9a29Slum 137bbed9a29Slum /* 138bbed9a29Slum * load expressions into a list called 'expentry', to be processd 139bbed9a29Slum * when all are obtained. 140bbed9a29Slum * Not really live code at the moment. Just part of the process of 141bbed9a29Slum * working out what needs to be done. 142bbed9a29Slum */ 1434937e3a7Slum TAILQ_INIT(&ehead); 1444937e3a7Slum 14584dad9ebSlum /* 14684dad9ebSlum * Check for blocks of code with opening and closing (). 14784dad9ebSlum * One block = (cmd p a r a m) 14884dad9ebSlum * Two blocks = (cmd p a r a m s)(hola) 14984dad9ebSlum * Two blocks = (cmd p a r (list a m s))(hola) 15084dad9ebSlum * Only single line at moment, but more for multiline. 15184dad9ebSlum */ 15284dad9ebSlum p = funstr; 15384dad9ebSlum 154bdb72052Slum for (i = 0; i < llen; ++i, p++) { 155818faf02Slum if (pctr == 0 && *p != ' ' && *p != '\t' && *p != '(') { 156818faf02Slum if (*p == ')') 15781b9a829Slum return(dobeep_num("Extra ')' found on line:", 15881b9a829Slum lnm)); 15981b9a829Slum return(dobeep_num("Error line:", lnm)); 160818faf02Slum } 1615e37f6aeSlum if (begp != NULL) 1625e37f6aeSlum elen++; 1635e37f6aeSlum 1647ec1563aSlum if (*p == '\\') { 1657ec1563aSlum esc = 1; 1667ec1563aSlum } else if (*p == '(') { 16755a1b3e1Slum if (lastchr != NULL && *lastchr == '(') 16855a1b3e1Slum return(dobeep_num("Multiple consecutive "\ 16955a1b3e1Slum "left parantheses line", lnm)); 17005d4bb1aSlum if (inquote == 0) { 171bdb72052Slum if (begp != NULL) { 17241167f9aSlum if (*prechr == ' ') 17341167f9aSlum ns--; 174bbed9a29Slum if (endp == NULL) 175bbed9a29Slum *p = '\0'; 176bbed9a29Slum else 177bbed9a29Slum *endp = '\0'; 17884dad9ebSlum 17905d4bb1aSlum ret = parse(begp, lrp, &lp, blkid, 18041167f9aSlum ++expctr, elen - spc, ns); 18184dad9ebSlum if (!ret) { 182036c776aSlum cleanup(); 18384dad9ebSlum return(ret); 184bbed9a29Slum } 1855e37f6aeSlum elen = 0; 186036c776aSlum } 18717c1e8f1Slum lrp = &lp; 188bdb72052Slum begp = endp = NULL; 189bbed9a29Slum pctr++; 19005d4bb1aSlum } else if (inquote != 1) { 191036c776aSlum cleanup(); 19281b9a829Slum return(dobeep_num("Opening and closing quote "\ 19381b9a829Slum "char error line:", lnm)); 194036c776aSlum } 1955e37f6aeSlum esc = spc = 0; 19605d4bb1aSlum } else if (*p == ')') { 19755a1b3e1Slum if (lastchr != NULL && *lastchr == '(') 19855a1b3e1Slum return(dobeep_num("Empty parenthesis "\ 19955a1b3e1Slum "not supported line", lnm)); 20005d4bb1aSlum if (inquote == 0) { 201bdb72052Slum if (begp != NULL) { 20241167f9aSlum if (*prechr == ' ') 20341167f9aSlum ns--; 204bbed9a29Slum if (endp == NULL) 205bbed9a29Slum *p = '\0'; 206bbed9a29Slum else 207bbed9a29Slum *endp = '\0'; 20884dad9ebSlum 20905d4bb1aSlum ret = parse(begp, lrp, &rp, blkid, 21041167f9aSlum ++expctr, elen - spc, ns); 21184dad9ebSlum if (!ret) { 212036c776aSlum cleanup(); 21384dad9ebSlum return(ret); 21484dad9ebSlum } 2155e37f6aeSlum elen = 0; 216036c776aSlum } 21717c1e8f1Slum lrp = &rp; 218bdb72052Slum begp = endp = NULL; 219bbed9a29Slum pctr--; 22005d4bb1aSlum } else if (inquote != 1) { 22105d4bb1aSlum cleanup(); 22281b9a829Slum return(dobeep_num("Opening and closing quote "\ 22381b9a829Slum "char error line:", lnm)); 22405d4bb1aSlum } 2255e37f6aeSlum esc = spc = 0; 226bbed9a29Slum } else if (*p != ' ' && *p != '\t') { 22723ef0c45Slum if (begp == NULL) { 228edf7cdfaSlum begp = p; 22942033fc5Slum if (*begp == '"' || isdigit(*begp)) 23023ef0c45Slum return(dobeep_num("First char of "\ 23123ef0c45Slum "expression error line:", lnm)); 23223ef0c45Slum } 233036c776aSlum if (*p == '"') { 234e5166e5aSlum if (inquote == 0 && esc == 0) { 235e5166e5aSlum if (*prechr != ' ' && *prechr != '\t') 23681b9a829Slum return(dobeep_num("Parse error"\ 23781b9a829Slum " line:", lnm)); 2387ec1563aSlum inquote++; 239e5166e5aSlum } else if (inquote > 0 && esc == 1) 2407ec1563aSlum esc = 0; 241036c776aSlum else 2427ec1563aSlum inquote--; 243e5166e5aSlum } else if (*prechr == '"' && inquote == 0) { 24481b9a829Slum return(dobeep_num("Parse error line:", lnm)); 245036c776aSlum } 246bbed9a29Slum endp = NULL; 2475e37f6aeSlum spc = 0; 248bdb72052Slum } else if (endp == NULL && (*p == ' ' || *p == '\t')) { 2495e37f6aeSlum if (inquote == 0) { 250bbed9a29Slum *p = ' '; 251bbed9a29Slum endp = p; 2525e37f6aeSlum spc++; 25341167f9aSlum if (begp != NULL) 25441167f9aSlum ns++; 2555e37f6aeSlum } 2567ec1563aSlum esc = 0; 2575e37f6aeSlum } else if (*p == '\t' || *p == ' ') { 2585e37f6aeSlum if (inquote == 0) { 259bbed9a29Slum *p = ' '; 2605e37f6aeSlum spc++; 2615e37f6aeSlum } 2627ec1563aSlum esc = 0; 2637ec1563aSlum } 26455a1b3e1Slum if (*p != '\t' && *p != ' ' && inquote == 0) 26555a1b3e1Slum lastchr = p; 266edf7cdfaSlum 26717c1e8f1Slum if (pctr == 0) { 268bbed9a29Slum blkid++; 26917c1e8f1Slum expctr = 0; 27017c1e8f1Slum defnam = NULL; 27117c1e8f1Slum } 272e5166e5aSlum prechr = p; 273bbed9a29Slum } 274bbed9a29Slum 275036c776aSlum if (pctr != 0) { 276036c776aSlum cleanup(); 27781b9a829Slum return(dobeep_num("Opening and closing parentheses error line:", 27881b9a829Slum lnm)); 279036c776aSlum } 280036c776aSlum if (ret == FALSE) 281036c776aSlum cleanup(); 282036c776aSlum else 283036c776aSlum clearexp(); /* leave lists but remove expressions */ 284bbed9a29Slum 285bbed9a29Slum return (ret); 286bbed9a29Slum } 287bbed9a29Slum 28884dad9ebSlum 28984dad9ebSlum static int 2905e37f6aeSlum parse(char *begp, const char *par1, const char *par2, int blkid, int expctr, 29141167f9aSlum int elen, int ns) 29284dad9ebSlum { 29317c1e8f1Slum char *regs; 29417c1e8f1Slum int ret = FALSE; 29584dad9ebSlum 29617c1e8f1Slum if (strncmp(begp, "define", 6) == 0) { 2975e37f6aeSlum ret = parsdef(begp, par1, par2, blkid, expctr, elen); 29817c1e8f1Slum if (ret == TRUE || ret == FALSE) 29917c1e8f1Slum return (ret); 30017c1e8f1Slum } else if (strncmp(begp, "list", 4) == 0) 3015e37f6aeSlum return(parsval(begp, par1, par2, blkid, expctr, elen)); 30284dad9ebSlum 30317c1e8f1Slum regs = "^exit$"; 30417c1e8f1Slum if (doregex(regs, begp)) 30517c1e8f1Slum return(exitinterpreter(NULL, NULL, FALSE)); 30617c1e8f1Slum 30717c1e8f1Slum /* mg function name regex */ 30817c1e8f1Slum regs = "^[A-Za-z-]+$"; 30917c1e8f1Slum if (doregex(regs, begp)) 31081b9a829Slum return(excline(begp, 0, 0)); 31117c1e8f1Slum 31217c1e8f1Slum /* Corner case 1 */ 31317c1e8f1Slum if (strncmp(begp, "global-set-key ", 15) == 0) 31417c1e8f1Slum /* function name as 2nd param screws up multiarg. */ 31581b9a829Slum return(excline(begp, 0, 0)); 31617c1e8f1Slum 31717c1e8f1Slum /* Corner case 2 */ 31817c1e8f1Slum if (strncmp(begp, "define-key ", 11) == 0) 31917c1e8f1Slum /* function name as 3rd param screws up multiarg. */ 32081b9a829Slum return(excline(begp, 0, 0)); 32117c1e8f1Slum 3225e37f6aeSlum return (parsexp(begp, par1, par2, blkid, expctr, elen)); 32384dad9ebSlum } 32484dad9ebSlum 325bbed9a29Slum static int 3265e37f6aeSlum parsdef(char *begp, const char *par1, const char *par2, int blkid, int expctr, 3275e37f6aeSlum int elen) 328bbed9a29Slum { 329bbed9a29Slum char *regs; 330bbed9a29Slum 33117c1e8f1Slum if ((defnam == NULL) && (expctr != 1)) 33281b9a829Slum return(dobeep_num("'define' incorrectly used line:", lnm)); 3334937e3a7Slum 334bbed9a29Slum /* Does the line have a incorrect variable 'define' like: */ 335bbed9a29Slum /* (define i y z) */ 33617c1e8f1Slum regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.+[ ]+.+$"; 33717c1e8f1Slum if (doregex(regs, begp)) 33881b9a829Slum return(dobeep_num("Invalid use of define line:", lnm)); 339bbed9a29Slum 340bbed9a29Slum /* Does the line have a single variable 'define' like: */ 341bbed9a29Slum /* (define i 0) */ 3427b3a2786Slum regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]+.*$"; 34317c1e8f1Slum if (doregex(regs, begp)) { 34417c1e8f1Slum if (par1 == &lp && par2 == &rp && expctr == 1) 3455e37f6aeSlum return(founddef(begp, blkid, expctr, 1, elen)); 34681b9a829Slum return(dobeep_num("Invalid use of define line:", lnm)); 34717c1e8f1Slum } 34817c1e8f1Slum /* Does the line have '(define i(' */ 34917c1e8f1Slum regs = "^define[ ]+[A-Za-z][.0-9_A-Z+a-z-]*[ ]*$"; 35017c1e8f1Slum if (doregex(regs, begp)) { 35117c1e8f1Slum if (par1 == &lp && par2 == &lp && expctr == 1) 3525e37f6aeSlum return(founddef(begp, blkid, expctr, 0, elen)); 35381b9a829Slum return(dobeep_num("Invalid use of 'define' line:", lnm)); 35417c1e8f1Slum } 35517c1e8f1Slum /* Does the line have '(define (' */ 35617c1e8f1Slum regs = "^define$"; 35717c1e8f1Slum if (doregex(regs, begp)) { 35817c1e8f1Slum if (par1 == &lp && par2 == &lp && expctr == 1) 35917c1e8f1Slum return(foundfun(begp, expctr)); 36081b9a829Slum return(dobeep_num("Invalid use of 'define' line:", lnm)); 361bbed9a29Slum } 362bbed9a29Slum 36317c1e8f1Slum return (ABORT); 36417c1e8f1Slum } 36517c1e8f1Slum 3663c2c8acbSlum static int 3675e37f6aeSlum parsval(char *begp, const char *par1, const char *par2, int blkid, int expctr, 3685e37f6aeSlum int elen) 3693c2c8acbSlum { 370036c776aSlum char *regs; 3713c2c8acbSlum 37217c1e8f1Slum /* Does the line have 'list' */ 37317c1e8f1Slum regs = "^list$"; 37417c1e8f1Slum if (doregex(regs, begp)) 37581b9a829Slum return(dobeep_num("Invalid use of list line:", lnm)); 376228b1246Slum 37717c1e8f1Slum /* Does the line have a 'list' like: */ 37817c1e8f1Slum /* (list "a" "b") */ 37917c1e8f1Slum regs = "^list[ ]+.*$"; 38017c1e8f1Slum if (doregex(regs, begp)) { 38117c1e8f1Slum if (expctr == 1) 38281b9a829Slum return(dobeep_num("list with no-where to go.", lnm)); 38317c1e8f1Slum 38417c1e8f1Slum if (par1 == &lp && expctr > 1) 3855e37f6aeSlum return(foundlst(begp, blkid, expctr, elen)); 38617c1e8f1Slum 38781b9a829Slum return(dobeep_num("Invalid use of list line:", lnm)); 38817c1e8f1Slum } 38917c1e8f1Slum return (FALSE); 39017c1e8f1Slum } 39117c1e8f1Slum 39217c1e8f1Slum static int 3935e37f6aeSlum parsexp(char *begp, const char *par1, const char *par2, int blkid, int expctr, 3945e37f6aeSlum int elen) 39517c1e8f1Slum { 39617c1e8f1Slum struct expentry *e1 = NULL; 39717c1e8f1Slum PF funcp; 398*479c151dSjsg char *cmdp, *fendp, *valp, *fname, *funb = NULL; 39917c1e8f1Slum int numparams, ret; 40017c1e8f1Slum 40117c1e8f1Slum cmdp = begp; 402bbed9a29Slum fendp = strchr(cmdp, ' '); 4033c2c8acbSlum *fendp = '\0'; 40417c1e8f1Slum 4053c2c8acbSlum /* 4063c2c8acbSlum * If no extant mg command found, just return. 4073c2c8acbSlum */ 4083c2c8acbSlum if ((funcp = name_function(cmdp)) == NULL) 4093c2c8acbSlum return (dobeep_msgs("Unknown command:", cmdp)); 4103c2c8acbSlum 4113c2c8acbSlum numparams = numparams_function(funcp); 4123c2c8acbSlum if (numparams == 0) 4133c2c8acbSlum return (dobeep_msgs("Command takes no arguments:", cmdp)); 4143c2c8acbSlum 415089ec08bSlum if (numparams == -1) 416089ec08bSlum return (dobeep_msgs("Interactive command found:", cmdp)); 417089ec08bSlum 41817c1e8f1Slum if ((e1 = malloc(sizeof(struct expentry))) == NULL) { 41917c1e8f1Slum cleanup(); 42017c1e8f1Slum return (dobeep_msg("malloc Error")); 42117c1e8f1Slum } 42217c1e8f1Slum TAILQ_INSERT_HEAD(&ehead, e1, eentry); 42317c1e8f1Slum if ((e1->fun = strndup(cmdp, BUFSIZE)) == NULL) { 42417c1e8f1Slum cleanup(); 42517c1e8f1Slum return(dobeep_msg("strndup error")); 42617c1e8f1Slum } 42717c1e8f1Slum cmdp = e1->fun; 42817c1e8f1Slum fname = e1->fun; 42917c1e8f1Slum e1->funbuf[0] = '\0'; 43017c1e8f1Slum funb = e1->funbuf; 43117c1e8f1Slum e1->expctr = expctr; 43217c1e8f1Slum e1->blkid = blkid; 43317c1e8f1Slum /* need to think about these two */ 43417c1e8f1Slum e1->par1 = par1; 43517c1e8f1Slum e1->par2 = par2; 436bbed9a29Slum 43717c1e8f1Slum *fendp = ' '; 43817c1e8f1Slum valp = fendp + 1; 43917c1e8f1Slum 44017c1e8f1Slum ret = expandvals(cmdp, valp, funb); 44117c1e8f1Slum if (!ret) 44217c1e8f1Slum return (ret); 44317c1e8f1Slum 44417c1e8f1Slum return (multiarg(fname, funb, numparams)); 44517c1e8f1Slum } 44617c1e8f1Slum 44717c1e8f1Slum /* 44817c1e8f1Slum * Pass a list of arguments to a function. 44917c1e8f1Slum */ 45017c1e8f1Slum static int 45117c1e8f1Slum multiarg(char *cmdp, char *argbuf, int numparams) 45217c1e8f1Slum { 45317c1e8f1Slum char excbuf[BUFSIZE]; 45417c1e8f1Slum char *argp, *p, *s = " "; 45517c1e8f1Slum char *regs; 45617c1e8f1Slum int spc, numspc; 45717c1e8f1Slum int fin, inquote; 45817c1e8f1Slum 4593c2c8acbSlum argp = argbuf; 46017c1e8f1Slum spc = 1; /* initially fake a space so we find first argument */ 46117c1e8f1Slum numspc = fin = inquote = 0; 4623c2c8acbSlum 463bbed9a29Slum for (p = argbuf; *p != '\0'; p++) { 464bbed9a29Slum if (*(p + 1) == '\0') 465bbed9a29Slum fin = 1; 466bbed9a29Slum 467bbed9a29Slum if (*p != ' ') { 468036c776aSlum if (*p == '"') { 469036c776aSlum if (inquote == 1) 470036c776aSlum inquote = 0; 471036c776aSlum else 472036c776aSlum inquote = 1; 473036c776aSlum } 474bbed9a29Slum if (spc == 1) 47517c1e8f1Slum if ((numspc % numparams) == 0) { 476bbed9a29Slum argp = p; 47717c1e8f1Slum } 478bbed9a29Slum spc = 0; 4793c2c8acbSlum } 480036c776aSlum if ((*p == ' ' && inquote == 0) || fin) { 48117c1e8f1Slum if (spc == 1)/* || (numspc % numparams == 0))*/ 4823c2c8acbSlum continue; 48317c1e8f1Slum if ((numspc % numparams) != (numparams - 1)) { 48417c1e8f1Slum numspc++; 48517c1e8f1Slum continue; 48617c1e8f1Slum } 487bbed9a29Slum if (*p == ' ') { 4883c2c8acbSlum *p = '\0'; /* terminate arg string */ 489bbed9a29Slum } 4903c2c8acbSlum excbuf[0] = '\0'; 491036c776aSlum regs = "[\"]+.*[\"]+"; 492bbed9a29Slum 49317c1e8f1Slum if (!doregex(regs, argp)) { 4948fa75880Slum const char *errstr; 4958fa75880Slum 4965fb5aa3fSnaddy strtonum(argp, 0, INT_MAX, &errstr); 4978fa75880Slum if (errstr != NULL) 4988fa75880Slum return (dobeep_msgs("Var not found:", 4998fa75880Slum argp)); 5008fa75880Slum } 501036c776aSlum 5023c2c8acbSlum if (strlcpy(excbuf, cmdp, sizeof(excbuf)) 5033c2c8acbSlum >= sizeof(excbuf)) 5043c2c8acbSlum return (dobeep_msg("strlcpy error")); 5053c2c8acbSlum if (strlcat(excbuf, s, sizeof(excbuf)) 5063c2c8acbSlum >= sizeof(excbuf)) 5073c2c8acbSlum return (dobeep_msg("strlcat error")); 5083c2c8acbSlum if (strlcat(excbuf, argp, sizeof(excbuf)) 5093c2c8acbSlum >= sizeof(excbuf)) 5103c2c8acbSlum return (dobeep_msg("strlcat error")); 5113c2c8acbSlum 51281b9a829Slum excline(excbuf, 0, 0); 513bbed9a29Slum 514bbed9a29Slum if (fin) 515bbed9a29Slum break; 516bbed9a29Slum 517bbed9a29Slum *p = ' '; /* unterminate arg string */ 51817c1e8f1Slum numspc++; 5193c2c8acbSlum spc = 1; 5203c2c8acbSlum } 5213c2c8acbSlum } 5223c2c8acbSlum return (TRUE); 5233c2c8acbSlum } 5243c2c8acbSlum 5253c2c8acbSlum /* 5263c2c8acbSlum * Is an item a value or a variable? 5273c2c8acbSlum */ 5283c2c8acbSlum static int 529bbed9a29Slum isvar(char **argp, char **varbuf, int sizof) 5303c2c8acbSlum { 5313c2c8acbSlum struct varentry *v1 = NULL; 5323c2c8acbSlum 5333c2c8acbSlum if (SLIST_EMPTY(&varhead)) 5343c2c8acbSlum return (FALSE); 5357bf68b76Slum #ifdef MGLOG 536bbed9a29Slum mglog_isvar(*varbuf, *argp, sizof); 5377bf68b76Slum #endif 5383c2c8acbSlum SLIST_FOREACH(v1, &varhead, entry) { 5397ec1563aSlum if (strcmp(*argp, v1->v_name) == 0) { 5407ec1563aSlum (void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof); 5413c2c8acbSlum return (TRUE); 5423c2c8acbSlum } 5433c2c8acbSlum } 5443c2c8acbSlum return (FALSE); 5453c2c8acbSlum } 5463c2c8acbSlum 54717c1e8f1Slum 54817c1e8f1Slum static int 54917c1e8f1Slum foundfun(char *defstr, int expctr) 55017c1e8f1Slum { 55117c1e8f1Slum return (TRUE); 55217c1e8f1Slum } 55317c1e8f1Slum 55417c1e8f1Slum static int 5555e37f6aeSlum foundlst(char *defstr, int blkid, int expctr, int elen) 55617c1e8f1Slum { 55717c1e8f1Slum char *p; 55817c1e8f1Slum 55917c1e8f1Slum p = strstr(defstr, " "); 56017c1e8f1Slum p = skipwhite(p); 56117c1e8f1Slum expandvals(NULL, p, defnam); 56217c1e8f1Slum 56317c1e8f1Slum return (TRUE); 56417c1e8f1Slum } 56517c1e8f1Slum 5663c2c8acbSlum /* 56717c1e8f1Slum * 'define' strings follow the regex in parsdef(). 5683c2c8acbSlum */ 5693c2c8acbSlum static int 5705e37f6aeSlum founddef(char *defstr, int blkid, int expctr, int hasval, int elen) 5713c2c8acbSlum { 5723c2c8acbSlum struct varentry *vt, *v1 = NULL; 573bbed9a29Slum char *p, *vnamep, *vendp = NULL, *valp; 574036c776aSlum 575bbed9a29Slum p = strstr(defstr, " "); /* move to first ' ' char. */ 5763c2c8acbSlum vnamep = skipwhite(p); /* find first char of var name. */ 5773c2c8acbSlum vendp = vnamep; 5783c2c8acbSlum 57917c1e8f1Slum /* now find the end of the define/list name */ 5803c2c8acbSlum while (1) { 5813c2c8acbSlum ++vendp; 582bbed9a29Slum if (*vendp == ' ') 5833c2c8acbSlum break; 5843c2c8acbSlum } 5853c2c8acbSlum *vendp = '\0'; 586bbed9a29Slum 5873c2c8acbSlum /* 58817c1e8f1Slum * Check list name is not an existing mg function. 5893c2c8acbSlum */ 5903c2c8acbSlum if (name_function(vnamep) != NULL) 5913c2c8acbSlum return(dobeep_msgs("Variable/function name clash:", vnamep)); 5923c2c8acbSlum 5933c2c8acbSlum if (!SLIST_EMPTY(&varhead)) { 5943c2c8acbSlum SLIST_FOREACH_SAFE(v1, &varhead, entry, vt) { 5957ec1563aSlum if (strcmp(vnamep, v1->v_name) == 0) 5963c2c8acbSlum SLIST_REMOVE(&varhead, v1, varentry, entry); 5973c2c8acbSlum } 5983c2c8acbSlum } 5993c2c8acbSlum if ((v1 = malloc(sizeof(struct varentry))) == NULL) 6003c2c8acbSlum return (ABORT); 6013c2c8acbSlum SLIST_INSERT_HEAD(&varhead, v1, entry); 6027ec1563aSlum if ((v1->v_name = strndup(vnamep, BUFSIZE)) == NULL) 6033c2c8acbSlum return(dobeep_msg("strndup error")); 6047ec1563aSlum vnamep = v1->v_name; 6057ec1563aSlum v1->v_count = 0; 6067ec1563aSlum v1->v_vals = NULL; 6077ec1563aSlum v1->v_buf[0] = '\0'; 6083c2c8acbSlum 6097ec1563aSlum defnam = v1->v_buf; 61017c1e8f1Slum 61117c1e8f1Slum if (hasval) { 61217c1e8f1Slum valp = skipwhite(vendp + 1); 61317c1e8f1Slum 61417c1e8f1Slum expandvals(NULL, valp, defnam); 61517c1e8f1Slum defnam = NULL; 61617c1e8f1Slum } 61717c1e8f1Slum *vendp = ' '; 61817c1e8f1Slum return (TRUE); 61917c1e8f1Slum } 62017c1e8f1Slum 62117c1e8f1Slum 62217c1e8f1Slum static int 62317c1e8f1Slum expandvals(char *cmdp, char *valp, char *bp) 62417c1e8f1Slum { 62517c1e8f1Slum char excbuf[BUFSIZE], argbuf[BUFSIZE]; 62617c1e8f1Slum char contbuf[BUFSIZE], varbuf[BUFSIZE]; 62717c1e8f1Slum char *argp, *endp, *p, *v, *s = " "; 62817c1e8f1Slum char *regs; 62917c1e8f1Slum int spc, cnt; 6305fb5aa3fSnaddy int sizof, fin, inquote; 63117c1e8f1Slum 63217c1e8f1Slum /* now find the first argument */ 63317c1e8f1Slum p = skipwhite(valp); 63417c1e8f1Slum 63517c1e8f1Slum if (strlcpy(argbuf, p, sizeof(argbuf)) >= sizeof(argbuf)) 63617c1e8f1Slum return (dobeep_msg("strlcpy error")); 63717c1e8f1Slum argp = argbuf; 63817c1e8f1Slum spc = 1; /* initially fake a space so we find first argument */ 6395fb5aa3fSnaddy fin = inquote = cnt = spc = 0; 64017c1e8f1Slum 64117c1e8f1Slum for (p = argbuf; *p != '\0'; p++) { 64217c1e8f1Slum if (*(p + 1) == '\0') 64317c1e8f1Slum fin = 1; 64417c1e8f1Slum 64517c1e8f1Slum if (*p != ' ') { 64617c1e8f1Slum if (*p == '"') { 64717c1e8f1Slum if (inquote == 1) 64817c1e8f1Slum inquote = 0; 64917c1e8f1Slum else 65017c1e8f1Slum inquote = 1; 65117c1e8f1Slum } 6523c2c8acbSlum if (spc == 1) 65317c1e8f1Slum argp = p; 6543c2c8acbSlum spc = 0; 6553c2c8acbSlum } 65617c1e8f1Slum if ((*p == ' ' && inquote == 0) || fin) { 65717c1e8f1Slum if (spc == 1) 65817c1e8f1Slum continue; 65917c1e8f1Slum /* terminate arg string */ 66017c1e8f1Slum if (*p == ' ') { 66117c1e8f1Slum *p = '\0'; 6623c2c8acbSlum } 66317c1e8f1Slum endp = p + 1; 66417c1e8f1Slum excbuf[0] = '\0'; 66517c1e8f1Slum varbuf[0] = '\0'; 66617c1e8f1Slum contbuf[0] = '\0'; 66717c1e8f1Slum sizof = sizeof(varbuf); 66817c1e8f1Slum v = varbuf; 66917c1e8f1Slum regs = "[\"]+.*[\"]+"; 67017c1e8f1Slum if (doregex(regs, argp)) 67117c1e8f1Slum ; /* found quotes */ 67217c1e8f1Slum else if (isvar(&argp, &v, sizof)) { 6733c2c8acbSlum 67417c1e8f1Slum (void)(strlcat(varbuf, " ", 67517c1e8f1Slum sizof) >= sizof); 67617c1e8f1Slum 67717c1e8f1Slum *p = ' '; 67817c1e8f1Slum (void)(strlcpy(contbuf, endp, 67917c1e8f1Slum sizeof(contbuf)) >= sizeof(contbuf)); 68017c1e8f1Slum 68117c1e8f1Slum (void)(strlcat(varbuf, contbuf, 68217c1e8f1Slum sizof) >= sizof); 68317c1e8f1Slum 68417c1e8f1Slum argbuf[0] = ' '; 68517c1e8f1Slum argbuf[1] = '\0'; 68617c1e8f1Slum (void)(strlcat(argbuf, varbuf, 68717c1e8f1Slum sizof) >= sizof); 68817c1e8f1Slum 68917c1e8f1Slum p = argp = argbuf; 69017c1e8f1Slum spc = 1; 69117c1e8f1Slum fin = 0; 69217c1e8f1Slum continue; 69317c1e8f1Slum } else { 69417c1e8f1Slum const char *errstr; 69517c1e8f1Slum 6965fb5aa3fSnaddy strtonum(argp, 0, INT_MAX, &errstr); 69717c1e8f1Slum if (errstr != NULL) 69817c1e8f1Slum return (dobeep_msgs("Var not found:", 69917c1e8f1Slum argp)); 70017c1e8f1Slum } 701fefadbf1Slum #ifdef MGLOG 70217c1e8f1Slum mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE); 703fefadbf1Slum #endif 70417c1e8f1Slum if (*bp != '\0') { 70517c1e8f1Slum if (strlcat(bp, s, BUFSIZE) >= BUFSIZE) 70617c1e8f1Slum return (dobeep_msg("strlcat error")); 70717c1e8f1Slum } 70817c1e8f1Slum if (strlcat(bp, argp, BUFSIZE) >= BUFSIZE) { 70917c1e8f1Slum return (dobeep_msg("strlcat error")); 71017c1e8f1Slum } 7117ec1563aSlum /* v1->v_count++;*/ 7123c2c8acbSlum 71317c1e8f1Slum if (fin) 71417c1e8f1Slum break; 71517c1e8f1Slum 71617c1e8f1Slum *p = ' '; /* unterminate arg string */ 71717c1e8f1Slum spc = 1; 71817c1e8f1Slum } 71917c1e8f1Slum } 7203c2c8acbSlum return (TRUE); 7213c2c8acbSlum } 7223c2c8acbSlum 7233c2c8acbSlum /* 724bbed9a29Slum * Finished with buffer evaluation, so clean up any vars. 725bbed9a29Slum * Perhaps keeps them in mg even after use,... 7263c2c8acbSlum */ 7277ec1563aSlum /*static int 7283c2c8acbSlum clearvars(void) 7293c2c8acbSlum { 7303c2c8acbSlum struct varentry *v1 = NULL; 7313c2c8acbSlum 7323c2c8acbSlum while (!SLIST_EMPTY(&varhead)) { 7333c2c8acbSlum v1 = SLIST_FIRST(&varhead); 7343c2c8acbSlum SLIST_REMOVE_HEAD(&varhead, entry); 7357ec1563aSlum free(v1->v_name); 7363c2c8acbSlum free(v1); 7373c2c8acbSlum } 7383c2c8acbSlum return (FALSE); 7393c2c8acbSlum } 7407ec1563aSlum */ 7413c2c8acbSlum /* 742bbed9a29Slum * Finished with block evaluation, so clean up any expressions. 7433c2c8acbSlum */ 744bbed9a29Slum static void 745bbed9a29Slum clearexp(void) 7463c2c8acbSlum { 747bbed9a29Slum struct expentry *e1 = NULL; 748e5364bf1Slum 7494937e3a7Slum while (!TAILQ_EMPTY(&ehead)) { 7504937e3a7Slum e1 = TAILQ_FIRST(&ehead); 7514937e3a7Slum TAILQ_REMOVE(&ehead, e1, eentry); 75217c1e8f1Slum free(e1->fun); 753bbed9a29Slum free(e1); 754e5364bf1Slum } 755bbed9a29Slum return; 7563c2c8acbSlum } 757228b1246Slum 758228b1246Slum /* 759036c776aSlum * Cleanup before leaving. 760036c776aSlum */ 761036c776aSlum void 762036c776aSlum cleanup(void) 763036c776aSlum { 76417c1e8f1Slum defnam = NULL; 76517c1e8f1Slum 766036c776aSlum clearexp(); 7677ec1563aSlum /* clearvars();*/ 768036c776aSlum } 769036c776aSlum 770036c776aSlum /* 771228b1246Slum * Test a string against a regular expression. 772228b1246Slum */ 773bbed9a29Slum static int 774228b1246Slum doregex(char *r, char *e) 775228b1246Slum { 776228b1246Slum regex_t regex_buff; 777228b1246Slum 778228b1246Slum if (regcomp(®ex_buff, r, REG_EXTENDED)) { 779228b1246Slum regfree(®ex_buff); 78081b9a829Slum return(dobeep_num("Regex compilation error line:", lnm)); 781228b1246Slum } 782228b1246Slum if (!regexec(®ex_buff, e, 0, NULL, 0)) { 783228b1246Slum regfree(®ex_buff); 784228b1246Slum return(TRUE); 785228b1246Slum } 786e5364bf1Slum regfree(®ex_buff); 787e5364bf1Slum return(FALSE); 788228b1246Slum } 789ef122b21Slum 790ef122b21Slum /* 791ef122b21Slum * Display a message so it is apparent that this is the method which stopped 792ef122b21Slum * execution. 793ef122b21Slum */ 794ef122b21Slum static int 79517c1e8f1Slum exitinterpreter(char *ptr, char *dobuf, int dosiz) 796ef122b21Slum { 797ef122b21Slum cleanup(); 798ef122b21Slum if (batch == 0) 799ef122b21Slum return(dobeep_msg("Interpreter exited via exit command.")); 800ef122b21Slum return(FALSE); 801ef122b21Slum } 80217c1e8f1Slum 80317c1e8f1Slum /* 80417c1e8f1Slum * All code below commented out (until end of file). 80517c1e8f1Slum * 80617c1e8f1Slum * Need to think about how interpreter functions are done. 80717c1e8f1Slum * Probably don't have a choice with string-append(). 80817c1e8f1Slum 80917c1e8f1Slum static int getenvironmentvariable(char *, char *, int); 81017c1e8f1Slum static int stringappend(char *, char *, int); 81117c1e8f1Slum 81217c1e8f1Slum typedef int (*PFI)(char *, char *, int); 81317c1e8f1Slum 81417c1e8f1Slum 81517c1e8f1Slum struct ifunmap { 81617c1e8f1Slum PFI fn_funct; 81717c1e8f1Slum const char *fn_name; 81817c1e8f1Slum struct ifunmap *fn_next; 81917c1e8f1Slum }; 82017c1e8f1Slum static struct ifunmap *ifuns; 82117c1e8f1Slum 82217c1e8f1Slum static struct ifunmap ifunctnames[] = { 82317c1e8f1Slum {exitinterpreter, "exit"}, 82417c1e8f1Slum {getenvironmentvariable, "get-environment-variable"}, 82517c1e8f1Slum {stringappend, "string-append"}, 82617c1e8f1Slum {NULL, NULL} 82717c1e8f1Slum }; 82817c1e8f1Slum 82917c1e8f1Slum void 83017c1e8f1Slum ifunmap_init(void) 83117c1e8f1Slum { 83217c1e8f1Slum struct ifunmap *fn; 83317c1e8f1Slum 83417c1e8f1Slum for (fn = ifunctnames; fn->fn_name != NULL; fn++) { 83517c1e8f1Slum fn->fn_next = ifuns; 83617c1e8f1Slum ifuns = fn; 83717c1e8f1Slum } 83817c1e8f1Slum } 83917c1e8f1Slum 84017c1e8f1Slum PFI 84117c1e8f1Slum name_ifun(const char *ifname) 84217c1e8f1Slum { 84317c1e8f1Slum struct ifunmap *fn; 84417c1e8f1Slum 84517c1e8f1Slum for (fn = ifuns; fn != NULL; fn = fn->fn_next) { 84617c1e8f1Slum if (strcmp(fn->fn_name, ifname) == 0) 84717c1e8f1Slum return (fn->fn_funct); 84817c1e8f1Slum } 84917c1e8f1Slum 85017c1e8f1Slum return (NULL); 85117c1e8f1Slum } 85217c1e8f1Slum 85317c1e8f1Slum 85417c1e8f1Slum int 85517c1e8f1Slum dofunc(char **ifname, char **tmpbuf, int sizof) 85617c1e8f1Slum { 85717c1e8f1Slum PFI fnc; 85817c1e8f1Slum char *p, *tmp; 85917c1e8f1Slum 86017c1e8f1Slum p = strstr(*ifname, " "); 86117c1e8f1Slum *p = '\0'; 86217c1e8f1Slum 86317c1e8f1Slum fnc = name_ifun(*ifname); 86417c1e8f1Slum if (fnc == NULL) 86517c1e8f1Slum return (FALSE); 86617c1e8f1Slum 86717c1e8f1Slum *p = ' '; 86817c1e8f1Slum 86917c1e8f1Slum tmp = *tmpbuf; 87017c1e8f1Slum 87117c1e8f1Slum fnc(p, tmp, sizof); 87217c1e8f1Slum 87317c1e8f1Slum return (TRUE); 87417c1e8f1Slum } 87517c1e8f1Slum 87617c1e8f1Slum static int 87717c1e8f1Slum getenvironmentvariable(char *ptr, char *dobuf, int dosiz) 87817c1e8f1Slum { 87917c1e8f1Slum char *t; 88017c1e8f1Slum char *tmp; 88117c1e8f1Slum const char *q = "\""; 88217c1e8f1Slum 88317c1e8f1Slum t = skipwhite(ptr); 88417c1e8f1Slum 88517c1e8f1Slum if (t[0] == *q || t[strlen(t) - 1] == *q) 88617c1e8f1Slum return (dobeep_msgs("Please remove '\"' around:", t)); 88717c1e8f1Slum if ((tmp = getenv(t)) == NULL || *tmp == '\0') 88817c1e8f1Slum return(dobeep_msgs("Envar not found:", t)); 88917c1e8f1Slum 89017c1e8f1Slum dobuf[0] = '\0'; 89117c1e8f1Slum if (strlcat(dobuf, q, dosiz) >= dosiz) 89217c1e8f1Slum return (dobeep_msg("strlcat error")); 89317c1e8f1Slum if (strlcat(dobuf, tmp, dosiz) >= dosiz) 89417c1e8f1Slum return (dobeep_msg("strlcat error")); 89517c1e8f1Slum if (strlcat(dobuf, q, dosiz) >= dosiz) 89617c1e8f1Slum return (dobeep_msg("strlcat error")); 89717c1e8f1Slum 89817c1e8f1Slum return (TRUE); 89917c1e8f1Slum } 90017c1e8f1Slum 90117c1e8f1Slum static int 90217c1e8f1Slum stringappend(char *ptr, char *dobuf, int dosiz) 90317c1e8f1Slum { 90417c1e8f1Slum char varbuf[BUFSIZE], funbuf[BUFSIZE]; 90517c1e8f1Slum char *p, *f, *v, *vendp; 90617c1e8f1Slum int sizof, fin = 0; 90717c1e8f1Slum 90817c1e8f1Slum varbuf[0] = funbuf[0] = '\0'; 90917c1e8f1Slum f = funbuf; 91017c1e8f1Slum v = varbuf; 91117c1e8f1Slum sizof = sizeof(varbuf); 91217c1e8f1Slum *dobuf = '\0'; 91317c1e8f1Slum 91417c1e8f1Slum p = skipwhite(ptr); 91517c1e8f1Slum 91617c1e8f1Slum while (*p != '\0') { 91717c1e8f1Slum vendp = p; 91817c1e8f1Slum while (1) { 91917c1e8f1Slum if (*vendp == ' ') { 92017c1e8f1Slum break; 92117c1e8f1Slum } else if (*vendp == '\0') { 92217c1e8f1Slum fin = 1; 92317c1e8f1Slum break; 92417c1e8f1Slum } 92517c1e8f1Slum ++vendp; 92617c1e8f1Slum } 92717c1e8f1Slum *vendp = '\0'; 92817c1e8f1Slum 92917c1e8f1Slum if (isvar(&p, &v, sizof)) { 93017c1e8f1Slum if (v[0] == '"' && v[strlen(v) - 1] == '"' ) { 93117c1e8f1Slum v[strlen(v) - 1] = '\0'; 93217c1e8f1Slum v = v + 1; 93317c1e8f1Slum } 93417c1e8f1Slum if (strlcat(f, v, sizof) >= sizof) 93517c1e8f1Slum return (dobeep_msg("strlcat error")); 93617c1e8f1Slum } else { 93717c1e8f1Slum if (p[0] == '"' && p[strlen(p) - 1] == '"' ) { 93817c1e8f1Slum p[strlen(p) - 1] = '\0'; 93917c1e8f1Slum p = p + 1; 94017c1e8f1Slum } 94117c1e8f1Slum if (strlcat(f, p, sizof) >= sizof) 94217c1e8f1Slum return (dobeep_msg("strlcat error")); 94317c1e8f1Slum } 94417c1e8f1Slum if (fin) 94517c1e8f1Slum break; 94617c1e8f1Slum vendp++; 94717c1e8f1Slum if (*vendp == '\0') 94817c1e8f1Slum break; 94917c1e8f1Slum p = skipwhite(vendp); 95017c1e8f1Slum } 95117c1e8f1Slum 95217c1e8f1Slum (void)snprintf(dobuf, dosiz, "\"%s\"", f); 95317c1e8f1Slum 95417c1e8f1Slum return (TRUE); 95517c1e8f1Slum } 95617c1e8f1Slum 95717c1e8f1Slum Index: main.c 95817c1e8f1Slum =================================================================== 95917c1e8f1Slum RCS file: /cvs/src/usr.bin/mg/main.c,v 96017c1e8f1Slum retrieving revision 1.89 96117c1e8f1Slum diff -u -p -u -p -r1.89 main.c 96217c1e8f1Slum --- main.c 20 Mar 2021 09:00:49 -0000 1.89 96317c1e8f1Slum +++ main.c 12 Apr 2021 17:58:52 -0000 96417c1e8f1Slum @@ -133,10 +133,12 @@ main(int argc, char **argv) 96517c1e8f1Slum extern void grep_init(void); 96617c1e8f1Slum extern void cmode_init(void); 96717c1e8f1Slum extern void dired_init(void); 96817c1e8f1Slum + extern void ifunmap_init(void); 96917c1e8f1Slum 97017c1e8f1Slum dired_init(); 97117c1e8f1Slum grep_init(); 97217c1e8f1Slum cmode_init(); 97317c1e8f1Slum + ifunmap_init(); 97417c1e8f1Slum } 97517c1e8f1Slum 97617c1e8f1Slum 97717c1e8f1Slum */ 978