File: | src/usr.bin/mg/interpreter.c |
Warning: | line 638, column 2 Value stored to 'spc' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
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)struct exphead { struct expentry *tqh_first; struct expentry * *tqh_last; } ehead; |
90 | struct expentry { |
91 | TAILQ_ENTRY(expentry)struct { struct expentry *tqe_next; struct expentry **tqe_prev ; } eentry; |
92 | char *fun; /* The 1st string found between parens. */ |
93 | char funbuf[BUFSIZE128]; |
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 NUMSCHKEYS4 4 |
104 | #define MAXLENSCHKEYS17 17 /* 17 = longest keyword (16) + 1 */ |
105 | |
106 | char scharkey[NUMSCHKEYS4][MAXLENSCHKEYS17] = |
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((void *)0); |
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((void *)0); |
128 | char *p, *begp = NULL((void *)0), *endp = NULL((void *)0), *prechr; |
129 | char *lastchr = NULL((void *)0); |
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)do { (&ehead)->tqh_first = ((void *)0); (&ehead)-> tqh_last = &(&ehead)->tqh_first; } while (0); |
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((void *)0)) |
162 | elen++; |
163 | |
164 | if (*p == '\\') { |
165 | esc = 1; |
166 | } else if (*p == '(') { |
167 | if (lastchr != NULL((void *)0) && *lastchr == '(') |
168 | return(dobeep_num("Multiple consecutive "\ |
169 | "left parantheses line", lnm)); |
170 | if (inquote == 0) { |
171 | if (begp != NULL((void *)0)) { |
172 | if (*prechr == ' ') |
173 | ns--; |
174 | if (endp == NULL((void *)0)) |
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((void *)0); |
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((void *)0) && *lastchr == '(') |
198 | return(dobeep_num("Empty parenthesis "\ |
199 | "not supported line", lnm)); |
200 | if (inquote == 0) { |
201 | if (begp != NULL((void *)0)) { |
202 | if (*prechr == ' ') |
203 | ns--; |
204 | if (endp == NULL((void *)0)) |
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((void *)0); |
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((void *)0)) { |
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((void *)0); |
247 | spc = 0; |
248 | } else if (endp == NULL((void *)0) && (*p == ' ' || *p == '\t')) { |
249 | if (inquote == 0) { |
250 | *p = ' '; |
251 | endp = p; |
252 | spc++; |
253 | if (begp != NULL((void *)0)) |
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((void *)0); |
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 == FALSE0) |
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 = FALSE0; |
295 | |
296 | if (strncmp(begp, "define", 6) == 0) { |
297 | ret = parsdef(begp, par1, par2, blkid, expctr, elen); |
298 | if (ret == TRUE1 || ret == FALSE0) |
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((void *)0), NULL((void *)0), FALSE0)); |
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((void *)0)) && (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 (ABORT2); |
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 (FALSE0); |
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((void *)0); |
397 | PF funcp; |
398 | char *cmdp, *fendp, *valp, *fname, *funb = NULL((void *)0);; |
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((void *)0)) |
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((void *)0)) { |
419 | cleanup(); |
420 | return (dobeep_msg("malloc Error")); |
421 | } |
422 | TAILQ_INSERT_HEAD(&ehead, e1, eentry)do { if (((e1)->eentry.tqe_next = (&ehead)->tqh_first ) != ((void *)0)) (&ehead)->tqh_first->eentry.tqe_prev = &(e1)->eentry.tqe_next; else (&ehead)->tqh_last = &(e1)->eentry.tqe_next; (&ehead)->tqh_first = (e1); (e1)->eentry.tqe_prev = &(&ehead)->tqh_first ; } while (0); |
423 | if ((e1->fun = strndup(cmdp, BUFSIZE128)) == NULL((void *)0)) { |
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[BUFSIZE128]; |
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_MAX0x7fffffff, &errstr); |
497 | if (errstr != NULL((void *)0)) |
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 (TRUE1); |
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((void *)0); |
532 | |
533 | if (SLIST_EMPTY(&varhead)(((&varhead)->slh_first) == ((void *)0))) |
534 | return (FALSE0); |
535 | #ifdef MGLOG |
536 | mglog_isvar(*varbuf, *argp, sizof); |
537 | #endif |
538 | SLIST_FOREACH(v1, &varhead, entry)for((v1) = ((&varhead)->slh_first); (v1) != ((void *)0 ); (v1) = ((v1)->entry.sle_next)) { |
539 | if (strcmp(*argp, v1->v_name) == 0) { |
540 | (void)(strlcpy(*varbuf, v1->v_buf, sizof) >= sizof); |
541 | return (TRUE1); |
542 | } |
543 | } |
544 | return (FALSE0); |
545 | } |
546 | |
547 | |
548 | static int |
549 | foundfun(char *defstr, int expctr) |
550 | { |
551 | return (TRUE1); |
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((void *)0), p, defnam); |
562 | |
563 | return (TRUE1); |
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((void *)0); |
573 | char *p, *vnamep, *vendp = NULL((void *)0), *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((void *)0)) |
591 | return(dobeep_msgs("Variable/function name clash:", vnamep)); |
592 | |
593 | if (!SLIST_EMPTY(&varhead)(((&varhead)->slh_first) == ((void *)0))) { |
594 | SLIST_FOREACH_SAFE(v1, &varhead, entry, vt)for ((v1) = ((&varhead)->slh_first); (v1) && ( (vt) = ((v1)->entry.sle_next), 1); (v1) = (vt)) { |
595 | if (strcmp(vnamep, v1->v_name) == 0) |
596 | SLIST_REMOVE(&varhead, v1, varentry, entry)do { if ((&varhead)->slh_first == (v1)) { do { ((& varhead))->slh_first = ((&varhead))->slh_first-> entry.sle_next; } while (0); } else { struct varentry *curelm = (&varhead)->slh_first; while (curelm->entry.sle_next != (v1)) curelm = curelm->entry.sle_next; curelm->entry .sle_next = curelm->entry.sle_next->entry.sle_next; } ; } while (0); |
597 | } |
598 | } |
599 | if ((v1 = malloc(sizeof(struct varentry))) == NULL((void *)0)) |
600 | return (ABORT2); |
601 | SLIST_INSERT_HEAD(&varhead, v1, entry)do { (v1)->entry.sle_next = (&varhead)->slh_first; ( &varhead)->slh_first = (v1); } while (0); |
602 | if ((v1->v_name = strndup(vnamep, BUFSIZE128)) == NULL((void *)0)) |
603 | return(dobeep_msg("strndup error")); |
604 | vnamep = v1->v_name; |
605 | v1->v_count = 0; |
606 | v1->v_vals = NULL((void *)0); |
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((void *)0), valp, defnam); |
615 | defnam = NULL((void *)0); |
616 | } |
617 | *vendp = ' '; |
618 | return (TRUE1); |
619 | } |
620 | |
621 | |
622 | static int |
623 | expandvals(char *cmdp, char *valp, char *bp) |
624 | { |
625 | char excbuf[BUFSIZE128], argbuf[BUFSIZE128]; |
626 | char contbuf[BUFSIZE128], varbuf[BUFSIZE128]; |
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 */ |
Value stored to 'spc' is never read | |
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_MAX0x7fffffff, &errstr); |
697 | if (errstr != NULL((void *)0)) |
698 | return (dobeep_msgs("Var not found:", |
699 | argp)); |
700 | } |
701 | #ifdef MGLOG |
702 | mglog_misc("x|%s|%p|%d|\n", bp, defnam, BUFSIZE128); |
703 | #endif |
704 | if (*bp != '\0') { |
705 | if (strlcat(bp, s, BUFSIZE128) >= BUFSIZE128) |
706 | return (dobeep_msg("strlcat error")); |
707 | } |
708 | if (strlcat(bp, argp, BUFSIZE128) >= BUFSIZE128) { |
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 (TRUE1); |
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((void *)0); |
748 | |
749 | while (!TAILQ_EMPTY(&ehead)(((&ehead)->tqh_first) == ((void *)0))) { |
750 | e1 = TAILQ_FIRST(&ehead)((&ehead)->tqh_first); |
751 | TAILQ_REMOVE(&ehead, e1, eentry)do { if (((e1)->eentry.tqe_next) != ((void *)0)) (e1)-> eentry.tqe_next->eentry.tqe_prev = (e1)->eentry.tqe_prev ; else (&ehead)->tqh_last = (e1)->eentry.tqe_prev; * (e1)->eentry.tqe_prev = (e1)->eentry.tqe_next; ; ; } while (0); |
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((void *)0); |
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_EXTENDED0001)) { |
779 | regfree(®ex_buff); |
780 | return(dobeep_num("Regex compilation error line:", lnm)); |
781 | } |
782 | if (!regexec(®ex_buff, e, 0, NULL((void *)0), 0)) { |
783 | regfree(®ex_buff); |
784 | return(TRUE1); |
785 | } |
786 | regfree(®ex_buff); |
787 | return(FALSE0); |
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(FALSE0); |
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 | */ |