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(&regpool, 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 = &regtab[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 = &regtab[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 = &regtab[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 = &regtab[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(&regtab[index].re,
2322 			     (char*) (dataseg + regtab[index].expr),
2323 				      regtab[index].regflags);
2324 		if (rc) {
2325 			char errbuf[512];
2326 			regerror(rc, &regtab[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