1 /* This file is part of Mailfromd.
2 Copyright (C) 2006-2021 Sergey Poznyakoff
3
4 This program is free software; you can redistribute it and/or modify
5 it under the terms of the GNU General Public License as published by
6 the Free Software Foundation; either version 3, or (at your option)
7 any later version.
8
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 GNU General Public License for more details.
13
14 You should have received a copy of the GNU General Public License
15 along with this program. If not, see <http://www.gnu.org/licenses/>. */
16
17 #ifdef HAVE_CONFIG_H
18 # include <config.h>
19 #endif
20 #include <setjmp.h>
21 #include <stdarg.h>
22 #include <sys/types.h>
23 #include <sys/socket.h>
24 #include <netinet/in.h>
25 #include <netdb.h>
26 #include <arpa/inet.h>
27 #include <assert.h>
28 #include <fnmatch.h>
29 #include <syslog.h>
30 #include "mailfromd.h"
31 #include "prog.h"
32 #include "msg.h"
33 #include "optab.h"
34 #include "builtin.h"
35 #include "mflib/_register.h"
36 #include "global.h"
37
38
39 /* Code generation support */
40 #define CODE_INITIAL 128
41 #define CODE_INCREMENT 128
42
43 static prog_counter_t pc, pmax;
44 mf_code_cell_t *prog;
45 size_t stack_size = STACK_SIZE;
46 size_t stack_max_size = 0;
47 size_t stack_expand_incr = STACK_INCR;
48 enum stack_expand_policy stack_expand_policy = stack_expand_add;
49
50 /* Data segment */
51 STKVAL *dataseg;
52 size_t datasize;
53 size_t dvarsize;
54
55 /* Table of relocatable entries in the data segment */
56 size_t *dataseg_reloc;
57 size_t dataseg_reloc_count;
58
59 /* Regular expressions */
60 struct rt_regex *regtab;
61 size_t regcount;
62 mu_opool_t regpool;
63
64 void
code_init()65 code_init()
66 {
67 pmax = CODE_INITIAL;
68 prog = calloc(pmax, sizeof prog[0]);
69 if (!prog) {
70 mu_error(_("not enough memory"));
71 exit(1);
72 }
73 }
74
75 prog_counter_t
code_get_counter()76 code_get_counter()
77 {
78 return pc;
79 }
80
81 prog_counter_t
code_reserve(size_t count)82 code_reserve(size_t count)
83 {
84 prog_counter_t ret = pc;
85 if (pc + count > pmax) {
86 size_t incr = (pc + count - pmax + CODE_INCREMENT - 1)
87 / CODE_INCREMENT;
88 pmax += incr * CODE_INCREMENT;
89 prog = realloc(prog, pmax*sizeof prog[0]);
90 if (!prog) {
91 mu_error(_("not enough memory"));
92 exit(1);
93 }
94 }
95 pc += count;
96 return ret;
97 }
98
99 prog_counter_t
code_cell(mf_code_cell_t cell)100 code_cell(mf_code_cell_t cell)
101 {
102 if (pc >= pmax) {
103 pmax += CODE_INCREMENT;
104 prog = realloc(prog, pmax*sizeof prog[0]);
105 if (!prog) {
106 mu_error(_("not enough memory"));
107 exit(1);
108 }
109 }
110 prog[pc] = cell;
111 return pc++;
112 }
113
114 prog_counter_t
code_instr(const instr_t ptr)115 code_instr(const instr_t ptr)
116 {
117 return code_cell((mf_code_cell_t)ptr);
118 }
119
120 prog_counter_t
code_op(unsigned code)121 code_op(unsigned code)
122 {
123 return code_cell((mf_code_cell_t)(STKVAL)code);
124 }
125
126 prog_counter_t
code_immediate_stkval(STKVAL val)127 code_immediate_stkval(STKVAL val)
128 {
129 return code_cell((mf_code_cell_t)val);
130 }
131
132 prog_counter_t
code_exmask(struct exmask * exmask)133 code_exmask(struct exmask *exmask)
134 {
135 return code_immediate(exmask->off, size);
136 }
137
138 void
code_put_stkval(prog_counter_t pos,STKVAL val)139 code_put_stkval(prog_counter_t pos, STKVAL val)
140 {
141 assert(pos < pmax);
142 prog[pos].c_value = val;
143 }
144
145 mf_code_cell_t
code_peek(prog_counter_t pos)146 code_peek(prog_counter_t pos)
147 {
148 assert(pos < pmax);
149 return prog[pos];
150 }
151
152 static size_t transform_count;
153 static size_t transform_max;
154 static transform_t *transform_tab;
155
156 size_t
next_transform_index(void)157 next_transform_index(void)
158 {
159 if (transform_count == transform_max) {
160 transform_tab = mu_2nrealloc(transform_tab,
161 &transform_max,
162 sizeof(transform_tab[0]));
163 }
164 transform_tab[transform_count] = NULL;
165 return transform_count++;
166 }
167
168 void
install_transform(size_t index,transform_t tp)169 install_transform(size_t index, transform_t tp)
170 {
171 transform_tab[index] = tp;
172 }
173
174 transform_t
get_transform(size_t index)175 get_transform(size_t index)
176 {
177 return transform_tab[index];
178 }
179
180 /* Regexps*/
181
182 void
register_regex(struct sym_regex * rp)183 register_regex(struct sym_regex *rp)
184 {
185 struct rt_regex r;
186 if (regcount == 0)
187 mu_opool_create(®pool, MU_OPOOL_ENOMEMABRT);
188 r.compiled = 0; /* Will be compiled later */
189 r.expr = rp->lit ? rp->lit->off : 0;
190 r.regflags = rp->regflags;
191 rp->index = regcount++;
192 mu_opool_append(regpool, &r, sizeof r);
193 }
194
195 void
finalize_regex()196 finalize_regex()
197 {
198 if (regcount)
199 regtab = mu_opool_finish(regpool, NULL);
200 }
201
202
203 #define PROG_TRACE_ENGINE (builtin_module_trace(BUILTIN_IDX_prog))
204
205 static void
set_prog_trace(const char * modlist,int val)206 set_prog_trace(const char *modlist, int val)
207 {
208 while (1) {
209 size_t len = strcspn(modlist, ",");
210 if (len == 3 && memcmp(modlist, "all", 3) == 0)
211 builtin_set_all_module_trace(val);
212 else if (len == 4 && memcmp(modlist, "none", 4) == 0)
213 builtin_set_all_module_trace(!val);
214 else {
215 if (len > 3 && memcmp (modlist, "no-", 3) == 0)
216 builtin_set_module_trace(modlist+3, len-3,
217 !val);
218 else
219 builtin_set_module_trace(modlist, len, val);
220 }
221 modlist += len;
222 if (*modlist)
223 modlist++;
224 else
225 break;
226 }
227 }
228
229 void
enable_prog_trace(const char * modlist)230 enable_prog_trace(const char *modlist)
231 {
232 set_prog_trace(modlist, 1);
233 }
234
235 void
disable_prog_trace(const char * modlist)236 disable_prog_trace(const char *modlist)
237 {
238 set_prog_trace(modlist, 0);
239 }
240
241 /* ========================================================================
242 Drzewa w górę, rzeki w dół.
243
244 Jacek Kaczmarski.
245 "Upadek Ikara"
246 ======================================================================== */
247
248 /* Run-time evaluation */
249
250 /* Max. number of C locals to save in struct eval_environ for eventual fixups.
251 See comment to env_fixup_autos, below. */
252 #define MAX_AUTO_PTR 128
253
254 struct exception_context {
255 prog_counter_t pc;
256 prog_counter_t tos;
257 prog_counter_t base;
258 };
259
260 #define TOS_INVARIANT(env,t) (datasize + (env)->stack_size - (t))
261
262 struct environ_locus {
263 unsigned long file; /* File name */
264 unsigned int line; /* Line number */
265 };
266
267 struct environ_cleanup_closure {
268 void (*cleanup)(void *);
269 void *data;
270 };
271
272 struct eval_environ {
273 prog_counter_t pc; /* Program counter */
274
275 prog_counter_t tos; /* Top of stack:
276 toh <= tos < datasize + stack_size */
277 prog_counter_t toh; /* Top of heap:
278 datasize <= toh <= tos */
279
280 prog_counter_t base; /* Base pointer */
281 STKVAL reg; /* General purpose register */
282 STKVAL *dataseg; /* Data space */
283 size_t stack_size; /* Size of allocated stack + heap */
284
285 /* Program locus corresponding to PC */
286 struct environ_locus locus;
287
288 /* Temporary heap space */
289 size_t temp_start;
290 size_t temp_size;
291
292 STKVAL *auto_ptr[MAX_AUTO_PTR]; /* Pointers to C automatic variables
293 referring to dataseg. */
294 size_t numautos; /* Number of entries in auto_ptr. */
295
296 /* Sendmail interaction data: */
297 SMFICTX *ctx; /* Milter Context */
298 void *data; /* MTA symbol table */
299 /* methods to access the latter */
300 const char *(*getsym)(void *data, const char *str);
301 int (*setreply)(void *data, char *code, char *xcode, char *message);
302 void (*msgmod)(void *data, struct msgmod_closure *c);
303
304 /* Regular expression matching */
305 regmatch_t *matches; /* Match map */
306 size_t matchcount; /* Number of used entries in matches */
307 size_t matchsize; /* Total number of entries in matches */
308 prog_counter_t matchstr; /* Pointer to the last matched string
309 in the dataseg. */
310
311 mu_stream_t stream; /* Capture stream */
312 size_t line_count; /* Number of lines in stream */
313 int reposition; /* When !0, stream must be repositioned to
314 its end before writing. */
315 mu_header_t header; /* Headers from stream, converted to a MU
316 object. */
317
318 /* Message modification queue */
319 mu_list_t mmq;
320
321 /* Non-local exits */
322 struct exception_context *defcatch_ctx;
323 struct exception_context *catch_ctx;
324
325 /* Built-in private data */
326 void **bi_priv_array;
327
328 /* Function clean-up sequence */
329 mu_list_t function_cleanup_list;
330
331 mu_list_t environ_cleanup_list;
332
333 /* Exit information */
334 sfsistat status; /* Program exit status */
335 jmp_buf x_jmp; /* Return point for runtime errors */
336 jmp_buf catch_jmp; /* Return point for throws */
337 };
338
339 #define ENV_LOC_FILE(env) \
340 ((const char*)((env)->dataseg + (size_t) (env)->locus.file))
341 #define ENV_LOC_LINE(env) ((env)->locus.line)
342
343 void
advance_pc(eval_environ_t env,long cnt)344 advance_pc(eval_environ_t env, long cnt)
345 {
346 env->pc += cnt;
347 }
348
349 void
adjust_stack(eval_environ_t env,unsigned cnt)350 adjust_stack(eval_environ_t env, unsigned cnt)
351 {
352 env->tos += cnt;
353 }
354
355 void
unroll_stack(eval_environ_t env,unsigned cnt)356 unroll_stack(eval_environ_t env, unsigned cnt)
357 {
358 env->tos -= cnt;
359 }
360
361 prog_counter_t
env_register_read(eval_environ_t env,int what)362 env_register_read(eval_environ_t env, int what)
363 {
364 switch (what) {
365 case REG_PC:
366 return env->pc;
367
368 case REG_TOS:
369 return env->tos;
370
371 case REG_TOH:
372 return env->toh;
373
374 case REG_BASE:
375 return env->base;
376
377 case REG_REG:
378 return (prog_counter_t) mf_c_val(env->reg, ulong);
379
380 case REG_MATCHSTR:
381 return env->matchstr;
382 }
383 return -1;
384 }
385
386 size_t
env_base(eval_environ_t env,size_t frame)387 env_base(eval_environ_t env, size_t frame)
388 {
389 size_t base = env->base;
390 for (; frame; frame--)
391 base = mf_c_val(env->dataseg[base + 1], size) + base + 1;
392 return base;
393 }
394
395 char *
env_vaptr(eval_environ_t env,size_t off)396 env_vaptr(eval_environ_t env, size_t off)
397 {
398 return (char*)(env->dataseg + mf_c_val(env->dataseg[off], size));
399 }
400
401 void
env_var_inc(eval_environ_t env,size_t off)402 env_var_inc(eval_environ_t env, size_t off)
403 {
404 ++mf_c_val(*env_data_ref(env, off), long);
405 }
406
407 static void env_register_auto(eval_environ_t env, void *ptr);
408
409 void
env_get_locus(eval_environ_t env,struct mu_locus_range * locus)410 env_get_locus(eval_environ_t env, struct mu_locus_range *locus)
411 {
412 mu_locus_range_init(locus);
413 locus->beg.mu_file = (char*)(env->dataseg + env->locus.file);
414 env_register_auto(env, (void*) &locus->beg.mu_file);
415 locus->beg.mu_line = env->locus.line;
416 #if 0
417 locus->beg.mu_col = env->locus.point;
418 ...
419 #endif
420 }
421
422 const char *
env_get_macro(eval_environ_t env,const char * symbol)423 env_get_macro(eval_environ_t env, const char *symbol)
424 {
425 return env->getsym(env->data, symbol);
426 }
427
428 int
env_get_stream(eval_environ_t env,mu_stream_t * pstr)429 env_get_stream(eval_environ_t env, mu_stream_t *pstr)
430 {
431 return mu_streamref_create(pstr, env->stream);
432 }
433
434 STKVAL
env_get_reg(eval_environ_t env)435 env_get_reg(eval_environ_t env)
436 {
437 return env->reg;
438 }
439
440 void
env_reposition(eval_environ_t env)441 env_reposition(eval_environ_t env)
442 {
443 env->reposition = 1;
444 }
445
446 static int
_cleanup_compare(const void * a,const void * b)447 _cleanup_compare(const void *a, const void *b)
448 {
449 struct environ_cleanup_closure const *closa = a;
450 struct environ_cleanup_closure const *closb = b;
451 return closa->data != closb->data;
452 }
453
454 static void
_cleanup_destroy(void * item)455 _cleanup_destroy(void *item)
456 {
457 struct environ_cleanup_closure *clos = item;
458 if (clos->cleanup)
459 clos->cleanup(clos->data);
460 }
461
462 static void
env_cleanup_list_create(mu_list_t * plist)463 env_cleanup_list_create(mu_list_t *plist)
464 {
465 mu_list_t list;
466 mu_list_create(&list);//FIXME: error checking
467 mu_list_set_destroy_item(list, _cleanup_destroy);
468 mu_list_set_comparator(list, _cleanup_compare);
469 *plist = list;
470 }
471
472 static void
env_cleanup_list_append(mu_list_t list,void * data,void (* func)(void *))473 env_cleanup_list_append(mu_list_t list, void *data, void (*func)(void *))
474 {
475 struct environ_cleanup_closure *clos = mu_zalloc(sizeof(*clos));
476
477 clos->cleanup = func ? func : free;
478 clos->data = data;
479 mu_list_append(list, clos);
480 }
481
482 void
env_function_cleanup_flush(eval_environ_t env,void * ptr)483 env_function_cleanup_flush(eval_environ_t env, void *ptr)
484 {
485 if (ptr) {
486 struct environ_cleanup_closure clos;
487 clos.data = ptr;
488 mu_list_remove(env->function_cleanup_list, &clos);
489 } else {
490 mu_list_clear(env->function_cleanup_list);
491 }
492 }
493
494 void
env_function_cleanup_del(eval_environ_t env,void * ptr)495 env_function_cleanup_del(eval_environ_t env, void *ptr)
496 {
497 int rc;
498 struct environ_cleanup_closure clos, *cptr;
499
500 clos.data = ptr;
501
502 rc = mu_list_locate(env->function_cleanup_list, &clos, (void**)&cptr);
503 if (rc == 0) {
504 cptr->cleanup = NULL;
505 mu_list_remove(env->function_cleanup_list, &clos);
506 }
507 }
508
509 void
env_function_cleanup_add(eval_environ_t env,void * data,void (* func)(void *))510 env_function_cleanup_add(eval_environ_t env, void *data, void (*func)(void *))
511 {
512 env_cleanup_list_append(env->function_cleanup_list, data, func);
513 }
514
515 void
env_environ_cleanup_add(eval_environ_t env,void * data,void (* func)(void *))516 env_environ_cleanup_add(eval_environ_t env, void *data, void (*func)(void *))
517 {
518 env_cleanup_list_append(env->environ_cleanup_list, data, func);
519 }
520
521 /* A call to expand_dataseg (see below) invalidates any C variables that
522 pointed to the dataseg before the call. To avoid dereferencing invalid
523 memory pointers, addresses of such C variables are stored in env->auto_ptr
524 using env_register_auto (it is done by get_string_arg). When
525 expand_dataseg is called, it calls env_fixup_autos and passes it the
526 offset of the new dataseg from the old one. Env_fixup_autos adds this
527 value to every address in auto_ptr, thereby fixing them.
528
529 The auto_ptr array is cleared (by calling env_unregister_autos) after
530 executing each instruction (see eval_environment).
531 */
532 static void
env_register_auto(eval_environ_t env,void * ptr)533 env_register_auto(eval_environ_t env, void *ptr)
534 {
535 char *addr = *(char**)ptr;
536
537 if (env->numautos == MAX_AUTO_PTR)
538 runtime_error(env, "INTERNAL ERROR at %s:%d, please report",
539 __FILE__, __LINE__);
540 /* Check if address is within the dataseg */
541 if (!(addr >= (char*) env->dataseg
542 && (addr < (char*) (env->dataseg + datasize + env->stack_size))))
543 ptr = NULL;
544 env->auto_ptr[env->numautos++] = ptr;
545 }
546
547 /* Pop the last registered auto variable */
548 static void
env_pop_auto(eval_environ_t env)549 env_pop_auto(eval_environ_t env)
550 {
551 env->numautos--;
552 }
553
554 static void
env_unregister_autos(eval_environ_t env)555 env_unregister_autos(eval_environ_t env)
556 {
557 env->numautos = 0;
558 }
559
560 static void
env_fixup_autos(eval_environ_t env,ptrdiff_t offset)561 env_fixup_autos(eval_environ_t env, ptrdiff_t offset)
562 {
563 int i;
564 for (i = 0; i < env->numautos; i++) {
565 STKVAL *pptr = env->auto_ptr[i];
566 if (pptr)
567 mf_c_val(*pptr,str) += offset; /*FIXME*/
568 }
569 }
570
571
572 int
expand_dataseg(eval_environ_t env,size_t count)573 expand_dataseg(eval_environ_t env, size_t count)
574 {
575 STKVAL *newds;
576 ptrdiff_t offset;
577 size_t new_stack_size;
578 size_t diff;
579 enum { DATASEG_MAX = ((size_t)-1) / sizeof(STKVAL) };
580
581 switch (stack_expand_policy) {
582 case stack_expand_add:
583 diff = ((count + stack_expand_incr - 1) / stack_expand_incr)
584 * stack_expand_incr;
585 if (DATASEG_MAX - diff < env->stack_size)
586 return -1;
587 new_stack_size = env->stack_size + diff;
588 break;
589
590 case stack_expand_twice:
591 new_stack_size = env->stack_size;
592 do {
593 if (DATASEG_MAX / 2 < new_stack_size)
594 return -1;
595 new_stack_size *= 2;
596 diff = new_stack_size - env->stack_size;
597 } while (diff < count);
598 }
599
600 if ((stack_max_size && new_stack_size > stack_max_size)
601 || DATASEG_MAX - datasize < new_stack_size
602 || (newds = realloc(env->dataseg,
603 (new_stack_size + datasize)
604 * sizeof env->dataseg[0])) == NULL)
605 return 1;
606
607 offset = (char*)newds - (char*)env->dataseg;
608 env->dataseg = newds;
609 env->stack_size = new_stack_size;
610 env->tos += diff;
611 env->base += diff;
612 memmove(newds + env->tos, newds + env->tos - diff,
613 (datasize + env->stack_size - env->tos)
614 * sizeof newds[0]);
615 env_fixup_autos(env, offset);
616 mu_diag_output(MU_DIAG_WARNING,
617 _("stack segment expanded, new size=%lu"),
618 (unsigned long) env->stack_size);
619 return 0;
620 }
621
622
623 void
prog_trace(eval_environ_t env,const char * fmt,...)624 prog_trace(eval_environ_t env, const char *fmt, ...)
625 {
626 char buf[512];
627 va_list ap;
628
629 va_start(ap, fmt);
630 vsnprintf(buf, sizeof buf, fmt, ap);
631 va_end(ap);
632 logmsg(MU_LOG_DEBUG, "%4lu: %s:%u: %s",
633 (unsigned long) env->pc,
634 ENV_LOC_FILE(env), ENV_LOC_LINE(env),
635 buf);
636 }
637
638 void instr_funcall(eval_environ_t env);
639 void instr_locus(eval_environ_t env);
640
641 void
runtime_stack_trace(eval_environ_t env)642 runtime_stack_trace(eval_environ_t env)
643 {
644 size_t base;
645
646 mu_error(_("stack trace:"));
647 for (base = env->base; base < datasize + env->stack_size - 4;
648 base = mf_c_val(env->dataseg[base + 1], size) + base + 1) {
649 int i;
650 prog_counter_t pc = mf_c_val(env->dataseg[base + 2], ulong) - 1;
651 char *name;
652 struct mu_locus_point *ploc = NULL, loc;
653
654 if (pc < 2)
655 break; /*FIXME*/
656 if (mf_cell_instr(prog[pc-2]) == instr_funcall) {
657 name = (char*)(env->dataseg + mf_cell_c_value(prog[pc-1], size));
658 pc -= 2;
659 } else {
660 name = "(in catch)";
661 pc -= 3;
662 }
663
664 for (i = 0; i < 10; i++) {
665 if (pc > i + 3
666 && prog[pc - i - 3].c_instr == instr_locus) {
667 loc.mu_file = (char*)(env->dataseg
668 + mf_cell_c_value(prog[pc - i - 2], size));
669 loc.mu_line = mf_cell_c_value(prog[pc - i - 1], size);
670 ploc = &loc;
671 break;
672 }
673 }
674
675 if (ploc)
676 mu_error("%04lu: %s:%u: %s",
677 (unsigned long) pc,
678 ploc->mu_file, ploc->mu_line, name);
679 else
680 mu_error("%04lu: %s",
681 (unsigned long) pc, name);
682 }
683 mu_error(_("stack trace finishes"));
684 }
685
686 void
runtime_warning(eval_environ_t env,const char * fmt,...)687 runtime_warning(eval_environ_t env, const char *fmt, ...)
688 {
689 va_list ap;
690
691 mu_diag_printf(MU_DIAG_WARNING,
692 _("RUNTIME WARNING near %s:%u: "),
693 ENV_LOC_FILE(env), ENV_LOC_LINE(env));
694 va_start(ap, fmt);
695 mu_diag_cont_printf(fmt, ap);
696 va_end(ap);
697 mu_diag_cont_printf("\n");
698 }
699
700 void
runtime_error(eval_environ_t env,const char * fmt,...)701 runtime_error(eval_environ_t env, const char *fmt, ...)
702 {
703 int n;
704 va_list ap;
705 char buf[512];
706
707 n = snprintf(buf, sizeof buf, _("RUNTIME ERROR near %s:%u: "),
708 ENV_LOC_FILE(env), ENV_LOC_LINE(env));
709 va_start(ap, fmt);
710 vsnprintf(buf + n, sizeof buf - n, fmt, ap);
711 va_end(ap);
712 mu_error("%s", buf);
713 if (stack_trace_option)
714 runtime_stack_trace(env);
715 env->status = SMFIS_TEMPFAIL; /* FIXME */
716 longjmp(env->x_jmp, 1);
717 }
718
719 STKVAL
get_immediate(eval_environ_t env,unsigned n)720 get_immediate(eval_environ_t env, unsigned n)
721 {
722 return mf_cell_value(prog[env->pc + n + 1]);
723 }
724
725 static void
get_literal(eval_environ_t env,unsigned n,const char ** p)726 get_literal(eval_environ_t env, unsigned n, const char **p)
727 {
728 *p = (char*)(env->dataseg + mf_c_val(get_immediate(env, n), size));
729 env_register_auto(env, p);
730 }
731
732 STKVAL
get_arg(eval_environ_t env,unsigned n)733 get_arg(eval_environ_t env, unsigned n)
734 {
735 return env->dataseg[env->tos + n + 1];
736 }
737
738 void
get_string_arg(eval_environ_t env,unsigned n,char * MFL_DATASEG * p)739 get_string_arg(eval_environ_t env, unsigned n, char * MFL_DATASEG *p)
740 {
741 *p = (char*) (env->dataseg + mf_c_val(get_arg(env, n), size));
742 env_register_auto(env, (void*) p);
743 }
744
745 void
get_numeric_arg(eval_environ_t env,unsigned n,long * np)746 get_numeric_arg(eval_environ_t env, unsigned n, long *np)
747 {
748 *np = mf_c_val(get_arg(env, n), long);
749 }
750
751 void
get_pointer_arg(eval_environ_t env,unsigned n,void * MFL_DATASEG * p)752 get_pointer_arg(eval_environ_t env, unsigned n, void * MFL_DATASEG *p)
753 {
754 *p = mf_c_val(get_arg(env, n), ptr);
755 }
756
757 void
push(eval_environ_t env,STKVAL val)758 push(eval_environ_t env, STKVAL val)
759 {
760 if (env->tos < env->toh)
761 runtime_error(env, "INTERNAL ERROR at %s:%d, please report",
762 __FILE__, __LINE__);
763 if (env->tos == env->toh) {
764 mu_debug(MF_SOURCE_PROG, MU_DEBUG_TRACE8,
765 ("tos=%lu, toh=%lu",
766 (unsigned long) env->tos,
767 (unsigned long) env->toh));
768 if (expand_dataseg(env, 1))
769 runtime_error(env, "%s",
770 _("out of stack space; increase #pragma stacksize"));
771 }
772 env->dataseg[env->tos--] = val;
773 }
774
775 STKVAL
pop(eval_environ_t env)776 pop(eval_environ_t env)
777 {
778 if (env->tos == datasize + env->stack_size - 1)
779 runtime_error(env, _("stack underflow"));
780 return env->dataseg[++env->tos];
781 }
782
783 size_t
heap_reserve_words(eval_environ_t env,size_t words)784 heap_reserve_words(eval_environ_t env, size_t words)
785 {
786 size_t off = env->toh;
787 if (env->toh + words > env->tos) {
788 if (expand_dataseg(env, words))
789 runtime_error(env, "%s",
790 _("heap overrun; increase #pragma stacksize"));
791 }
792 env->toh += words;
793 return off;
794 }
795
796 size_t
heap_reserve(eval_environ_t env,size_t size)797 heap_reserve(eval_environ_t env, size_t size)
798 {
799 return heap_reserve_words(env, B2STACK(size));
800 }
801
802 STKVAL
heap_tempspace(eval_environ_t env,size_t size)803 heap_tempspace(eval_environ_t env, size_t size)
804 {
805 size_t words = B2STACK(size);
806 STKVAL ret;
807 if (env->toh + words > env->tos) {
808 if (expand_dataseg(env, words))
809 runtime_error(env, "%s",
810 _("heap overrun; increase #pragma stacksize"));
811 }
812 mf_c_val(ret, ptr) = env->dataseg + env->toh;
813 return ret;
814 }
815
816 void
heap_obstack_begin(eval_environ_t env)817 heap_obstack_begin(eval_environ_t env)
818 {
819 env->temp_start = env->toh;
820 env->temp_size = 0;
821 }
822
823 void
heap_obstack_cancel(eval_environ_t env)824 heap_obstack_cancel(eval_environ_t env)
825 {
826 env->toh = env->temp_start;
827 env->temp_start = 0;
828 env->temp_size = 0;
829 }
830
831 STKVAL
heap_obstack_finish(eval_environ_t env)832 heap_obstack_finish(eval_environ_t env)
833 {
834 STKVAL ret;
835 mf_c_val(ret, size) = env->temp_start;
836 env->temp_start = 0;
837 env->temp_size = 0;
838 return ret;
839 }
840
841 static inline size_t
heap_obstack_size(eval_environ_t env)842 heap_obstack_size(eval_environ_t env)
843 {
844 return (env->tos - env->toh - B2STACK(env->temp_size))
845 * sizeof env->dataseg[0];
846 }
847
848 void
heap_obstack_vsprintf(eval_environ_t env,const char * fmt,va_list ap)849 heap_obstack_vsprintf(eval_environ_t env, const char *fmt, va_list ap)
850 {
851 size_t n;
852 while (1) {
853 size_t size;
854 va_list apc;
855
856 if (env->tos == env->toh)
857 if (expand_dataseg(env, B2STACK(strlen(fmt)))) {
858 runtime_error(env, "%s",
859 _("memory chunk too big to fit into heap"));
860 }
861
862 size = heap_obstack_size(env);
863 va_copy(apc, ap);
864 n = vsnprintf((char*) env_data_ref(env, env->temp_start) + env->temp_size,
865 size, fmt, apc);
866 va_end(apc);
867 if (n >= size) {
868 if (expand_dataseg(env, B2STACK(n))) {
869 runtime_error(env, "%s",
870 _("memory chunk too big to fit into heap"));
871 }
872 continue;
873 }
874 break;
875 }
876 env->temp_size += n;
877 env->toh += B2STACK(n);
878 }
879
880 void
heap_obstack_sprintf(eval_environ_t env,const char * fmt,...)881 heap_obstack_sprintf(eval_environ_t env, const char *fmt, ...)
882 {
883 va_list ap;
884 va_start(ap, fmt);
885 heap_obstack_vsprintf(env, fmt, ap);
886 va_end(ap);
887 }
888
889 void *
heap_obstack_grow(eval_environ_t env,void * MFL_DATASEG ptr,size_t size)890 heap_obstack_grow(eval_environ_t env, void * MFL_DATASEG ptr, size_t size)
891 {
892 size_t words = B2STACK(size);
893 char *ret;
894
895 env_register_auto(env, (void*) &ptr);
896 if (env->tos - env->toh < words + B2STACK(env->temp_size)) {
897 if (expand_dataseg(env, words))
898 runtime_error(env, "%s",
899 _("memory chunk too big to fit into heap"));
900 }
901 ret = (char*) env_data_ref(env, env->temp_start) + env->temp_size;
902 if (ptr)
903 memmove(ret, ptr, size);
904 env->temp_size += size;
905 env->toh += words;
906 env_pop_auto(env);
907 return ret;
908 }
909
910 void *
heap_obstack_base(eval_environ_t env)911 heap_obstack_base(eval_environ_t env)
912 {
913 return (void*) env_data_ref(env, env->temp_start);
914 }
915
916 STKVAL *
env_data_ref(eval_environ_t env,size_t off)917 env_data_ref(eval_environ_t env, size_t off)
918 {
919 return env->dataseg + off;
920 }
921
922 void
pushs(eval_environ_t env,const char * MFL_DATASEG s)923 pushs(eval_environ_t env, const char * MFL_DATASEG s)
924 {
925 size_t off;
926
927 env_register_auto(env, (void*) &s);
928 off = heap_reserve(env, strlen(s) + 1);
929 strcpy((char*) env_data_ref(env, off), s);
930 env_pop_auto(env);
931 push(env, (STKVAL) off);
932 }
933
934 void
pushn(eval_environ_t env,long n)935 pushn(eval_environ_t env, long n)
936 {
937 push(env, (STKVAL) n);
938 }
939
940 /* Auxiliary instructions */
941
942 void
instr_locus(eval_environ_t env)943 instr_locus(eval_environ_t env)
944 {
945 env->locus.file = mf_c_val(get_immediate(env, 0), uint);
946 env->locus.line = mf_c_val(get_immediate(env, 1), uint);
947 if (PROG_TRACE_ENGINE)
948 prog_trace(env, "LOCUS");
949 advance_pc(env, 2);
950 }
951
952 void
dump_locus(prog_counter_t i)953 dump_locus(prog_counter_t i)
954 {
955 printf("\"%s\" %lu",
956 (char*) (dataseg + mf_cell_c_value(prog[i], size)),
957 (unsigned long) mf_cell_c_value(prog[i+1], size));
958 }
959
960 /* Stack manipulation instructions */
961 void
instr_xchg(eval_environ_t env)962 instr_xchg(eval_environ_t env)
963 {
964 prog_counter_t p = env->tos + 1;
965 STKVAL tmp = env->dataseg[p];
966 env->dataseg[p] = env->dataseg[p + 1];
967 env->dataseg[p + 1] = tmp;
968 if (PROG_TRACE_ENGINE)
969 prog_trace(env, "XCHG");
970 }
971
972 void
instr_dup(eval_environ_t env)973 instr_dup(eval_environ_t env)
974 {
975 if (PROG_TRACE_ENGINE)
976 prog_trace(env, "DUP");
977 push(env, get_arg(env, 0));
978 }
979
980 void
instr_pop(eval_environ_t env)981 instr_pop(eval_environ_t env)
982 {
983 if (PROG_TRACE_ENGINE)
984 prog_trace(env, "POP");
985 pop(env);
986 }
987
988 void
instr_push(eval_environ_t env)989 instr_push(eval_environ_t env)
990 {
991 if (PROG_TRACE_ENGINE)
992 prog_trace(env, "PUSH %p",
993 mf_c_val(get_immediate(env, 0), ptr));
994 push(env, get_immediate(env, 0));
995 advance_pc(env, 1);
996 }
997
998 void
dump_push(prog_counter_t i)999 dump_push(prog_counter_t i)
1000 {
1001 printf("%lx", mf_cell_c_value(prog[i], ulong));
1002 }
1003
1004 void
instr_memstk(eval_environ_t env)1005 instr_memstk(eval_environ_t env)
1006 {
1007 size_t frame = mf_c_val(get_immediate(env, 0), size);
1008 long off = mf_c_val(get_immediate(env, 1), long);
1009 size_t val;
1010
1011 /* The naive approach would be to just
1012
1013 push(env, env_base(env, frame) + off)
1014
1015 However, push() itself might cause dataseg expansion,
1016 which would change tos/toh pointers and render the previously
1017 computed argument value invalid. The correct way is to push
1018 a placeholder value first, thereby occupying stack slot and
1019 eventually causing dataseg expansion, and then compute val
1020 using actual environment:
1021 */
1022
1023 push(env, (STKVAL)0L);
1024 val = env_base(env, frame) + off;
1025 env->dataseg[env->tos+1] = (STKVAL)val;
1026
1027 if (PROG_TRACE_ENGINE)
1028 prog_trace(env, "MEMSTK %lu(%ld)=%lu",
1029 (unsigned long) frame, off,
1030 (unsigned long) val);
1031 advance_pc(env, 2);
1032 }
1033
1034 void
dump_memstk(prog_counter_t i)1035 dump_memstk(prog_counter_t i)
1036 {
1037 printf("%lu(%ld)",
1038 mf_cell_c_value(prog[i], ulong),
1039 mf_cell_c_value(prog[i+1], long));
1040 }
1041
1042 void
instr_xmemstk(eval_environ_t env)1043 instr_xmemstk(eval_environ_t env)
1044 {
1045 size_t frame = mf_c_val(get_arg(env, 1), size);
1046 long off = mf_c_val(get_arg(env, 0), long);
1047 size_t val;
1048
1049 adjust_stack(env, 2);
1050
1051 /* See comment in instr_memstk, above */
1052 push(env, (STKVAL)0L);
1053 val = env_base(env, frame) + off;
1054 env->dataseg[env->tos+1] = (STKVAL)val;
1055
1056 if (PROG_TRACE_ENGINE)
1057 prog_trace(env, "XMEMSTK %lu(%ld)=%lu",
1058 (unsigned long) frame, off,
1059 (unsigned long) val);
1060 }
1061
1062 void
instr_deref(eval_environ_t env)1063 instr_deref(eval_environ_t env)
1064 {
1065 size_t off = mf_c_val(get_arg(env, 0), size);
1066 STKVAL val = env->dataseg[off];
1067
1068 if (PROG_TRACE_ENGINE)
1069 prog_trace(env, "DEREF %lu=%lu (%p)",
1070 (unsigned long) off,
1071 mf_c_val(val, ulong), mf_c_val(val, ptr));
1072 adjust_stack(env, 1);
1073 push(env, val);
1074 }
1075
1076 void
instr_stkalloc(eval_environ_t env)1077 instr_stkalloc(eval_environ_t env)
1078 {
1079 unsigned n = mf_c_val(get_immediate(env, 0), uint);
1080 if (PROG_TRACE_ENGINE)
1081 prog_trace(env, "STKALLOC %x", n);
1082 if (env->tos - n < env->toh) {
1083 mu_debug(MF_SOURCE_PROG, MU_DEBUG_TRACE8,
1084 ("tos=%lu, toh=%lu, delta=%u",
1085 (unsigned long) env->tos,
1086 (unsigned long) env->toh,
1087 n));
1088 if (expand_dataseg(env, env->toh - (env->tos - n)))
1089 runtime_error(env, "%s",
1090 _("Out of stack space; increase #pragma stacksize"));
1091 }
1092 env->tos -= n;
1093 advance_pc(env, 1);
1094 }
1095
1096 void
dump_stkalloc(prog_counter_t i)1097 dump_stkalloc(prog_counter_t i)
1098 {
1099 printf("%u", mf_cell_c_value(prog[i], uint));
1100 }
1101
1102 void
instr_backref(eval_environ_t env)1103 instr_backref(eval_environ_t env)
1104 {
1105 unsigned n = mf_c_val(get_immediate(env, 0), uint);
1106 size_t matchlen;
1107
1108 if (PROG_TRACE_ENGINE)
1109 prog_trace(env, "BACKREF %u",
1110 mf_c_val(get_immediate(env, 0), uint));
1111 advance_pc(env, 1);
1112
1113 if (!env->matches || !env->matchstr) {
1114 /* FIXME: Try to throw exception first:
1115 env_throw(env, mf_no_regex);
1116 */
1117 runtime_error(env, _("no previous regular expression"));
1118 }
1119 if (n > env->matchcount) {
1120 /* FIXME: See above */
1121 runtime_error(env, _("invalid back-reference number"));
1122 }
1123
1124 if (env->matches[n].rm_so == -1) {
1125 push(env, (STKVAL)0L);
1126 } else {
1127 char *s;
1128 size_t off;
1129
1130 matchlen = env->matches[n].rm_eo - env->matches[n].rm_so;
1131 off = heap_reserve(env, matchlen + 1);
1132 s = (char*) env_data_ref(env, off);
1133 memcpy(s,
1134 (const char *) &env->dataseg[env->matchstr] +
1135 env->matches[n].rm_so, matchlen);
1136 s[matchlen] = 0;
1137 push(env, (STKVAL) off);
1138 }
1139 }
1140
1141 void
dump_backref(prog_counter_t i)1142 dump_backref(prog_counter_t i)
1143 {
1144 printf("%u", mf_cell_c_value(prog[i], uint));
1145 }
1146
1147 /* Type conversion instructions */
1148 void
instr_ston(eval_environ_t env)1149 instr_ston(eval_environ_t env)
1150 {
1151 char *s;
1152 char *p;
1153 long v;
1154
1155 get_string_arg(env, 0, &s);
1156 v = strtol(s, &p, 0);
1157
1158 if (PROG_TRACE_ENGINE)
1159 prog_trace(env, "STON %s", s);
1160 adjust_stack(env, 1);
1161 if (*p)
1162 env_throw(env, mfe_ston_conv,
1163 "Cannot convert stack value to number (stopped at %-.8s)",
1164 p);
1165
1166 push(env, (STKVAL) v);
1167 }
1168
1169 void
instr_ntos(eval_environ_t env)1170 instr_ntos(eval_environ_t env)
1171 {
1172 long v = mf_c_val(get_arg(env, 0), long);
1173 char buf[NUMERIC_BUFSIZE_BOUND];
1174
1175 if (PROG_TRACE_ENGINE)
1176 prog_trace(env, "NTOS %ld", v);
1177 adjust_stack(env, 1);
1178
1179 snprintf(buf, sizeof buf, "%ld", v);
1180 pushs(env, buf);
1181 }
1182
1183 /* Evaluation instructions */
1184 void
instr_cmp(eval_environ_t env)1185 instr_cmp(eval_environ_t env)
1186 {
1187 long l = mf_c_val(get_arg(env, 1), long);
1188 long r = mf_c_val(get_arg(env, 0), long);
1189
1190 if (PROG_TRACE_ENGINE)
1191 prog_trace(env, "CMP %ld %ld", l, r);
1192 adjust_stack(env, 2);
1193 pushn(env, l == r);
1194 }
1195
1196 void
instr_symbol(eval_environ_t env)1197 instr_symbol(eval_environ_t env)
1198 {
1199 char *symbol;
1200 const char *s;
1201
1202 get_literal(env, 0, (const char **)&symbol);
1203 s = env->getsym(env->data, symbol);
1204
1205 if (PROG_TRACE_ENGINE)
1206 prog_trace(env, "SYMBOL %s", symbol);
1207 if (!s)
1208 env_throw(env, mfe_macroundef, _("macro not defined: %s"),
1209 symbol);
1210
1211 if (PROG_TRACE_ENGINE)
1212 prog_trace(env, "%s dereferenced to %s", symbol, s);
1213
1214 advance_pc(env, 1);
1215
1216 pushs(env, s);
1217 }
1218
1219 void
dump_symbol(prog_counter_t i)1220 dump_symbol(prog_counter_t i)
1221 {
1222 printf("%08lx %s",
1223 mf_cell_c_value(prog[i], ulong),
1224 (char *) (dataseg + mf_cell_c_value(prog[i], size)));
1225 }
1226
1227 /* Comparation instructions */
1228 void
instr_eqn(eval_environ_t env)1229 instr_eqn(eval_environ_t env)
1230 {
1231 long a = mf_c_val(get_arg(env, 1), long);
1232 long b = mf_c_val(get_arg(env, 0), long);
1233 if (PROG_TRACE_ENGINE)
1234 prog_trace(env, "EQN %ld %ld", a, b);
1235 adjust_stack(env, 2);
1236 pushn(env, a == b);
1237 }
1238
1239 void
instr_eqs(eval_environ_t env)1240 instr_eqs(eval_environ_t env)
1241 {
1242 char *a, *b;
1243 get_string_arg(env, 1, &a);
1244 get_string_arg(env, 0, &b);
1245 if (PROG_TRACE_ENGINE)
1246 prog_trace(env, "EQS %s %s", a, b);
1247 adjust_stack(env, 2);
1248 pushn(env, strcmp(a, b) == 0);
1249 }
1250
1251 void
instr_nen(eval_environ_t env)1252 instr_nen(eval_environ_t env)
1253 {
1254 long a = mf_c_val(get_arg(env, 1), long);
1255 long b = mf_c_val(get_arg(env, 0), long);
1256 if (PROG_TRACE_ENGINE)
1257 prog_trace(env, "NEN %ld %ld", a, b);
1258 adjust_stack(env, 2);
1259 pushn(env, a != b);
1260 }
1261
1262 void
instr_nes(eval_environ_t env)1263 instr_nes(eval_environ_t env)
1264 {
1265 char *a, *b;
1266
1267 get_string_arg(env, 1, &a);
1268 get_string_arg(env, 0, &b);
1269 if (PROG_TRACE_ENGINE)
1270 prog_trace(env, "NES %s %s", a, b);
1271 adjust_stack(env, 2);
1272 pushn(env, strcmp(a, b) != 0);
1273 }
1274
1275 void
instr_ltn(eval_environ_t env)1276 instr_ltn(eval_environ_t env)
1277 {
1278 long a = mf_c_val(get_arg(env, 1), long);
1279 long b = mf_c_val(get_arg(env, 0), long);
1280 if (PROG_TRACE_ENGINE)
1281 prog_trace(env, "LTN %ld %ld", a, b);
1282 adjust_stack(env, 2);
1283 pushn(env, a < b);
1284 }
1285
1286 void
instr_lts(eval_environ_t env)1287 instr_lts(eval_environ_t env)
1288 {
1289 char *a, *b;
1290
1291 get_string_arg(env, 1, &a);
1292 get_string_arg(env, 0, &b);
1293 if (PROG_TRACE_ENGINE)
1294 prog_trace(env, "LTS %s %s", a, b);
1295 adjust_stack(env, 2);
1296 pushn(env, strcmp(a, b) < 0);
1297 }
1298
1299 void
instr_len(eval_environ_t env)1300 instr_len(eval_environ_t env)
1301 {
1302 long a = mf_c_val(get_arg(env, 1), long);
1303 long b = mf_c_val(get_arg(env, 0), long);
1304 if (PROG_TRACE_ENGINE)
1305 prog_trace(env, "LEN %ld %ld", a, b);
1306 adjust_stack(env, 2);
1307 pushn(env, a <= b);
1308 }
1309
1310 void
instr_les(eval_environ_t env)1311 instr_les(eval_environ_t env)
1312 {
1313 char *a, *b;
1314
1315 get_string_arg(env, 1, &a);
1316 get_string_arg(env, 0, &b);
1317 if (PROG_TRACE_ENGINE)
1318 prog_trace(env, "LES %s %s", a, b);
1319 adjust_stack(env, 2);
1320 pushn(env, strcmp(a, b) <= 0);
1321 }
1322
1323 void
instr_gtn(eval_environ_t env)1324 instr_gtn(eval_environ_t env)
1325 {
1326 long a = mf_c_val(get_arg(env, 1), long);
1327 long b = mf_c_val(get_arg(env, 0), long);
1328 if (PROG_TRACE_ENGINE)
1329 prog_trace(env, "GTN %ld %ld", a, b);
1330 adjust_stack(env, 2);
1331 pushn(env, a > b);
1332 }
1333
1334 void
instr_gts(eval_environ_t env)1335 instr_gts(eval_environ_t env)
1336 {
1337 char *a, *b;
1338
1339 get_string_arg(env, 1, &a);
1340 get_string_arg(env, 0, &b);
1341 if (PROG_TRACE_ENGINE)
1342 prog_trace(env, "GTS %s %s", a, b);
1343 adjust_stack(env, 2);
1344 pushn(env, strcmp(a, b) > 0);
1345 }
1346
1347 void
instr_gen(eval_environ_t env)1348 instr_gen(eval_environ_t env)
1349 {
1350 long a = mf_c_val(get_arg(env, 1), long);
1351 long b = mf_c_val(get_arg(env, 0), long);
1352 if (PROG_TRACE_ENGINE)
1353 prog_trace(env, "GEN %ld %ld", a, b);
1354 adjust_stack(env, 2);
1355 pushn(env, a >= b);
1356 }
1357
1358 void
instr_ges(eval_environ_t env)1359 instr_ges(eval_environ_t env)
1360 {
1361 char *a, *b;
1362
1363 get_string_arg(env, 1, &a);
1364 get_string_arg(env, 0, &b);
1365 if (PROG_TRACE_ENGINE)
1366 prog_trace(env, "GES %s %s", a, b);
1367 adjust_stack(env, 2);
1368 pushn(env, strcmp(a, b) >= 0);
1369 }
1370
1371 /* Jump instructions */
1372 void
instr_bz(eval_environ_t env)1373 instr_bz(eval_environ_t env)
1374 {
1375 long v = mf_c_val(get_arg(env, 0), long);
1376 long off = mf_c_val(get_immediate(env, 0), long);
1377
1378 if (PROG_TRACE_ENGINE)
1379 prog_trace(env, "BZ %ld (%ld)", off, v);
1380 adjust_stack(env, 1);
1381 if (v == 0)
1382 advance_pc(env, off);
1383 advance_pc(env, 1);
1384 }
1385
1386 void
dump_branch(prog_counter_t i)1387 dump_branch (prog_counter_t i)
1388 {
1389 printf("%ld (%ld)",
1390 mf_cell_c_value(prog[i], long),
1391 i + mf_cell_c_value(prog[i], long) + 1);
1392 }
1393
1394 void
instr_bnz(eval_environ_t env)1395 instr_bnz(eval_environ_t env)
1396 {
1397 long v = mf_c_val(get_arg(env, 0), long);
1398 long off = mf_c_val(get_immediate(env, 0), long);
1399
1400 if (PROG_TRACE_ENGINE)
1401 prog_trace(env, "BNZ %ld (%ld)", off, v);
1402 adjust_stack(env, 1);
1403 if (v != 0)
1404 advance_pc(env, off);
1405 advance_pc(env, 1);
1406 }
1407
1408 void
instr_jmp(eval_environ_t env)1409 instr_jmp(eval_environ_t env)
1410 {
1411 long off = mf_c_val(get_immediate(env, 0), long);
1412 if (PROG_TRACE_ENGINE)
1413 prog_trace(env, "JMP %ld", off);
1414 advance_pc(env, off+1);
1415 }
1416
1417 /* Longean instructions */
1418 void
instr_not(eval_environ_t env)1419 instr_not(eval_environ_t env)
1420 {
1421 long v = mf_c_val(get_arg(env, 0), long);
1422 if (PROG_TRACE_ENGINE)
1423 prog_trace(env, "NOT %ld", v);
1424 adjust_stack(env, 1);
1425 pushn(env, !v);
1426 }
1427
1428 /* Bitwise arithmetics */
1429 void
instr_logand(eval_environ_t env)1430 instr_logand(eval_environ_t env)
1431 {
1432 unsigned long a = mf_c_val(get_arg(env, 1), ulong);
1433 unsigned long b = mf_c_val(get_arg(env, 0), ulong);
1434 if (PROG_TRACE_ENGINE)
1435 prog_trace(env, "LOGAND %lu %lu", a, b);
1436 adjust_stack(env, 2);
1437 pushn(env, a & b);
1438 }
1439
1440 void
instr_logor(eval_environ_t env)1441 instr_logor(eval_environ_t env)
1442 {
1443 unsigned long a = mf_c_val(get_arg(env, 1), ulong);
1444 unsigned long b = mf_c_val(get_arg(env, 0), ulong);
1445 if (PROG_TRACE_ENGINE)
1446 prog_trace(env, "LOGOR %lu %lu", a, b);
1447 adjust_stack(env, 2);
1448 pushn(env, a | b);
1449 }
1450
1451 void
instr_logxor(eval_environ_t env)1452 instr_logxor(eval_environ_t env)
1453 {
1454 unsigned long a = mf_c_val(get_arg(env, 1), ulong);
1455 unsigned long b = mf_c_val(get_arg(env, 0), ulong);
1456 if (PROG_TRACE_ENGINE)
1457 prog_trace(env, "LOGXOR %lu %lu", a, b);
1458 adjust_stack(env, 2);
1459 pushn(env, a ^ b);
1460 }
1461
1462 void
instr_lognot(eval_environ_t env)1463 instr_lognot(eval_environ_t env)
1464 {
1465 unsigned long v = mf_c_val(get_arg(env, 0), ulong);
1466 if (PROG_TRACE_ENGINE)
1467 prog_trace(env, "LOGNOT %ld", v);
1468 adjust_stack(env, 1);
1469 pushn(env, ~v);
1470 }
1471
1472 /* Arithmetic instructions */
1473 void
instr_add(eval_environ_t env)1474 instr_add(eval_environ_t env)
1475 {
1476 long a = mf_c_val(get_arg(env, 1), long);
1477 long b = mf_c_val(get_arg(env, 0), long);
1478 if (PROG_TRACE_ENGINE)
1479 prog_trace(env, "ADD %ld %ld", a, b);
1480 adjust_stack(env, 2);
1481 pushn(env, a + b);
1482 }
1483
1484 void
instr_sub(eval_environ_t env)1485 instr_sub(eval_environ_t env)
1486 {
1487 long a = mf_c_val(get_arg(env, 1), long);
1488 long b = mf_c_val(get_arg(env, 0), long);
1489 if (PROG_TRACE_ENGINE)
1490 prog_trace(env, "SUB %ld %ld", a, b);
1491 adjust_stack(env, 2);
1492 pushn(env, a - b);
1493 }
1494
1495 void
instr_mul(eval_environ_t env)1496 instr_mul(eval_environ_t env)
1497 {
1498 long a = mf_c_val(get_arg(env, 1), long);
1499 long b = mf_c_val(get_arg(env, 0), long);
1500 if (PROG_TRACE_ENGINE)
1501 prog_trace(env, "MUL %ld %ld", a, b);
1502 adjust_stack(env, 2);
1503 pushn(env, a * b);
1504 }
1505
1506 void
instr_div(eval_environ_t env)1507 instr_div(eval_environ_t env)
1508 {
1509 long a = mf_c_val(get_arg(env, 1), long);
1510 long b = mf_c_val(get_arg(env, 0), long);
1511 if (PROG_TRACE_ENGINE)
1512 prog_trace(env, "DIV %ld %ld", a, b);
1513 adjust_stack(env, 2);
1514 if (b == 0)
1515 env_throw(env, mfe_divzero,
1516 "Division by zero at %08x", (unsigned int) env->pc);
1517 pushn(env, a / b);
1518 }
1519
1520 void
instr_mod(eval_environ_t env)1521 instr_mod(eval_environ_t env)
1522 {
1523 long a = mf_c_val(get_arg(env, 1), long);
1524 long b = mf_c_val(get_arg(env, 0), long);
1525 if (PROG_TRACE_ENGINE)
1526 prog_trace(env, "MOD %ld %ld", a, b);
1527 adjust_stack(env, 2);
1528 if (b == 0)
1529 env_throw(env, mfe_divzero,
1530 "Division by zero at %08x", (unsigned int) env->pc);
1531 pushn(env, a % b);
1532 }
1533
1534 void
instr_shl(eval_environ_t env)1535 instr_shl(eval_environ_t env)
1536 {
1537 long a = mf_c_val(get_arg(env, 1), long);
1538 long b = mf_c_val(get_arg(env, 0), long);
1539 if (PROG_TRACE_ENGINE)
1540 prog_trace(env, "SHL %ld %ld", a, b);
1541 adjust_stack(env, 2);
1542 pushn(env, a << (unsigned long) b);
1543 }
1544
1545 void
instr_shr(eval_environ_t env)1546 instr_shr(eval_environ_t env)
1547 {
1548 long a = mf_c_val(get_arg(env, 1), long);
1549 long b = mf_c_val(get_arg(env, 0), long);
1550 if (PROG_TRACE_ENGINE)
1551 prog_trace(env, "SHR %ld %ld", a, b);
1552 adjust_stack(env, 2);
1553 pushn(env, a >> (unsigned long) b);
1554 }
1555
1556 void
instr_neg(eval_environ_t env)1557 instr_neg(eval_environ_t env)
1558 {
1559 long v = mf_c_val(get_arg(env, 0), long);
1560 if (PROG_TRACE_ENGINE)
1561 prog_trace(env, "NEG %ld", v);
1562 adjust_stack(env, 1);
1563 pushn(env, -v);
1564 }
1565
1566 /* Matching and Regular expression instructions */
1567
1568 /* REGEX: Basically it is useful only for debugging. Its effect is
1569 the same as that of instr_push */
1570 void
instr_regex(eval_environ_t env)1571 instr_regex(eval_environ_t env)
1572 {
1573 char buffer[REGEX_STRING_BUFSIZE];
1574 size_t index = mf_c_val(get_immediate(env, 0), size);
1575
1576 if (PROG_TRACE_ENGINE)
1577 prog_trace(env, "REGEX (%s) %s",
1578 regex_flags_to_string(regtab[index].regflags,
1579 buffer,
1580 sizeof buffer),
1581 (char*) env_data_ref(env, regtab[index].expr));
1582 advance_pc(env, 1);
1583 push(env, (STKVAL) index);
1584 }
1585
1586 void
dump_regex(prog_counter_t i)1587 dump_regex(prog_counter_t i)
1588 {
1589 size_t index = mf_cell_c_value(prog[i], size);
1590 char buffer[REGEX_STRING_BUFSIZE];
1591 printf("(%s) %s",
1592 regex_flags_to_string(regtab[index].regflags,
1593 buffer, sizeof buffer),
1594 (char*)(dataseg + regtab[index].expr));
1595 }
1596
1597 void
instr_regmatch(eval_environ_t env)1598 instr_regmatch(eval_environ_t env)
1599 {
1600 int v;
1601 size_t index = mf_c_val(get_arg(env, 0), size);
1602 regex_t *re = ®tab[index].re;
1603 char *string;
1604
1605 get_string_arg(env, 1, &string);
1606 env->matchstr = (STKVAL*)string - env->dataseg;
1607
1608 adjust_stack(env, 2);
1609
1610 if (PROG_TRACE_ENGINE)
1611 prog_trace(env, "REGMATCH %s %s",
1612 (char*)env_data_ref(env, regtab[index].expr),
1613 string);
1614
1615 env->matchcount = re->re_nsub;
1616 if (env->matchsize < env->matchcount + 1) {
1617 void *p = realloc(env->matches,
1618 sizeof(env->matches[0])
1619 * (env->matchcount + 1));
1620 if (!p)
1621 runtime_error(env, _("not enough memory"));
1622 env->matches = p;
1623 env->matchsize = env->matchcount + 1;
1624 }
1625
1626 v = regexec(re, string, env->matchcount + 1, env->matches, 0);
1627
1628 pushn(env, v == 0);
1629 }
1630
1631 void
instr_regcomp(eval_environ_t env)1632 instr_regcomp(eval_environ_t env)
1633 {
1634 int v;
1635 char buffer[REGEX_STRING_BUFSIZE];
1636 size_t expr_off = mf_c_val(get_arg(env, 0), size);
1637 char *expr;
1638 size_t index = mf_c_val(get_immediate(env, 0), size);
1639 struct rt_regex *rtx = ®tab[index];
1640
1641 get_string_arg(env, 0, &expr);
1642
1643 if (PROG_TRACE_ENGINE)
1644 prog_trace(env, "REGCOMP %s %s",
1645 regex_flags_to_string(rtx->regflags,
1646 buffer, sizeof buffer),
1647 expr);
1648
1649 advance_pc(env, 1);
1650 adjust_stack(env, 1);
1651
1652 if (rtx->compiled) {
1653 regfree(&rtx->re);
1654 rtx->compiled = 0;
1655 }
1656 v = regcomp(&rtx->re, expr, rtx->regflags);
1657 if (v) {
1658 char errbuf[512];
1659 regerror(v, &rtx->re, errbuf, sizeof(errbuf));
1660 env_throw(env, mfe_regcomp,
1661 "compiling regex `%s': %s",
1662 expr,
1663 errbuf);
1664 } else {
1665 rtx->compiled = 1;
1666 rtx->expr = expr_off;
1667 }
1668 push(env, (STKVAL) index);
1669 }
1670
1671 void
dump_regcomp(prog_counter_t i)1672 dump_regcomp(prog_counter_t i)
1673 {
1674 size_t index = mf_cell_c_value(prog[i], size);
1675 struct rt_regex *rtx = ®tab[index];
1676 char buffer[REGEX_STRING_BUFSIZE];
1677 printf("%s", regex_flags_to_string(rtx->regflags,
1678 buffer, sizeof buffer));
1679 }
1680
1681 void
instr_sedcomp(eval_environ_t env)1682 instr_sedcomp(eval_environ_t env)
1683 {
1684 char * MFL_DATASEG str;
1685 size_t index = mf_c_val(get_immediate(env, 0), size);
1686 transform_t *tp = &transform_tab[index];
1687 int flags = mf_c_val(get_immediate(env, 1), int);
1688
1689 get_string_arg(env, 0, &str);
1690 if (PROG_TRACE_ENGINE)
1691 prog_trace(env, "SUBCOMP %s %zu %d", str, index, flags);
1692
1693 advance_pc(env, 2);
1694 adjust_stack(env, 1);
1695
1696 transform_free(*tp);
1697
1698 *tp = transform_compile(str, flags);
1699 if (*tp == NULL)
1700 runtime_error(env,
1701 _("invalid transform string \"%s\": %s"),
1702 str,
1703 transform_error_string());
1704
1705 push(env, (STKVAL) index);
1706 }
1707
1708 void
dump_sedcomp(prog_counter_t i)1709 dump_sedcomp(prog_counter_t i)
1710 {
1711 size_t index = mf_cell_c_value(prog[i], size);
1712 int flags = mf_cell_c_value(prog[i+1], int);
1713 printf("%zu %d", index, flags);
1714 }
1715
1716 void
instr_sed(eval_environ_t env)1717 instr_sed(eval_environ_t env)
1718 {
1719 char * MFL_DATASEG arg;
1720 long i = mf_c_val(get_arg(env, 0), long);
1721 char *res;
1722
1723 get_string_arg(env, 1, &arg);
1724
1725 if (PROG_TRACE_ENGINE)
1726 prog_trace(env, "SED %s %ld", arg, i);
1727
1728 adjust_stack(env, 2);
1729
1730 res = transform_string(transform_tab[i], arg);
1731 pushs(env, res);
1732 free(res);
1733 }
1734
1735 void
instr_fnmatch(eval_environ_t env)1736 instr_fnmatch(eval_environ_t env)
1737 {
1738 char *string, *pattern;
1739
1740 get_string_arg(env, 1, &string);
1741 get_string_arg(env, 0, &pattern);
1742 adjust_stack(env, 2);
1743 if (PROG_TRACE_ENGINE)
1744 prog_trace(env, "FNMATCH %s %s", string, pattern);
1745 pushn(env, fnmatch(pattern, string, 0) == 0);
1746 }
1747
1748 static int
mx_match(eval_environ_t env,char * string,int (* matcher)(const char * name,void * data),void * data)1749 mx_match(eval_environ_t env, char *string,
1750 int (*matcher)(const char *name, void *data), void *data)
1751 {
1752 int rc = 0;
1753 struct dns_reply reply;
1754 mf_status mxstat;
1755
1756 char *p = strchr(string, '@');
1757 if (p)
1758 p++;
1759 else
1760 p = string;
1761 mxstat = dns_to_mf_status(mx_lookup(p, 0, &reply));
1762 rc = 0;
1763 if (mxstat == mf_success) {
1764 int i;
1765
1766 for (i = 0; i < reply.count; i++) {
1767 if (matcher(reply.data.str[i], data)) {
1768 rc = 1;
1769 break;
1770 }
1771 }
1772 }
1773 dns_reply_free(&reply);
1774
1775 if (!mf_resolved(mxstat))
1776 env_throw(env, mf_status_to_exception(mxstat),
1777 "cannot get MXs for %s", p);
1778 return rc;
1779 }
1780
1781 static int
fn_matcher(const char * string,void * data)1782 fn_matcher(const char *string, void *data)
1783 {
1784 return fnmatch (data, string, 0) == 0;
1785 }
1786
1787 void
instr_fnmatch_mx(eval_environ_t env)1788 instr_fnmatch_mx(eval_environ_t env)
1789 {
1790 char *string, *pattern;
1791
1792 get_string_arg(env, 1, &string);
1793 get_string_arg(env, 0, &pattern);
1794 adjust_stack(env, 2);
1795
1796 if (PROG_TRACE_ENGINE)
1797 prog_trace(env, "FNMATCH,MX %s %s", string, pattern);
1798 pushn(env, mx_match(env, string, fn_matcher, pattern));
1799 }
1800
1801 static int
regex_matcher(const char * string,void * data)1802 regex_matcher(const char *string, void *data)
1803 {
1804 return regexec(data, string, 0, NULL, 0) == 0;
1805 }
1806
1807 void
instr_regmatch_mx(eval_environ_t env)1808 instr_regmatch_mx(eval_environ_t env)
1809 {
1810 int rc;
1811 size_t index = mf_c_val(get_arg(env, 0), size);
1812 regex_t *re = ®tab[index].re;
1813 char *string;
1814
1815 get_string_arg(env, 1, &string);
1816 adjust_stack(env, 2);
1817
1818 if (PROG_TRACE_ENGINE)
1819 prog_trace(env, "REGMATCH,MX %s %s",
1820 (char*) env_data_ref(env, regtab[index].expr),
1821 string);
1822
1823 rc = mx_match(env, string, regex_matcher, re);
1824
1825 pushn(env, rc);
1826 }
1827
1828 /* Mail filter specific instructions */
1829
1830 void
instr_next(eval_environ_t env)1831 instr_next(eval_environ_t env)
1832 {
1833 if (PROG_TRACE_ENGINE)
1834 prog_trace(env, "NEXT");
1835 trace("%s%s:%u: next",
1836 mailfromd_msgid(env->ctx),
1837 ENV_LOC_FILE(env), ENV_LOC_LINE(env));
1838 }
1839
1840 void
instr_result(eval_environ_t env)1841 instr_result(eval_environ_t env)
1842 {
1843 sfsistat status = (sfsistat) mf_c_val(get_immediate(env, 0), int);
1844 char *code, *xcode;
1845 char *message;
1846
1847 get_string_arg(env, 2, &message);
1848 get_string_arg(env, 1, &xcode);
1849 get_string_arg(env, 0, &code);
1850
1851 if (PROG_TRACE_ENGINE)
1852 prog_trace(env, "RESULT %d %s %s %s",
1853 status,
1854 SP(code),
1855 SP(xcode),
1856 SP(message));
1857
1858 trace("%s%s:%u: %s %s %s %s",
1859 mailfromd_msgid(env->ctx),
1860 ENV_LOC_FILE(env), ENV_LOC_LINE(env),
1861 sfsistat_str(status),
1862 SP(code),
1863 SP(xcode),
1864 SP(message));
1865
1866 if (code[0] == 0)
1867 code = NULL;
1868 if (xcode[0] == 0)
1869 xcode = NULL;
1870
1871 if (status == SMFIS_ACCEPT && env_msgmod_count(env))
1872 runtime_warning(env, _("`accept' causes previous message "
1873 "modification commands to be ignored; "
1874 "call mmq_purge() prior to `accept', "
1875 "to suppress this warning"));
1876 env->status = status;
1877 env->setreply(env->data, code, xcode, message);
1878 advance_pc(env, 1);
1879 adjust_stack(env, 3);
1880 }
1881
1882 void
dump_result(prog_counter_t i)1883 dump_result(prog_counter_t i)
1884 {
1885 printf("%s", sfsistat_str((sfsistat)mf_cell_c_value(prog[i], int)));
1886 }
1887
1888 void
instr_header(eval_environ_t env)1889 instr_header(eval_environ_t env)
1890 {
1891 struct msgmod_closure *hdr = mu_alloc (sizeof(*hdr));
1892 enum msgmod_opcode opcode =
1893 (enum msgmod_opcode) mf_c_val(get_immediate(env, 0), int);
1894 const char *name;
1895 char *value;
1896
1897 get_string_arg(env, 0, &value);
1898 get_literal(env, 1, &name);
1899
1900 advance_pc(env, 2);
1901 adjust_stack(env, 1);
1902
1903 trace("%s%s:%u: %s %s %s",
1904 mailfromd_msgid(env->ctx),
1905 ENV_LOC_FILE(env), ENV_LOC_LINE(env),
1906 msgmod_opcode_str(opcode),
1907 name, SP(value));
1908
1909 env_msgmod_append(env, opcode, name, value, 1);
1910 }
1911
1912 void
dump_header(prog_counter_t i)1913 dump_header(prog_counter_t i)
1914 {
1915 printf("%s %s",
1916 msgmod_opcode_str((enum msgmod_opcode) mf_cell_c_value(prog[i], int)),
1917 (char*)(dataseg + mf_cell_c_value(prog[i+1], size)));
1918 }
1919
1920 void
instr_builtin(eval_environ_t env)1921 instr_builtin(eval_environ_t env)
1922 {
1923 const char *name;
1924 void (*handler)(eval_environ_t) = mf_c_val(get_immediate(env, 1), ptr);
1925
1926 get_literal(env, 0, &name);
1927 if (PROG_TRACE_ENGINE)
1928 prog_trace(env, "BUILTIN %s", name);
1929 advance_pc(env, 2);
1930 handler(env);
1931 }
1932
1933 void
dump_builtin(prog_counter_t i)1934 dump_builtin(prog_counter_t i)
1935 {
1936 printf("%s ", (char*)(dataseg + mf_cell_c_value(prog[i], size)));
1937 }
1938
1939 void
instr_concat(eval_environ_t env)1940 instr_concat(eval_environ_t env)
1941 {
1942 char * MFL_DATASEG left, * MFL_DATASEG right;
1943 size_t off;
1944 char *res;
1945
1946 get_string_arg(env, 1, &left);
1947 get_string_arg(env, 0, &right);
1948 off = heap_reserve(env, strlen(left) + strlen(right) + 1);
1949 res = (char*) env_data_ref(env, off);
1950
1951 strcat(strcpy(res, left), right);
1952 adjust_stack(env, 2);
1953 if (PROG_TRACE_ENGINE)
1954 prog_trace(env, "CONCAT ('%s','%s')='%s'", left, right, res);
1955 push(env, (STKVAL) off);
1956 }
1957
1958 void
dump_adjust(prog_counter_t i)1959 dump_adjust(prog_counter_t i)
1960 {
1961 printf("%lu ", mf_cell_c_value(prog[i], ulong));
1962 }
1963
1964 void
instr_asgn(eval_environ_t env)1965 instr_asgn(eval_environ_t env)
1966 {
1967 STKVAL val = get_arg(env, 1);
1968 size_t dest = mf_c_val(get_arg(env, 0), size);
1969 adjust_stack(env, 2);
1970 if (PROG_TRACE_ENGINE)
1971 prog_trace(env, "ASGN %lu=%u",
1972 (unsigned long) dest,
1973 mf_c_val(val, uint));
1974 env->dataseg[dest] = val;
1975 }
1976
1977 void
instr_catch(eval_environ_t env)1978 instr_catch(eval_environ_t env)
1979 {
1980 long off = mf_c_val(get_immediate(env, 0), long);
1981 unsigned toff = mf_c_val(get_immediate(env, 1), uint);
1982 size_t count = mf_c_val(env->dataseg[toff], size);
1983 STKVAL *tab = (STKVAL *) (env->dataseg + toff + 1);
1984 size_t i;
1985 prog_counter_t entry = env->pc + 3;
1986
1987 if (PROG_TRACE_ENGINE)
1988 prog_trace(env, "CATCH %ld, %ld", entry, off);
1989
1990 for (i = 0; i < count * NBMBITS; i++)
1991 if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i)) {
1992 if (PROG_TRACE_ENGINE)
1993 prog_trace(env, "CATCH TARGET: %lu %s",
1994 (unsigned long) i,
1995 mf_exception_str(i));
1996 env->catch_ctx[i].pc = entry;
1997 env->catch_ctx[i].tos =
1998 TOS_INVARIANT(env, env->tos);
1999 env->catch_ctx[i].base =
2000 TOS_INVARIANT(env, env->base);
2001 }
2002
2003 advance_pc(env, off);
2004 }
2005
2006 void
dump_catch(prog_counter_t i)2007 dump_catch(prog_counter_t i)
2008 {
2009 size_t toff = mf_cell_c_value(prog[i+1], size);
2010 size_t count = mf_c_val(dataseg[toff], size);
2011 STKVAL *tab = (STKVAL *) (dataseg + toff + 1);
2012 printf("%ld (%ld)",
2013 mf_cell_c_value(prog[i], long),
2014 i + mf_cell_c_value(prog[i], long));
2015 printf("; Targets:");
2016 for (i = 0; i < count * NBMBITS; i++)
2017 if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i))
2018 printf(" %s(%lu)", mf_exception_str(i), (unsigned long) i);
2019 }
2020
2021 void
instr_throw(eval_environ_t env)2022 instr_throw(eval_environ_t env)
2023 {
2024 unsigned long n = mf_c_val(get_immediate(env, 0), ulong);
2025 size_t off = mf_c_val(get_arg(env, 0), size);
2026 advance_pc(env, 1);
2027 adjust_stack(env, 1);
2028 if (n > exception_count)
2029 runtime_error(env, _("invalid exception number: %lu"), n);
2030 if (PROG_TRACE_ENGINE)
2031 prog_trace(env, "THROW %s(%ld)", mf_exception_str(n), n);
2032 env_throw_0(env, (mf_exception) n, off);
2033 }
2034
2035 void
dump_throw(prog_counter_t i)2036 dump_throw(prog_counter_t i)
2037 {
2038 printf("%s (%u)", mf_exception_str(mf_cell_c_value(prog[i], uint)),
2039 mf_cell_c_value(prog[i], uint));
2040 }
2041
2042
2043 void
instr_echo(eval_environ_t env)2044 instr_echo(eval_environ_t env)
2045 {
2046 char *str = (char*) env_data_ref(env, mf_c_val(pop(env), size));
2047 int rc = mu_stream_write(mf_strecho, str, strlen(str), NULL);
2048 if (rc == 0)
2049 rc = mu_stream_write(mf_strecho, "\n", 1, NULL);
2050 if (rc )
2051 logmsg(MU_LOG_EMERG, "cannot write to echo stream: %s",
2052 mu_strerror (rc));
2053 }
2054
2055 void
instr_return(eval_environ_t env)2056 instr_return(eval_environ_t env)
2057 {
2058 if (PROG_TRACE_ENGINE)
2059 prog_trace(env, "RETURN");
2060 env_leave_frame(env, 0);
2061 env->pc--;
2062 }
2063
2064 void
instr_retcatch(eval_environ_t env)2065 instr_retcatch(eval_environ_t env)
2066 {
2067 prog_counter_t pc = env->pc;
2068 if (PROG_TRACE_ENGINE)
2069 prog_trace(env, "RETCATCH");
2070 env_leave_frame(env, 2);
2071 env->pc = pc;
2072 }
2073
2074 void
instr_saveex(eval_environ_t env)2075 instr_saveex(eval_environ_t env)
2076 {
2077 unsigned off = mf_c_val(get_immediate(env, 0), uint);
2078 size_t count = mf_c_val(env->dataseg[off], size);
2079 STKVAL *tab = (STKVAL *) (env->dataseg + off + 1);
2080 size_t i;
2081
2082 if (PROG_TRACE_ENGINE)
2083 prog_trace(env, "SAVEEX %x (%lu ex.)", off,
2084 (unsigned long) count);
2085
2086 advance_pc(env, 1);
2087 for (i = 0; i < count * NBMBITS; i++)
2088 if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i)) {
2089 mu_debug(MF_SOURCE_PROG, MU_DEBUG_TRACE9,
2090 ("Push Exception: %lu %lu <- pc=%lu, tos=%lu, base=%lu",
2091 (unsigned long) i,
2092 (unsigned long) TOS_INVARIANT(env,env->tos),
2093 (unsigned long) env->catch_ctx[i].pc,
2094 (unsigned long) env->catch_ctx[i].tos,
2095 (unsigned long) env->catch_ctx[i].base));
2096 push(env, (STKVAL) env->catch_ctx[i].pc);
2097 push(env, (STKVAL) env->catch_ctx[i].tos);
2098 push(env, (STKVAL) env->catch_ctx[i].base);
2099 }
2100 push(env, (STKVAL) off);
2101 }
2102
2103 void
dump_saveex(prog_counter_t ctr)2104 dump_saveex(prog_counter_t ctr)
2105 {
2106 size_t off = mf_cell_c_value(prog[ctr], size);
2107 size_t count = mf_c_val(dataseg[off], size);
2108 STKVAL *tab = (STKVAL *) (dataseg + off + 1);
2109 size_t i;
2110
2111 printf("%lu:", (unsigned long) count);
2112 for (i = 0; i < count; i++)
2113 if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i))
2114 printf(" %lu", (unsigned long) i);
2115 }
2116
2117 void
instr_restex(eval_environ_t env)2118 instr_restex(eval_environ_t env)
2119 {
2120 unsigned off = mf_c_val(pop(env), uint);
2121 size_t count = mf_c_val(env->dataseg[off], size);
2122 STKVAL *tab = (STKVAL *) (env->dataseg + off + 1);
2123 size_t i;
2124
2125 if (PROG_TRACE_ENGINE)
2126 prog_trace(env, "RESTEX %x (%lu ex.)",
2127 off, (unsigned long) count);
2128 if (!count)
2129 return;
2130 i = count * NBMBITS - 1;
2131 do {
2132 i--;
2133 if (((bitmask_bits_t)mf_c_val(tab[BIT_WORD(i)], uint)) & BIT_MASK(i)) {
2134 env->catch_ctx[i].base = (prog_counter_t) mf_c_val(pop(env), ulong);
2135 env->catch_ctx[i].tos = (prog_counter_t) mf_c_val(pop(env), ulong);
2136 env->catch_ctx[i].pc = (prog_counter_t) mf_c_val(pop(env), ulong);
2137 mu_debug(MF_SOURCE_PROG, MU_DEBUG_TRACE9,
2138 ("Pop Exception: %lu %lu -> pc=%lu, tos=%lu, base=%lu",
2139 (unsigned long) i,
2140 (unsigned long) TOS_INVARIANT(env,env->tos),
2141 (unsigned long) env->catch_ctx[i].pc,
2142 (unsigned long) env->catch_ctx[i].tos,
2143 (unsigned long) env->catch_ctx[i].base));
2144 }
2145 } while (i > 0);
2146 }
2147
2148 void
instr_adjust(eval_environ_t env)2149 instr_adjust(eval_environ_t env)
2150 {
2151 long nargs = mf_c_val(get_immediate(env, 0), long);
2152 if (PROG_TRACE_ENGINE)
2153 prog_trace(env, "ADJUST %ld", nargs);
2154 adjust_stack(env, nargs);
2155 advance_pc(env, 1);
2156 }
2157
2158 void
instr_popreg(eval_environ_t env)2159 instr_popreg(eval_environ_t env)
2160 {
2161 env->reg = pop(env);
2162 if (PROG_TRACE_ENGINE)
2163 prog_trace(env, "POPREG %p", mf_c_val(env->reg, ptr));
2164 }
2165
2166 void
instr_pushreg(eval_environ_t env)2167 instr_pushreg(eval_environ_t env)
2168 {
2169 if (PROG_TRACE_ENGINE)
2170 prog_trace(env, "PUSHREG %p", mf_c_val(env->reg, ptr));
2171 push(env, env->reg);
2172 }
2173
2174 void
instr_funcall(eval_environ_t env)2175 instr_funcall(eval_environ_t env)
2176 {
2177 const char *name;
2178 prog_counter_t pc = (prog_counter_t) mf_c_val(get_immediate(env, 1), ulong);
2179 get_literal(env, 0, &name);
2180 if (PROG_TRACE_ENGINE)
2181 prog_trace(env, "FUNCALL %s (%lu)", name, (unsigned long)pc);
2182 advance_pc(env, 2);
2183 env_make_frame(env);
2184 env->pc = pc-1;
2185 }
2186
2187 void
dump_funcall(prog_counter_t i)2188 dump_funcall(prog_counter_t i)
2189 {
2190 printf("%s (%lu)",
2191 (char*)(dataseg + mf_cell_c_value(prog[i], size)),
2192 mf_cell_c_value(prog[i+1], ulong));
2193 }
2194
2195 /* Opcode:
2196
2197 xlat N OFF
2198
2199 Synopsis:
2200
2201 Scan the table until xI==REG is found or the table is exhausted.
2202 If found, replace REG with yI and return 0. Otherwise, return 1
2203 */
2204 void
instr_xlat(eval_environ_t env)2205 instr_xlat(eval_environ_t env)
2206 {
2207 unsigned long i;
2208 unsigned long count = mf_c_val(get_immediate(env, 0), ulong);
2209 size_t off = mf_c_val(get_immediate(env, 1), size);
2210 STKVAL *tab = (STKVAL *) (env->dataseg + off);
2211
2212 if (PROG_TRACE_ENGINE)
2213 prog_trace(env, "XLAT %lu %lu", count, (unsigned long) off);
2214 advance_pc(env, 2);
2215 for (i = 0; i < count; i += 2) {
2216 if (mf_c_val(tab[i], long) == mf_c_val(env->reg, long)) {
2217 env->reg = tab[i+1];
2218 push(env, (STKVAL)0);
2219 return;
2220 }
2221 }
2222 push(env, (STKVAL)1);
2223 }
2224
2225 void
instr_xlats(eval_environ_t env)2226 instr_xlats(eval_environ_t env)
2227 {
2228 unsigned long i;
2229 unsigned long count = mf_c_val(get_immediate(env, 0), uint);
2230 size_t off = mf_c_val(get_immediate(env, 1), size);
2231 STKVAL *tab = (STKVAL *) (env->dataseg + off);
2232 char *str = (char*) env_data_ref(env, mf_c_val(env->reg, size));
2233
2234 if (PROG_TRACE_ENGINE)
2235 prog_trace(env, "XLATS %lu %lu", count, (unsigned long) off);
2236 advance_pc(env, 2);
2237 for (i = 0; i < count; i += 2) {
2238 if (strcmp((char*)(env->dataseg + mf_c_val(tab[i], size)), str) == 0) {
2239 env->reg = tab[i+1];
2240 push(env, (STKVAL)0);
2241 return;
2242 }
2243 }
2244 push(env, (STKVAL)1);
2245 }
2246
2247 void
dump_xlat(prog_counter_t i)2248 dump_xlat(prog_counter_t i)
2249 {
2250 unsigned long j;
2251 unsigned long count = mf_cell_c_value(prog[i], ulong);
2252 size_t off = mf_cell_c_value(prog[i+1], size);
2253 STKVAL *tab = (STKVAL *) (dataseg + off);
2254
2255 printf("%lu %lu ", count, (unsigned long) off);
2256 for (j = 0; j < count; j += 2)
2257 printf("(%ld %ld) ", mf_c_val(tab[j], long), mf_c_val(tab[j+1], long));
2258 }
2259
2260 void
dump_xlats(prog_counter_t i)2261 dump_xlats(prog_counter_t i)
2262 {
2263 unsigned long j;
2264 unsigned long count = mf_cell_c_value(prog[i], ulong);
2265 size_t off = mf_cell_c_value(prog[i+1], size);
2266 STKVAL *tab = (STKVAL *) (dataseg + off);
2267
2268 printf("%lu %lu ", count, (unsigned long) off);
2269
2270 for (j = 0; j < count; j += 2)
2271 printf("(%08lx %s %ld) ",
2272 mf_c_val(tab[j], ulong),
2273 (char*)(dataseg + mf_c_val(tab[j], size)),
2274 mf_c_val(tab[j+1], long));
2275 }
2276
2277 void
instr_jreg(eval_environ_t env)2278 instr_jreg(eval_environ_t env)
2279 {
2280 env->pc += (prog_counter_t)mf_c_val(env->reg, ulong) - 1;
2281 if (PROG_TRACE_ENGINE)
2282 prog_trace(env, "JREG %ld (%lu)",
2283 (long)((prog_counter_t)mf_c_val(env->reg, ulong) - 1),
2284 (unsigned long)env->pc);
2285 }
2286
2287
2288 static void
_dumper(prog_counter_t pc,struct optab * op,void * data)2289 _dumper(prog_counter_t pc, struct optab *op, void *data)
2290 {
2291 printf("%04lu: ", (unsigned long) pc);
2292 printf("%s ", op->name);
2293 if (op->dump)
2294 op->dump(pc + 1);
2295 putchar('\n');
2296 }
2297
2298 void
dump_code(prog_counter_t start,prog_counter_t end)2299 dump_code(prog_counter_t start, prog_counter_t end)
2300 {
2301 if (end == 0)
2302 end = pc;
2303 scan_code(start, end, _dumper, NULL);
2304 }
2305
2306 static void
_fixup(prog_counter_t pc,struct optab * op,void * data)2307 _fixup(prog_counter_t pc, struct optab *op, void *data)
2308 {
2309 struct mu_locus_range *locus = data;
2310 enum instr_opcode opcode = (enum instr_opcode) mf_cell_c_value(prog[pc], int);
2311 prog[pc].c_instr = op->instr;
2312 if (opcode == opcode_regex) {
2313 int rc;
2314 size_t index = mf_cell_c_value(prog[pc+1], size);
2315 if (index > regcount) {
2316 parse_error_locus(locus,
2317 "Invalid regexp index %lu",
2318 (unsigned long) index);
2319 return;
2320 }
2321 rc = regcomp(®tab[index].re,
2322 (char*) (dataseg + regtab[index].expr),
2323 regtab[index].regflags);
2324 if (rc) {
2325 char errbuf[512];
2326 regerror(rc, ®tab[index].re, errbuf, sizeof(errbuf));
2327 parse_error_locus(locus,
2328 "Cannot compile regex: %s",
2329 errbuf);
2330 } else
2331 regtab[index].compiled = 1;
2332 } else if (opcode == opcode_locus) {
2333 mu_locus_point_set_file(&locus->beg,
2334 (char*) (dataseg + mf_cell_c_value(prog[pc+1], size)));
2335 locus->beg.mu_line = mf_cell_c_value(prog[pc+2], size);
2336 }
2337 }
2338
2339 void
fixup_code()2340 fixup_code()
2341 {
2342 struct mu_locus_range locus = MU_LOCUS_RANGE_INITIALIZER;
2343 scan_code(0, pc, _fixup, &locus);
2344 mu_locus_range_deinit(&locus);
2345 if (error_count)
2346 exit(EX_CONFIG);
2347 }
2348
2349
2350 void
env_init(eval_environ_t env)2351 env_init(eval_environ_t env)
2352 {
2353 /* Initialize status and registers */
2354 env->status = SMFIS_CONTINUE;
2355 env->tos = datasize + env->stack_size - 1;
2356 env->base = 0;
2357 mf_c_val(env->reg, long) = 0; //FIXME
2358
2359 env->matches = NULL;
2360 env->matchsize = 0;
2361 env->matchcount = 0;
2362 env->matchstr = 0;
2363
2364 env->numautos = 0;
2365
2366 /* Initialize catch functions */
2367 if (exception_count)
2368 memcpy(env->catch_ctx, env->defcatch_ctx,
2369 exception_count * sizeof(env->catch_ctx[0]));
2370
2371 env_final_gc(env);
2372 }
2373
2374 /* Initialize the data segment and relocate string variables */
2375 static void
init_dataseg(STKVAL * dseg,size_t count)2376 init_dataseg(STKVAL *dseg, size_t count)
2377 {
2378 memcpy(dseg, dataseg, count * sizeof dataseg[0]);
2379 }
2380
2381 void
env_init_dataseg(eval_environ_t env)2382 env_init_dataseg(eval_environ_t env)
2383 {
2384 init_dataseg(env->dataseg, dvarsize);
2385 }
2386
2387 void
env_make_frame0(eval_environ_t env)2388 env_make_frame0(eval_environ_t env)
2389 {
2390 push(env, (STKVAL) 0);
2391 push(env, (STKVAL) (env->base - env->tos));
2392 env->base = env->tos;
2393 }
2394
2395 void
env_make_frame(eval_environ_t env)2396 env_make_frame(eval_environ_t env)
2397 {
2398 push(env, (STKVAL) (env->pc + 1));
2399 push(env, (STKVAL) (env->base - env->tos));
2400 env->base = env->tos;
2401 }
2402
2403 void
env_leave_frame(eval_environ_t env,int nargs)2404 env_leave_frame(eval_environ_t env, int nargs)
2405 {
2406 env->tos = env->base;
2407 env->base += (prog_counter_t) mf_c_val(pop(env), ulong) + 1;
2408 env->pc = (prog_counter_t) mf_c_val(pop(env), ulong);
2409 adjust_stack(env, nargs);
2410 }
2411
2412 void
env_push_string(eval_environ_t env,char * arg)2413 env_push_string(eval_environ_t env, char *arg)
2414 {
2415 pushs(env, arg);
2416 }
2417
2418 void
env_push_number(eval_environ_t env,long arg)2419 env_push_number(eval_environ_t env, long arg)
2420 {
2421 push(env, (STKVAL) arg);
2422 }
2423
2424 void
env_push_pointer(eval_environ_t env,void * arg)2425 env_push_pointer(eval_environ_t env, void *arg)
2426 {
2427 push(env, (STKVAL) arg);
2428 }
2429
2430 int
eval_environment(eval_environ_t env,prog_counter_t start)2431 eval_environment(eval_environ_t env, prog_counter_t start)
2432 {
2433 if (setjmp(env->x_jmp))
2434 return 1;
2435
2436 for (env->pc = start; ; env->pc++) {
2437 if (env->pc >= pc)
2438 runtime_error(env, _("pc out of range"));
2439 if (!mf_cell_instr(prog[env->pc]))
2440 break;
2441 if (setjmp(env->catch_jmp) == 0) {
2442 (*mf_cell_instr(prog[env->pc]))(env);
2443 env_unregister_autos(env);
2444 }
2445 }
2446 return 0;
2447 }
2448
2449 static void
env_vsprintf_error(const char * fmt,va_list ap)2450 env_vsprintf_error(const char *fmt, va_list ap)
2451 {
2452 mu_error(_("out of memory while formatting error message:"));
2453 mu_verror(fmt, ap);
2454 }
2455
2456 size_t
env_vsprintf(eval_environ_t env,const char * biname,const char * fmt,va_list ap)2457 env_vsprintf(eval_environ_t env, const char *biname,
2458 const char *fmt, va_list ap)
2459 {
2460 size_t n = 0, off;
2461 char *p, *s;
2462
2463 while (1) {
2464 size_t k;
2465 size_t size;
2466 va_list apc;
2467
2468 if (env->tos == env->toh)
2469 if (expand_dataseg(env, B2STACK(strlen(fmt)))) {
2470 env_vsprintf_error(fmt, ap);
2471 break;
2472 }
2473
2474 size = (env->tos - env->toh - 1)
2475 * sizeof env->dataseg[0];
2476 s = (char*) env_data_ref(env, env->toh);
2477
2478 if (biname) {
2479 n = snprintf(s, size, "%s: ", biname);
2480 if (n >= size) {
2481 n += strlen(fmt); /* rough estimate */
2482 if (expand_dataseg(env, B2STACK(n))) {
2483 env_vsprintf_error(fmt, ap);
2484 break;
2485 }
2486 continue;
2487 }
2488 }
2489 va_copy(apc, ap);
2490 k = vsnprintf(s + n, size - n, fmt, apc);
2491 va_end(apc);
2492 if (k >= size) {
2493 if (expand_dataseg(env, B2STACK(k))) {
2494 env_vsprintf_error(fmt, ap);
2495 break;
2496 }
2497 continue;
2498 }
2499 n += k;
2500 break;
2501 }
2502
2503 p = (char*) env_data_ref(env, off = heap_reserve(env, n + 1));
2504 memmove(p, s, n + 1);
2505 return off;
2506 }
2507
2508 void
env_throw_0(eval_environ_t env,mf_exception status,size_t off)2509 env_throw_0(eval_environ_t env, mf_exception status, size_t off)
2510 {
2511 prog_counter_t pc;
2512
2513 env_function_cleanup_flush(env, NULL);
2514 if (status > exception_count)
2515 runtime_error(env, _("unknown exception: %d: %s"),
2516 status, (char*) env_data_ref(env, off));
2517
2518 pc = env->catch_ctx[status].pc;
2519 if (pc) {
2520 /* Restore tos */
2521 env->tos = TOS_INVARIANT(env, env->catch_ctx[status].tos);
2522 env->base = TOS_INVARIANT(env, env->catch_ctx[status].base);
2523 /* Reset the exception to avoid recursion. */
2524 env->catch_ctx[status].pc = 0;
2525 /* Fixup the program counter */
2526 env->pc = pc - 1;
2527 /* Generate normal entry frame */
2528 push(env, (STKVAL) off);
2529 env_push_number(env, status);
2530 env_make_frame(env);
2531 longjmp(env->catch_jmp, 1);
2532 }
2533 runtime_error(env, _("uncaught exception %s: %s"),
2534 mf_exception_str(status),
2535 (char*) env_data_ref(env, off));
2536 }
2537
2538 void
env_throw(eval_environ_t env,mf_exception status,const char * fmt,...)2539 env_throw(eval_environ_t env, mf_exception status, const char *fmt, ...)
2540 {
2541 va_list ap;
2542 size_t off;
2543 va_start(ap, fmt);
2544 off = env_vsprintf(env, NULL, fmt, ap);
2545 va_end(ap);
2546 env_throw_0(env, status, off);
2547 }
2548
2549 void
env_throw_bi(eval_environ_t env,mf_exception status,const char * biname,const char * fmt,...)2550 env_throw_bi(eval_environ_t env, mf_exception status, const char *biname,
2551 const char *fmt, ...)
2552 {
2553 va_list ap;
2554 size_t off;
2555 va_start(ap, fmt);
2556 off = env_vsprintf(env, biname, fmt, ap);
2557 va_end(ap);
2558 env_throw_0(env, status, off);
2559 }
2560
2561 sfsistat
environment_get_status(eval_environ_t env)2562 environment_get_status(eval_environ_t env)
2563 {
2564 return env->status;
2565 }
2566
2567 SMFICTX *
env_get_context(eval_environ_t env)2568 env_get_context(eval_environ_t env)
2569 {
2570 return env->ctx;
2571 }
2572
2573 size_t
env_get_line_count(eval_environ_t env)2574 env_get_line_count(eval_environ_t env)
2575 {
2576 return env->line_count;
2577 }
2578
2579
2580 /* Capturing support:
2581
2582 Captured message is stored in env->stream. Before storing, any
2583 CRs (\r) are removed from the message. (FIXME: Actually, only
2584 those CRs should be removed that are followed by LF. However,
2585 that should not be a problem, since no CRs are allowed in RFC822
2586 messages, unless followed by LF. Anyway, I'll fix that soon.)
2587
2588 The number of lines in stream is stored in env->line_count. It is
2589 used to produce correct message size for functions that need it,
2590 e.g. bi_sa.
2591 */
2592 int
env_capture_start(eval_environ_t env)2593 env_capture_start(eval_environ_t env)
2594 {
2595 int rc;
2596
2597 env->line_count = 0;
2598 if (env->stream) {
2599 /* Drop any previously captured message data */
2600 env_free_captured(env);
2601 mu_header_destroy(&env->header);
2602 /* Truncate existing stream and reposition to its
2603 beginning */
2604 rc = mu_stream_truncate(env->stream, 0);
2605 if (rc == 0 &&
2606 mu_stream_seek(env->stream, 0, SEEK_SET, NULL) == 0)
2607 return 0;
2608
2609 /* If truncate fails, try to re-create the stream */
2610 mu_stream_close(env->stream);
2611 mu_stream_destroy(&env->stream);
2612 }
2613 env->reposition = 0;
2614
2615 rc = mu_temp_file_stream_create(&env->stream, NULL, 0);
2616 if (rc) {
2617 mu_error(_("%scannot create temporary stream: %s"),
2618 mailfromd_msgid(env->ctx), mu_strerror(rc));
2619 return 1;
2620 }
2621 return 0;
2622 }
2623
2624 static void
env_capture_count_lines(eval_environ_t env,const char * buf,size_t size)2625 env_capture_count_lines(eval_environ_t env, const char *buf, size_t size)
2626 {
2627 while (size) {
2628 size_t len;
2629 const char *p = memchr(buf, '\n', size);
2630 if (p) {
2631 env->line_count++;
2632 len = p - buf + 1;
2633 } else
2634 len = size;
2635 buf += len;
2636 size -= len;
2637 }
2638 }
2639
2640 /* FIXME: Use CRLF filter */
2641 int
env_capture_write(eval_environ_t env,const char * buf,size_t size)2642 env_capture_write(eval_environ_t env, const char *buf, size_t size)
2643 {
2644 int rc;
2645
2646 if (!env->stream)
2647 return 1;
2648
2649 if (env->reposition) {
2650 rc = mu_stream_seek(env->stream, 0, SEEK_END, NULL);
2651 if (rc) {
2652 mu_error(_("%stemporary stream seek failed: %s"),
2653 mailfromd_msgid(env->ctx),
2654 mu_strerror(rc));
2655 mu_stream_close(env->stream);
2656 mu_stream_destroy(&env->stream);
2657 return rc;
2658 }
2659 env->reposition = 0;
2660 }
2661
2662 env_capture_count_lines(env, buf, size);
2663 while (size) {
2664 size_t len = mem_search(buf, '\r', size);
2665 rc = mu_stream_write(env->stream, buf, len, NULL);
2666 if (rc) {
2667 mu_error(_("%stemporary stream write failed: %s"),
2668 mailfromd_msgid(env->ctx),
2669 mu_strerror(rc));
2670 mu_stream_close(env->stream);
2671 mu_stream_destroy(&env->stream);
2672 return rc;
2673 }
2674 if (buf[len] == '\r')
2675 len++;
2676 buf += len;
2677 size -= len;
2678 }
2679 return 0;
2680 }
2681
2682 int
env_capture_write_args(eval_environ_t env,...)2683 env_capture_write_args(eval_environ_t env, ...)
2684 {
2685 va_list ap;
2686 char *arg;
2687 int rc = 0;
2688
2689 if (!env->stream)
2690 return 1;
2691 va_start(ap, env);
2692 while (arg = va_arg(ap, char*)) {
2693 if (rc = env_capture_write(env, arg, strlen(arg)))
2694 break;
2695 }
2696 va_end(ap);
2697 return rc;
2698 }
2699
2700 int
env_get_header(eval_environ_t env,mu_header_t * hdr)2701 env_get_header(eval_environ_t env, mu_header_t *hdr)
2702 {
2703 if (!env->header) {
2704 char *text;
2705 int rc;
2706 mu_off_t size;
2707 size_t total;
2708 size_t start;
2709
2710 rc = mu_stream_size(env->stream, &size);
2711 if (rc) {
2712 mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_size", NULL,
2713 rc);
2714 return rc;
2715 }
2716 text = mu_alloc(size + 1);
2717 rc = mu_stream_seek(env->stream, 0, SEEK_SET, NULL);
2718 if (rc) {
2719 mu_diag_funcall(MU_DIAG_ERROR, "mu_stream_seek",
2720 NULL, rc);
2721 free(text);
2722 return rc;
2723 }
2724
2725 /* FIXME: Use "header" filter instead of this loop */
2726 for (total = 0; total < size;) {
2727 size_t nrd;
2728
2729 rc = mu_stream_read(env->stream, text + total,
2730 size - total, &nrd);
2731 if (rc) {
2732 mu_diag_funcall(MU_DIAG_ERROR,
2733 "mu_stream_read",
2734 NULL, rc);
2735 free(text);
2736 return rc;
2737 }
2738 if (nrd == 0)
2739 break;
2740 total += nrd;
2741 }
2742 text[total] = 0;
2743
2744 /* FIXME: Reposition the stream at its end.
2745 This call may happen in the middle of capturing
2746 so I have to make sure to not disturb the capturing
2747 process.
2748
2749 The same effect could have been achieved by using
2750 streamref, but this approach speeds up things a
2751 bit.
2752 */
2753 env_reposition(env);
2754
2755 if (memcmp (text, "From ", 5) == 0)
2756 start = strcspn (text, "\n") + 1;
2757 else
2758 start = 0;
2759 rc = mu_header_create(&env->header, text + start,
2760 total - start);
2761 free(text);
2762 if (rc) {
2763 mu_diag_funcall(MU_DIAG_ERROR, "mu_header_create",
2764 NULL, rc);
2765 return rc;
2766 }
2767 }
2768 *hdr = env->header;
2769 return 0;
2770 }
2771
2772 /* MMQ */
2773
2774 void
env_msgmod_clear(eval_environ_t env)2775 env_msgmod_clear(eval_environ_t env)
2776 {
2777 if (PROG_TRACE_ENGINE)
2778 prog_trace(env, "Clearing message modification queue");
2779 if (env->msgmod)
2780 env->msgmod(env->data, NULL);
2781 mu_list_clear(env->mmq);
2782 }
2783
2784 void
destroy_msgmod_closure(void * item)2785 destroy_msgmod_closure(void *item)
2786 {
2787 struct msgmod_closure *cmd = item;
2788 free(cmd->name);
2789 free(cmd->value);
2790 }
2791
2792 void
env_msgmod_append(eval_environ_t env,enum msgmod_opcode opcode,const char * name,const char * value,unsigned idx)2793 env_msgmod_append(eval_environ_t env, enum msgmod_opcode opcode,
2794 const char *name, const char *value, unsigned idx)
2795 {
2796 struct msgmod_closure *cp = mu_alloc(sizeof *cp);
2797
2798 if (PROG_TRACE_ENGINE)
2799 prog_trace(env, "Registering %s \"%s\" \"%s\" %u",
2800 msgmod_opcode_str(opcode), SP(name), SP(value),
2801 idx);
2802
2803 cp->opcode = opcode;
2804 cp->name = name ? mu_strdup(name) : NULL;
2805 cp->value = value ? mu_strdup(value) : NULL;
2806 cp->idx = idx;
2807
2808 if (!env->mmq) {
2809 mu_list_create(&env->mmq);
2810 mu_list_set_destroy_item(env->mmq, destroy_msgmod_closure);
2811 }
2812 mu_debug(MF_SOURCE_ENGINE, MU_DEBUG_TRACE5,
2813 ("adding msgmod_closure: %s \"%s\" %s %u",
2814 msgmod_opcode_str(cp->opcode),
2815 SP(cp->name), SP(cp->value), cp->idx));
2816 if (env->msgmod)
2817 env->msgmod(env->data, cp);
2818 mu_list_append(env->mmq, cp);
2819 }
2820
2821 size_t
env_msgmod_count(eval_environ_t env)2822 env_msgmod_count(eval_environ_t env)
2823 {
2824 size_t n;
2825 if (!env->mmq)
2826 n = 0;
2827 else
2828 mu_list_count(env->mmq, &n);
2829 return n;
2830 }
2831
2832 int
env_msgmod_apply(eval_environ_t env,mu_list_action_t fun,void * data)2833 env_msgmod_apply(eval_environ_t env, mu_list_action_t fun, void *data)
2834 {
2835 return mu_list_foreach(env->mmq, fun, data);
2836 }
2837
2838
2839 struct builtin_priv { /* Built-in private data structure */
2840 void *(*init)();
2841 void (*destroy)(void*);
2842 int (*free_capture)(void*);
2843 };
2844
2845 static size_t builtin_priv_size;
2846 static size_t builtin_priv_count;
2847 static struct builtin_priv *bi_priv;
2848
2849 static void
builtin_priv_destroy(void * ptr)2850 builtin_priv_destroy(void *ptr)
2851 {
2852 free(ptr);
2853 }
2854
2855 int
builtin_priv_register(void * (* init)(),void (* destroy)(void *),void (* free_capture))2856 builtin_priv_register(void *(*init)(), void (*destroy)(void*),
2857 void (*free_capture))
2858 {
2859 struct builtin_priv *p;
2860 int desc;
2861
2862 if (!init)
2863 return -1;
2864 if (builtin_priv_count == builtin_priv_size) {
2865 if (builtin_priv_size == 0)
2866 builtin_priv_size = 4;
2867 bi_priv = mu_2nrealloc(bi_priv, &builtin_priv_size,
2868 sizeof bi_priv[0]);
2869 }
2870 desc = builtin_priv_count;
2871 p = bi_priv + builtin_priv_count++;
2872 p->init = init;
2873 p->destroy = destroy ? destroy : builtin_priv_destroy;
2874 p->free_capture = free_capture;
2875 return desc;
2876 }
2877
2878 void
env_free_captured(eval_environ_t env)2879 env_free_captured(eval_environ_t env)
2880 {
2881 int i;
2882
2883 if (!env->bi_priv_array)
2884 return;
2885 for (i = 0; i < builtin_priv_count; i++)
2886 if (bi_priv[i].free_capture && env->bi_priv_array[i])
2887 bi_priv[i].free_capture(env->bi_priv_array[i]);
2888 }
2889
2890 void *
env_get_builtin_priv(eval_environ_t env,int id)2891 env_get_builtin_priv(eval_environ_t env, int id)
2892 {
2893 if (id < 0 || id >= builtin_priv_count)
2894 runtime_error(env,
2895 _("unknown built-in private data requested (%d)"),
2896 id);
2897 if (!env->bi_priv_array) {
2898 if (builtin_priv_count == 0)
2899 runtime_error(env,
2900 _("no built-in private data registered"));
2901 env->bi_priv_array = mu_calloc(builtin_priv_count,
2902 sizeof env->bi_priv_array[0]);
2903 }
2904 if (env->bi_priv_array[id] == NULL) {
2905 env->bi_priv_array[id] = bi_priv[id].init();
2906 if (!env->bi_priv_array[id])
2907 runtime_error(env,
2908 _("initial allocation for built-in "
2909 "private data #%d failed"), id);
2910 }
2911 return env->bi_priv_array[id];
2912 }
2913
2914 static void
env_builtin_priv_destroy(eval_environ_t env)2915 env_builtin_priv_destroy(eval_environ_t env)
2916 {
2917 if (env->bi_priv_array) {
2918 int i;
2919 for (i = 0; i < builtin_priv_count; i++)
2920 if (env->bi_priv_array[i])
2921 bi_priv[i].destroy(env->bi_priv_array[i]);
2922 free(env->bi_priv_array);
2923 }
2924 }
2925
2926 eval_environ_t
create_environment(SMFICTX * ctx,const char * (* getsym)(void * data,const char * str),int (* setreply)(void * data,char * code,char * xcode,char * message),void (* msgmod)(void * data,struct msgmod_closure * cp),void * data)2927 create_environment(SMFICTX *ctx,
2928 const char *(*getsym)(void *data, const char *str),
2929 int (*setreply)(void *data, char *code, char *xcode,
2930 char *message),
2931 void (*msgmod)(void *data, struct msgmod_closure *cp),
2932 void *data)
2933 {
2934 struct eval_environ *env = calloc(1, sizeof *env);
2935
2936 if (!env) {
2937 mu_error(_("not enough memory"));
2938 exit(1);
2939 }
2940
2941 env->stack_size = stack_size;
2942 env->dataseg = calloc(stack_size + datasize, sizeof env->dataseg[0]);
2943 if (!env->dataseg) {
2944 mu_error(_("not enough memory"));
2945 exit(1);
2946 }
2947
2948 init_dataseg(env->dataseg, datasize);
2949
2950 env->ctx = ctx;
2951 env->data = data;
2952 env->getsym = getsym;
2953 env->setreply = setreply;
2954 env->msgmod = msgmod;
2955 env->status = SMFIS_CONTINUE;
2956 /* FIXME:
2957 The only registers that we initialize here. The rest is initialized
2958 in env_init. The top of heap should be retained across calls to
2959 handlers, since we store string variables there. This raises stack
2960 size requirements. */
2961 env->toh = datasize;
2962 env->tos = datasize + env->stack_size - 1;
2963
2964 env->bi_priv_array = NULL;
2965
2966 env_cleanup_list_create(&env->function_cleanup_list);
2967 env_cleanup_list_create(&env->environ_cleanup_list);
2968
2969 if (exception_count) {
2970 env->defcatch_ctx = mu_zalloc(exception_count *
2971 sizeof(env->defcatch_ctx[0]));
2972 env->catch_ctx = mu_zalloc(exception_count *
2973 sizeof(env->catch_ctx[0]));
2974 }
2975
2976 return env;
2977 }
2978
2979 void
destroy_environment(eval_environ_t env)2980 destroy_environment(eval_environ_t env)
2981 {
2982 free(env->catch_ctx);
2983 free(env->defcatch_ctx);
2984 free(env->dataseg);
2985 free(env->matches);
2986 mu_stream_destroy(&env->stream);
2987 mu_header_destroy(&env->header);
2988 mu_list_destroy(&env->function_cleanup_list);
2989 mu_list_destroy(&env->environ_cleanup_list);
2990 mu_list_destroy(&env->mmq);
2991 env_builtin_priv_destroy(env);
2992 free(env);
2993 }
2994
2995
2996 struct entry_point {
2997 int ishandler;
2998 prog_counter_t pc;
2999 union {
3000 enum smtp_state tag;
3001 const char *name;
3002 } v;
3003 };
3004
3005 struct enum_data {
3006 size_t i;
3007 struct module *mod;
3008 struct entry_point *base;
3009 };
3010
3011 static int
function_counter(void * sym,void * data)3012 function_counter(void *sym, void *data)
3013 {
3014 struct function *fp = (struct function *)sym;
3015 struct enum_data *d = data;
3016 if (fp->sym.module == d->mod && fp->sym.alias == NULL)
3017 d->i++;
3018 return 0;
3019 }
3020
3021 static int
function_lister(void * sym,void * data)3022 function_lister(void *sym, void *data)
3023 {
3024 struct function *f = sym;
3025 struct enum_data *d = data;
3026
3027 if (f->sym.module == d->mod && f->sym.alias == NULL) {
3028 d->base[d->i].ishandler = 0;
3029 d->base[d->i].v.name = f->sym.name;
3030 d->base[d->i].pc = f->entry;
3031 d->i++;
3032 }
3033 return 0;
3034 }
3035
3036 static int
comp_pc(const void * a,const void * b)3037 comp_pc(const void *a, const void *b)
3038 {
3039 const struct entry_point *ap = a;
3040 const struct entry_point *bp = b;
3041
3042 if (ap->pc < bp->pc)
3043 return -1;
3044 else if (ap->pc > bp->pc)
3045 return 1;
3046 return 0;
3047 }
3048
3049 static void
print_dataseg()3050 print_dataseg()
3051 {
3052 char *p = (char*) dataseg;
3053 size_t s = datasize * sizeof(STKVAL);
3054 size_t off;
3055
3056 printf("Data segment:\n");
3057 printf("-------------\n");
3058
3059 for (off = 0; off < s; ) {
3060 char vbuf[GACOPYZ_VBUFSIZE];
3061 size_t rd = gacopyz_format_vbuf(vbuf, p + off, s - off);
3062 printf("%08lx: %s\n", (unsigned long) off, vbuf);
3063 off += rd;
3064 }
3065 }
3066
3067 void
print_code()3068 print_code()
3069 {
3070 enum smtp_state tag;
3071 struct entry_point *ep; /* Entry points */
3072 size_t epcount = 0; /* Number of entry points */
3073 size_t i;
3074 struct enum_data d;
3075 struct module **modv;
3076 size_t modc;
3077
3078 /* Get all modules */
3079 collect_modules(&modv, &modc);
3080
3081 /* Count all available entry points: */
3082 d.i = 0;
3083 for (i = 0; i < modc; i++) {
3084 d.mod = modv[i];
3085 symtab_enumerate(MODULE_SYMTAB(modv[i], namespace_function),
3086 function_counter, &d);
3087 }
3088 epcount = d.i;
3089 for (tag = smtp_state_first; tag < smtp_state_count; tag++)
3090 if (entry_point[tag])
3091 epcount++;
3092
3093 ep = mu_alloc((epcount+1)*sizeof *ep);
3094
3095 /* Fill in entry points array */
3096 i = 0;
3097 for (tag = smtp_state_first; tag < smtp_state_count; tag++)
3098 if (entry_point[tag]) {
3099 ep[i].ishandler = 1;
3100 ep[i].pc = entry_point[tag];
3101 ep[i].v.tag = tag;
3102 i++;
3103 }
3104
3105 d.base = ep;
3106 d.i = i;
3107 for (i = 0; i < modc; i++) {
3108 d.mod = modv[i];
3109 symtab_enumerate(MODULE_SYMTAB(modv[i], namespace_function),
3110 function_lister, &d);
3111 }
3112
3113 /* Dispose of module vector */
3114 free (modv);
3115
3116 /* Sort the array */
3117 qsort(ep, epcount, sizeof ep[0], comp_pc);
3118 ep[epcount].pc = pc;
3119
3120 /* Actually print the code */
3121
3122 for (i = 0; i < epcount; i++) {
3123 if (ep[i].ishandler)
3124 printf("HANDLER %s\n", state_to_string(ep[i].v.tag));
3125 else
3126 printf("FUNCTION %s\n", ep[i].v.name);
3127 dump_code(ep[i].pc, ep[i+1].pc);
3128 }
3129 free(ep);
3130 print_dataseg();
3131 }
3132
3133
3134 static eval_environ_t genv;
3135 static size_t *s_off;
3136 static size_t s_cnt;
3137
3138 static int
s_off_cmp(const void * a,const void * b)3139 s_off_cmp(const void *a, const void *b)
3140 {
3141 char *pa = mf_c_val(genv->dataseg[*(const size_t *) a], str);
3142 char *pb = mf_c_val(genv->dataseg[*(const size_t *) b], str);
3143
3144 if (pa < pb)
3145 return -1;
3146 else if (pa > pb)
3147 return 1;
3148 return 1;
3149 }
3150
3151 void
env_final_gc(eval_environ_t env)3152 env_final_gc(eval_environ_t env)
3153 {
3154 size_t i;
3155 size_t top = datasize;
3156 size_t bot = env->toh;
3157
3158 genv = env;
3159 /* Prepare s_off/s_count: remove any variables that are not
3160 in heap */
3161 s_off = mu_calloc(dataseg_reloc_count, sizeof s_off[0]);
3162 for (i = 0, s_cnt = 0; i < dataseg_reloc_count; i++) {
3163 size_t p = mf_c_val(env->dataseg[dataseg_reloc[i]], size);
3164 if (top <= p && p < bot)
3165 s_off[s_cnt++] = dataseg_reloc[i];
3166 }
3167
3168 if (s_cnt) {
3169 qsort(s_off, s_cnt, sizeof s_off[0], s_off_cmp);
3170
3171 /* Compact the variables */
3172 env->toh = datasize;
3173 for (i = 0; i < s_cnt; i++) {
3174 size_t off = s_off[i];
3175 #define S_PTR ((char*) env_data_ref(env, mf_c_val(env->dataseg[off], size)))
3176 size_t len = strlen(S_PTR) + 1;
3177 size_t q = heap_reserve(env, len);
3178 memmove(env_data_ref(env, q), S_PTR, len);
3179 env->dataseg[off] = (STKVAL) q;
3180 #undef S_PTR
3181 }
3182 free(s_off);
3183 }
3184 }
3185
3186 void
env_save_catches(eval_environ_t env)3187 env_save_catches(eval_environ_t env)
3188 {
3189 memcpy(env->defcatch_ctx, env->catch_ctx,
3190 exception_count * sizeof env->defcatch_ctx[0]);
3191 }
3192
3193