1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 1999-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils 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    GNU Mailutils 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 GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 /* This module implements execution of MH format strings. */
18 
19 #include <mh.h>
20 #include <mh_format.h>
21 #include <mailutils/mime.h>
22 #include <mailutils/opool.h>
23 
24 #include <string.h>
25 #include <ctype.h>
26 #include <assert.h>
27 #include "mbiter.h"
28 #include "mbchar.h"
29 #include "mbswidth.h"
30 
31 
32 /* String functions */
33 
34 #define MH_STRING_INITIALIZER { 0, NULL }
35 
36 static inline void
mh_string_init(struct mh_string * s)37 mh_string_init (struct mh_string *s)
38 {
39   s->size = 0;
40   s->ptr = NULL;
41 }
42 
43 static void
mh_string_free(struct mh_string * s)44 mh_string_free (struct mh_string *s)
45 {
46   free (s->ptr);
47   s->size = 0;
48   s->ptr = NULL;
49 }
50 
51 static void
mh_string_realloc(struct mh_string * s,size_t length)52 mh_string_realloc (struct mh_string *s, size_t length)
53 {
54   if (length > s->size)
55     {
56       s->ptr = mu_realloc (s->ptr, length);
57       s->ptr[length-1] = 0;
58       s->size = length;
59     }
60 }
61 
62 static inline int
mh_string_is_null(struct mh_string * s)63 mh_string_is_null (struct mh_string *s)
64 {
65   return s->ptr == NULL || s->ptr[0] == 0;
66 }
67 
68 static inline size_t
mh_string_length(struct mh_string * s)69 mh_string_length (struct mh_string *s)
70 {
71   return mh_string_is_null (s) ? 0 : strlen (s->ptr);
72 }
73 
74 static inline char const *
mh_string_value(struct mh_string * s)75 mh_string_value (struct mh_string *s)
76 {
77   return mh_string_is_null (s) ? "" : s->ptr;
78 }
79 
80 static inline void
mh_string_clear(struct mh_string * s)81 mh_string_clear (struct mh_string *s)
82 {
83   if (s->ptr)
84     s->ptr[0] = 0;
85 }
86 
87 static void
mh_string_load(struct mh_string * s,char const * str)88 mh_string_load (struct mh_string *s, char const *str)
89 {
90   if (!str)
91     mh_string_clear (s);
92   else
93     {
94       mh_string_realloc (s, strlen (str) + 1);
95       strcpy (s->ptr, str);
96     }
97 }
98 
99 static void
mh_string_copy(struct mh_fvm * mach,enum regid dst,enum regid src)100 mh_string_copy (struct mh_fvm *mach, enum regid dst, enum regid src)
101 {
102   mh_string_load (&mach->str[dst], mach->str[src].ptr);
103 }
104 
105 
106 static char *_get_builtin_name (mh_builtin_fp ptr);
107 
108 static inline size_t
output_width(struct mh_fvm * mach)109 output_width (struct mh_fvm *mach)
110 {
111   if (mach->width < mach->ind)
112     return 0;
113   return mach->width - mach->ind;
114 }
115 
116 /* Return the length (number of octets) of a substring of
117    string STR of length LEN, such that it contains NCOL
118    multibyte characters. */
119 int
mbsubstrlen(char const * str,size_t len,size_t ncol)120 mbsubstrlen (char const *str, size_t len, size_t ncol)
121 {
122   int ret = 0;
123   mbi_iterator_t iter;
124 
125   if (ncol <= 0)
126     return 0;
127 
128   for (mbi_init (iter, str, len);
129        ncol && mbi_avail (iter);
130        ncol--, mbi_advance (iter))
131     ret += mb_len (mbi_cur (iter));
132   return ret;
133 }
134 
135 /* Return the number of multibyte characters in the first LEN bytes
136    of character string STRING.  */
137 size_t
mbsnlen(char const * str,size_t len)138 mbsnlen (char const *str, size_t len)
139 {
140   int ret = 0;
141   mbi_iterator_t iter;
142 
143   for (mbi_init (iter, str, len); mbi_avail (iter); mbi_advance (iter))
144     ret++;
145   return ret;
146 }
147 
148 /* Compress whitespace in a string (multi-byte) */
149 static void
str_compress_ws(char * str)150 str_compress_ws (char *str)
151 {
152   unsigned char *p;
153   char const *q;
154   size_t size = strlen (str);
155   mbi_iterator_t iter;
156   int space = 0;
157   size_t len;
158 
159   for (p = (unsigned char*) str,
160 	 mbi_init (iter, str, size);
161        mbi_avail (iter);
162        mbi_advance (iter))
163     {
164       if (mb_isspace (mbi_cur (iter)))
165 	{
166 	  if (!space)
167 	    *p++ = ' ';
168 	  space++;
169 	  continue;
170 	}
171       else if (space)
172 	space = 0;
173 
174       len = mb_len (mbi_cur (iter));
175       if ((q = mb_ptr (mbi_cur (iter))) != (char*) p && mb_isprint (mbi_cur (iter)))
176 	memcpy (p, q, len);
177       p += len;
178     }
179   *p = 0;
180 }
181 
182 static inline void
compress_ws(struct mh_fvm * mach,char * str)183 compress_ws (struct mh_fvm *mach, char *str)
184 {
185   if (mach->fmtflags & MH_FMT_COMPWS)
186     str_compress_ws (str);
187 }
188 
189 static void
put_string(struct mh_fvm * mach,char const * str,int len)190 put_string (struct mh_fvm *mach, char const *str, int len)
191 {
192   if (len == 0)
193     return;
194   mu_stream_write (mach->output, str, len, NULL);
195   mach->ind += mbsnwidth (str, len, 0);
196 }
197 
198 static void
print_hdr_segment(struct mh_fvm * mach,char const * str,size_t len)199 print_hdr_segment (struct mh_fvm *mach, char const *str, size_t len)
200 {
201   if (!len)
202     len = strlen (str);
203 
204   if (mbsnlen (str, len) < mach->width)
205     put_string (mach, str, len);
206   else
207     {
208       while (1)
209 	{
210 	  mbi_iterator_t iter;
211 	  size_t rest = output_width (mach);
212 	  size_t width = mbsnlen (str, len);
213 	  size_t off, size;
214 
215 	  if (width <= rest)
216 	    {
217 	      put_string (mach, str, len);
218 	      break;
219 	    }
220 
221 	  size = off = 0;
222 	  for (mbi_init (iter, str, len);
223 	       mbi_avail (iter);
224 	       mbi_advance (iter))
225 	    {
226 	      if (mb_isspace (mbi_cur (iter)))
227 		off = size;
228 	      size += mb_len (mbi_cur (iter));
229 	    }
230 
231 	  if (off > 0)
232 	    {
233 	      put_string (mach, str, off);
234 	      put_string (mach, "\n        ", 9);
235 	      mach->ind = 8;
236 	      str += off;
237 	      len -= off;
238 	    }
239 	  else
240 	    {
241 	      size = mbsubstrlen (str, len, rest);
242 	      put_string (mach, str, len);
243 	      break;
244 	    }
245 	}
246     }
247 }
248 
249 static void
print_hdr_string(struct mh_fvm * mach,char const * str)250 print_hdr_string (struct mh_fvm *mach, char const *str)
251 {
252   char const *p;
253 
254   if (!str)
255     str = "";
256 
257   p = strchr (str, '\n');
258   while (p)
259     {
260       print_hdr_segment (mach, str, p - str + 1);
261       mach->ind = 0;
262       str = p + 1;
263       p = strchr (str, '\n');
264     }
265 
266   if (str[0])
267     print_hdr_segment (mach, str, 0);
268 }
269 
270 static void
print_simple_segment(struct mh_fvm * mach,size_t width,char const * str,size_t len)271 print_simple_segment (struct mh_fvm *mach, size_t width,
272 		      char const *str, size_t len)
273 {
274   size_t rest;
275 
276   if (!str)
277     str = "";
278 
279   if (!len)
280     len = strlen (str);
281 
282   if (!width)
283     width = mach->width;
284 
285   rest = output_width (mach);
286   if (rest == 0)
287     {
288       if (len == 1 && str[0] == '\n')
289 	put_string (mach, str, len);
290       return;
291     }
292 
293   put_string (mach, str, mbsubstrlen (str, len, rest));
294 }
295 
296 static void
print_string(struct mh_fvm * mach,size_t width,char const * str)297 print_string (struct mh_fvm *mach, size_t width, char const *str)
298 {
299   char *p;
300 
301   if (!str)
302     str = "";
303 
304   if (!width)
305     width = mach->width;
306 
307   p = strchr (str, '\n');
308   while (p)
309     {
310       print_simple_segment (mach, width, str, p - str + 1);
311       mach->ind = 0;
312       str = p + 1;
313       p = strchr (str, '\n');
314     }
315 
316   if (str[0])
317     print_simple_segment (mach, width, str, 0);
318 }
319 
320 static void
print_fmt_segment(struct mh_fvm * mach,size_t fmtwidth,char const * str,size_t len)321 print_fmt_segment (struct mh_fvm *mach, size_t fmtwidth, char const *str,
322 		   size_t len)
323 {
324   size_t width = mbsnlen (str, len);
325 
326   if (fmtwidth && width > fmtwidth)
327     {
328       len = mbsubstrlen (str, len, fmtwidth);
329       width = fmtwidth;
330     }
331   else
332     len = mbsubstrlen (str, len, output_width (mach));
333 
334   put_string (mach, str, len);
335 
336   if (fmtwidth > width)
337     {
338       fmtwidth -= width;
339       mach->ind += fmtwidth;
340       while (fmtwidth--)
341 	mu_stream_write (mach->output, " ", 1, NULL);
342     }
343 }
344 
345 static void
print_fmt_string(struct mh_fvm * mach,size_t fmtwidth,char const * str)346 print_fmt_string (struct mh_fvm *mach, size_t fmtwidth, char const *str)
347 {
348   char *p;
349   while ((p = strchr (str, '\n')))
350     {
351       print_fmt_segment (mach, fmtwidth, str, p - str);
352       mu_stream_write (mach->output, "\n", 1, NULL);
353       mach->ind = 0;
354       str = p + 1;
355     }
356   if (str[0])
357     print_fmt_segment (mach, fmtwidth, str, strlen (str));
358 }
359 
360 static void
reset_fmt_defaults(struct mh_fvm * mach)361 reset_fmt_defaults (struct mh_fvm *mach)
362 {
363   const char *p;
364 
365   mach->fmtflags = 0;
366   p = mh_global_profile_get ("Compress-WS", "yes");
367   if (p && (mu_c_strcasecmp (p, "yes") == 0
368 	    || mu_c_strcasecmp (p, "true") == 0))
369     mach->fmtflags |= MH_FMT_COMPWS;
370 }
371 
372 static void
format_num(struct mh_fvm * mach,long num)373 format_num (struct mh_fvm *mach, long num)
374 {
375   int n;
376   char buf[64];
377   char *ptr;
378   int fmtwidth = mach->fmtflags & MH_WIDTH_MASK;
379   char padchar = mach->fmtflags & MH_FMT_ZEROPAD ? '0' : ' ';
380 
381   n = snprintf (buf, sizeof buf, "%ld", num);
382 
383   if (fmtwidth)
384     {
385       if (n > fmtwidth)
386 	{
387 	  ptr = buf + n - fmtwidth;
388 	  *ptr = '?';
389 	}
390       else
391 	{
392 	  int i;
393 	  ptr = buf;
394 	  for (i = n; i < fmtwidth && mach->ind < mach->width;
395 	       i++, mach->ind++)
396 	    mu_stream_write (mach->output, &padchar, 1, NULL);
397 	}
398     }
399   else
400     ptr = buf;
401 
402   print_string (mach, 0, ptr);
403   reset_fmt_defaults (mach);
404 }
405 
406 static void
format_str(struct mh_fvm * mach,char const * str)407 format_str (struct mh_fvm *mach, char const *str)
408 {
409   if (!str)
410     str = "";
411   if (mach->fmtflags)
412     {
413       int len = strlen (str);
414       int fmtwidth = mach->fmtflags & MH_WIDTH_MASK;
415       char padchar = ' ';
416 
417       if (mach->fmtflags & MH_FMT_RALIGN)
418 	{
419 	  int i, n;
420 
421 	  n = fmtwidth - len;
422 	  for (i = 0; i < n && mach->ind < mach->width;
423 	       i++, mach->ind++, fmtwidth--)
424 	    mu_stream_write (mach->output, &padchar, 1, NULL);
425 	}
426 
427       print_fmt_string (mach, fmtwidth, str);
428       reset_fmt_defaults (mach);
429     }
430   else
431     print_string (mach, 0, str);
432 }
433 
434 static int
addr_cmp(void * item,void * data)435 addr_cmp (void *item, void *data)
436 {
437   mu_address_t a = item;
438   mu_address_t b = data;
439   size_t i, count;
440   int rc = 0;
441 
442   mu_address_get_count (a, &count);
443   for (i = 1; rc == 0 && i <= count; i++)
444     {
445       const char *str;
446       if (mu_address_sget_email (a, i, &str) || str)
447 	continue;
448       rc = mu_address_contains_email (b, str);
449     }
450   return rc ? MU_ERR_USER0 : 0;
451 }
452 
453 static int
addrlist_lookup(mu_list_t list,mu_address_t addr)454 addrlist_lookup (mu_list_t list, mu_address_t addr)
455 {
456   return mu_list_foreach (list, addr_cmp, addr);
457 }
458 
459 static int
addr_free(void * item,void * data)460 addr_free (void *item, void *data)
461 {
462   mu_address_t addr = item;
463   mu_address_destroy (&addr);
464   return 0;
465 }
466 
467 static void
addrlist_destroy(mu_list_t * list)468 addrlist_destroy (mu_list_t *list)
469 {
470   mu_list_foreach (*list, addr_free, NULL);
471   mu_list_destroy (list);
472 }
473 
474 /* Execute pre-compiled format on message msg with number msgno.
475  */
476 void
mh_fvm_run(mh_fvm_t mach,mu_message_t msg)477 mh_fvm_run (mh_fvm_t mach, mu_message_t msg)
478 {
479   mach->message = msg;
480 
481   reset_fmt_defaults (mach);
482   mu_list_clear (mach->addrlist);
483   mh_string_init (&mach->str[R_REG]);
484   mh_string_init (&mach->str[R_ARG]);
485   mh_string_init (&mach->str[R_ACC]);
486 
487   mach->pc = 1;
488   mach->stop = 0;
489   mach->ind = 0;
490   mach->tos = 0;
491   mach->maxstack = 0;
492   mach->numstack = 0;
493   while (!mach->stop)
494     {
495       mh_opcode_t opcode;
496 
497       switch (opcode = MHI_OPCODE (mach->prog[mach->pc++]))
498 	{
499 	case mhop_stop:
500 	  mach->stop = 1;
501 	  break;
502 
503 	case mhop_branch:
504 	  mach->pc += MHI_NUM (mach->prog[mach->pc]);
505 	  break;
506 
507 	case mhop_brzn:
508 	  {
509 	    int res = mach->num[R_REG] == 0;
510 	    if (res)
511 	      mach->pc += MHI_NUM (mach->prog[mach->pc]);
512 	    else
513 	      mach->pc++;
514 	    mach->num[R_REG] = !res;
515 	  }
516 	  break;
517 
518 	case mhop_brzs:
519 	  {
520 	    int res = mh_string_is_null (&mach->str[R_REG]);
521 	    if (res)
522 	      mach->pc += MHI_NUM (mach->prog[mach->pc]);
523 	    else
524 	      mach->pc++;
525 	    mach->num[R_REG] = !res;
526 	  }
527 	  break;
528 
529 	case mhop_setn:
530 	  {
531 	    long reg = MHI_NUM (mach->prog[mach->pc++]);
532 	    mach->num[reg] = MHI_NUM (mach->prog[mach->pc++]);
533 	  }
534 	  break;
535 
536 	case mhop_sets:
537 	  {
538 	    long reg = MHI_NUM (mach->prog[mach->pc++]);
539 	    size_t skip = MHI_NUM (mach->prog[mach->pc++]);
540 	    char const *str = MHI_STR (mach->prog[mach->pc]);
541 
542 	    mach->pc += skip;
543 
544 	    mh_string_load (&mach->str[reg], str);
545 	  }
546 	  break;
547 
548 	case mhop_movn:
549 	  {
550 	    long dst = MHI_NUM (mach->prog[mach->pc++]);
551 	    long src = MHI_NUM (mach->prog[mach->pc++]);
552 	    mach->num[dst] = mach->num[src];
553 	  }
554 	  break;
555 
556 	case mhop_movs:
557 	  {
558 	    long dst = MHI_NUM (mach->prog[mach->pc++]);
559 	    long src = MHI_NUM (mach->prog[mach->pc++]);
560 	    mh_string_copy (mach, dst, src);
561 	    /* FIXME: perhaps copy, not move? */
562 	  }
563 	  break;
564 
565 	case mhop_pushn:
566 	  if (mach->tos == mach->maxstack)
567 	    mach->numstack = mu_2nrealloc (mach->numstack, &mach->maxstack,
568 					   sizeof (mach->numstack[0]));
569 	  mach->numstack[mach->tos++] = mach->num[R_REG];
570 	  break;
571 
572 	case mhop_popn:
573 	  assert (mach->tos > 0);
574 	  mach->num[R_REG] = mach->numstack[--mach->tos];
575 	  break;
576 
577 	case mhop_xchgn:
578 	  assert (mach->tos > 0);
579 	  {
580 	    long t = mach->numstack[mach->tos-1];
581 	    mach->numstack[mach->tos-1] = mach->num[R_REG];
582 	    mach->num[R_REG] = t;
583 	  }
584 	  break;
585 
586 	case mhop_ldcomp:
587 	  {
588 	    long reg = MHI_NUM (mach->prog[mach->pc++]);
589 	    size_t skip = MHI_NUM (mach->prog[mach->pc++]);
590 	    char const *comp = MHI_STR (mach->prog[mach->pc]);
591 	    mu_header_t hdr = NULL;
592 	    char *value = NULL;
593 
594 	    mach->pc += skip;
595 
596 	    mu_message_get_header (mach->message, &hdr);
597 	    mu_header_aget_value_unfold (hdr, comp, &value);
598 	    mh_string_clear (&mach->str[reg]);
599 	    if (value)
600 	      {
601       		compress_ws (mach, value);
602 		mh_string_load (&mach->str[reg], value);
603 		free (value);
604 	      }
605 	  }
606 	  break;
607 
608 	case mhop_ldbody:
609 	  {
610 	    long reg = MHI_NUM (mach->prog[mach->pc++]);
611 	    mu_body_t body = NULL;
612 	    mu_stream_t stream = NULL;
613 	    size_t size = 0;
614 	    size_t rest = output_width (mach);
615 
616 	    mh_string_clear (&mach->str[reg]);
617 
618 	    mu_message_get_body (mach->message, &body);
619 	    mu_body_size (body, &size);
620 	    if (size == 0)
621 	      break;
622 	    mu_body_get_streamref (body, &stream);
623 	    if (stream)
624 	      {
625 		if (size > rest)
626 		  size = rest;
627 
628 		mh_string_realloc (&mach->str[reg], size + 1);
629 		mu_stream_read (stream, mach->str[reg].ptr, size, NULL);
630 		mach->str[reg].ptr[size] = 0;
631 		compress_ws (mach, mach->str[reg].ptr);
632 
633 		mu_stream_destroy (&stream);
634 	      }
635 	  }
636 	  break;
637 
638 	case mhop_call:
639 	  MHI_BUILTIN (mach->prog[mach->pc++]) (mach);
640 	  break;
641 
642 	  /* Convert string register to number */
643 	case mhop_atoi:
644 	  {
645 	    if (mh_string_is_null (&mach->str[R_REG]))
646 	      mach->num[R_REG] = 0;
647 	    else
648 	      mach->num[R_REG] = strtoul (mach->str[R_REG].ptr, NULL, 0);
649 	  }
650 	  break;
651 
652 	  /* Convert numeric register to string */
653 	case mhop_itoa:
654 	  {
655 	    mu_asnprintf (&mach->str[R_REG].ptr, &mach->str[R_REG].size,
656 			  "%lu", mach->num[R_REG]);
657 	  }
658 	  break;
659 
660 	case mhop_printn:
661 	  format_num (mach, mach->num[R_REG]);
662 	  break;
663 
664 	case mhop_prints:
665 	  format_str (mach, mach->str[R_REG].ptr);
666 	  break;
667 
668 	case mhop_printlit:
669 	  {
670 	    size_t skip = MHI_NUM (mach->prog[mach->pc++]);
671 	    char const *str = MHI_STR (mach->prog[mach->pc]);
672 	    format_str (mach, str);
673 	    mach->pc += skip;
674 	  }
675 	  break;
676 
677 	case mhop_fmtspec:
678 	  mach->fmtflags = MHI_NUM (mach->prog[mach->pc++]);
679 	  break;
680 
681 	default:
682 	  mu_error (_("INTERNAL ERROR: Unknown opcode: %x"), opcode);
683 	  abort ();
684 	}
685     }
686   if ((mach->flags & MH_FMT_FORCENL) && mach->ind != 0)
687     put_string (mach, "\n", 1);
688 }
689 
690 static int
msg_uid_1(mu_message_t msg MU_ARG_UNUSED,size_t * ret)691 msg_uid_1 (mu_message_t msg MU_ARG_UNUSED, size_t *ret)
692 {
693   if (!ret)
694     return MU_ERR_OUT_PTR_NULL;
695   *ret = 1;
696   return 0;
697 }
698 
699 int
mh_format_str(mh_format_t fmt,char * str,size_t width,char ** pstr)700 mh_format_str (mh_format_t fmt, char *str, size_t width, char **pstr)
701 {
702   mu_message_t msg = NULL;
703   mu_header_t hdr = NULL;
704   int rc = 0;
705   mu_stream_t outstr;
706   char *buf;
707   mu_off_t size;
708 
709   mh_fvm_t fvm;
710 
711   MU_ASSERT (mu_message_create (&msg, NULL));
712   MU_ASSERT (mu_message_get_header (msg, &hdr));
713   MU_ASSERT (mu_header_set_value (hdr, "text", str, 1));
714   MU_ASSERT (mu_memory_stream_create (&outstr, MU_STREAM_RDWR));
715   MU_ASSERT (mu_message_set_uid (msg, msg_uid_1, NULL));
716 
717   mh_fvm_create (&fvm, 0);
718   mh_fvm_set_output (fvm, outstr);
719   mh_fvm_set_width (fvm, width);
720   mh_fvm_set_format (fvm, fmt);
721   mh_fvm_run (fvm, msg);
722   mh_fvm_destroy (&fvm);
723 
724   MU_ASSERT (mu_stream_size (outstr, &size));
725   buf = mu_alloc (size + 1);
726   MU_ASSERT (mu_stream_seek (outstr, 0, MU_SEEK_SET, NULL));
727   MU_ASSERT (mu_stream_read (outstr, buf, size, NULL));
728   buf[size] = 0;
729 
730   *pstr = buf;
731 
732   mu_message_destroy (&msg, NULL);
733   mu_stream_destroy (&outstr);
734 
735   return rc;
736 }
737 
738 /* Built-in functions */
739 
740 /* Handler for unimplemented functions */
741 static void
builtin_not_implemented(char * name)742 builtin_not_implemented (char *name)
743 {
744   mu_error ("%s is not yet implemented.", name);
745 }
746 
747 static void
builtin_msg(struct mh_fvm * mach)748 builtin_msg (struct mh_fvm *mach)
749 {
750   size_t msgno;
751   mh_message_number (mach->message, &msgno);
752   mach->num[R_REG] = msgno;
753 }
754 
755 static void
builtin_cur(struct mh_fvm * mach)756 builtin_cur (struct mh_fvm *mach)
757 {
758   size_t msgno;
759   size_t cur;
760   int rc;
761   mu_mailbox_t mbox;
762 
763   rc = mu_message_get_mailbox (mach->message, &mbox);
764   if (rc)
765     {
766       mu_diag_funcall (MU_DIAG_ERROR, "mu_message_get_mailbox", NULL, rc);
767       exit (1);
768     }
769   mh_message_number (mach->message, &msgno);
770   mh_mailbox_get_cur (mbox, &cur); /* FIXME: Cache this */
771   mach->num[R_REG] = msgno == cur;
772 }
773 
774 static void
builtin_size(struct mh_fvm * mach)775 builtin_size (struct mh_fvm *mach)
776 {
777   size_t size;
778   if (mu_message_size (mach->message, &size) == 0)
779     mach->num[R_REG] = size;
780   else
781     mach->num[R_REG] = 0;
782 }
783 
784 static void
builtin_strlen(struct mh_fvm * mach)785 builtin_strlen (struct mh_fvm *mach)
786 {
787   mach->num[R_REG] = mh_string_length (&mach->str[R_REG]);
788 }
789 
790 static void
builtin_width(struct mh_fvm * mach)791 builtin_width (struct mh_fvm *mach)
792 {
793   mach->num[R_REG] = mach->width;
794 }
795 
796 static void
builtin_charleft(struct mh_fvm * mach)797 builtin_charleft (struct mh_fvm *mach)
798 {
799   mach->num[R_REG] = output_width (mach);
800 }
801 
802 static void
builtin_timenow(struct mh_fvm * mach)803 builtin_timenow (struct mh_fvm *mach)
804 {
805   time_t t;
806 
807   time (&t);
808   mach->num[R_REG] = t;
809 }
810 
811 static void
builtin_me(struct mh_fvm * mach)812 builtin_me (struct mh_fvm *mach)
813 {
814   mh_string_load (&mach->str[R_REG], mh_get_my_user_name ());
815 }
816 
817 static void
builtin_myhost(struct mh_fvm * mach)818 builtin_myhost (struct mh_fvm *mach)
819 {
820   mh_string_load (&mach->str[R_REG], mh_my_host ());
821 }
822 
823 static void
builtin_myname(struct mh_fvm * mach)824 builtin_myname (struct mh_fvm *mach)
825 {
826   mh_string_load (&mach->str[R_REG], mh_get_my_real_name ());
827 }
828 
829 static void
builtin_localmbox(struct mh_fvm * mach)830 builtin_localmbox (struct mh_fvm *mach)
831 {
832   mh_string_load (&mach->str[R_REG], mh_my_email ());
833 }
834 
835 static void
builtin_eq(struct mh_fvm * mach)836 builtin_eq (struct mh_fvm *mach)
837 {
838   mach->num[R_REG] = mach->num[R_REG] == mach->num[R_ARG];
839 }
840 
841 static void
builtin_ne(struct mh_fvm * mach)842 builtin_ne (struct mh_fvm *mach)
843 {
844   mach->num[R_REG] = mach->num[R_REG] != mach->num[R_ARG];
845 }
846 
847 static void
builtin_gt(struct mh_fvm * mach)848 builtin_gt (struct mh_fvm *mach)
849 {
850   mach->num[R_REG] = mach->num[R_REG] > mach->num[R_ARG];
851 }
852 
853 static void
builtin_match(struct mh_fvm * mach)854 builtin_match (struct mh_fvm *mach)
855 {
856   mach->num[R_REG] = strstr (mh_string_value (&mach->str[R_REG]),
857 			     mh_string_value (&mach->str[R_ARG])) != NULL;
858 }
859 
860 static void
builtin_amatch(struct mh_fvm * mach)861 builtin_amatch (struct mh_fvm *mach)
862 {
863   char const *arg = mh_string_value (&mach->str[R_ARG]);
864   size_t len = strlen (arg);
865   mach->num[R_REG] =
866     strncmp (mh_string_value (&mach->str[R_REG]), arg, len) == 0;
867 }
868 
869 static void
builtin_plus(struct mh_fvm * mach)870 builtin_plus (struct mh_fvm *mach)
871 {
872   mach->num[R_REG] += mach->num[R_ARG];
873 }
874 
875 static void
builtin_minus(struct mh_fvm * mach)876 builtin_minus (struct mh_fvm *mach)
877 {
878   mach->num[R_REG] -= mach->num[R_ARG];
879 }
880 
881 static void
builtin_divide(struct mh_fvm * mach)882 builtin_divide (struct mh_fvm *mach)
883 {
884   if (mach->num[R_ARG] == 0)
885     {
886       /* TRANSLATORS: Do not translate the word 'format'! */
887       mu_error (_("format: divide by zero"));
888       mach->stop = 1;
889     }
890   else
891     mach->num[R_REG] /= mach->num[R_ARG];
892 }
893 
894 static void
builtin_modulo(struct mh_fvm * mach)895 builtin_modulo (struct mh_fvm *mach)
896 {
897   if (mach->num[R_ARG] == 0)
898     {
899       mu_error (_("format: divide by zero"));
900       mach->stop = 1;
901     }
902   else
903     mach->num[R_REG] %= mach->num[R_ARG];
904 }
905 
906 static void
builtin_getenv(struct mh_fvm * mach)907 builtin_getenv (struct mh_fvm *mach)
908 {
909   char const *val = getenv (mh_string_value (&mach->str[R_ARG]));
910   mh_string_load (&mach->str[R_REG], val);
911 }
912 
913 static void
builtin_profile(struct mh_fvm * mach)914 builtin_profile (struct mh_fvm *mach)
915 {
916   char const *val = mh_global_profile_get (mh_string_value (&mach->str[R_ARG]),
917 					   "");
918   mh_string_load (&mach->str[R_REG], val);
919 }
920 
921 static void
builtin_nonzero(struct mh_fvm * mach)922 builtin_nonzero (struct mh_fvm *mach)
923 {
924   mach->num[R_REG] = mach->num[R_ARG] != 0;
925 }
926 
927 static void
builtin_zero(struct mh_fvm * mach)928 builtin_zero (struct mh_fvm *mach)
929 {
930   mach->num[R_REG] = mach->num[R_ARG] == 0;
931 }
932 
933 static void
builtin_null(struct mh_fvm * mach)934 builtin_null (struct mh_fvm *mach)
935 {
936   mach->num[R_REG] = mh_string_is_null (&mach->str[R_ARG]);
937 }
938 
939 static void
builtin_nonnull(struct mh_fvm * mach)940 builtin_nonnull (struct mh_fvm *mach)
941 {
942   mach->num[R_REG] = !mh_string_is_null (&mach->str[R_ARG]);
943 }
944 
945 /*     comp       comp     string   Set str to component text*/
946 static void
builtin_comp(struct mh_fvm * mach)947 builtin_comp (struct mh_fvm *mach)
948 {
949   /* FIXME: Check this */
950   mh_string_copy (mach, R_REG, R_ARG);
951 }
952 
953 /*     compval    comp     integer  num set to "atoi(comp)"*/
954 static void
builtin_compval(struct mh_fvm * mach)955 builtin_compval (struct mh_fvm *mach)
956 {
957   /* FIXME: Check this */
958   mach->num[R_REG] = strtol (mh_string_value (&mach->str[R_ARG]), NULL, 0);
959 }
960 
961 /*     trim       expr              trim trailing white-space from str*/
962 static void
builtin_trim(struct mh_fvm * mach)963 builtin_trim (struct mh_fvm *mach)
964 {
965   if (!mh_string_is_null (&mach->str[R_REG]))
966     mu_rtrim_class (mach->str[R_REG].ptr, MU_CTYPE_SPACE);
967 }
968 
969 static void
_parse_date(struct mh_fvm * mach,struct tm * tm,struct mu_timezone * tz,int * pflags)970 _parse_date (struct mh_fvm *mach, struct tm *tm, struct mu_timezone *tz,
971 	     int *pflags)
972 {
973   char const *date = mh_string_value (&mach->str[R_ARG]);
974   int flags;
975 
976   if (!(mu_parse_date_dtl (date, NULL, NULL, tm, tz, &flags) == 0
977 	&& (flags & (MU_PD_MASK_DATE|MU_PD_MASK_TIME))))
978     {
979 
980       /*mu_error ("can't parse date: [%s]", date);*/
981       if (tm)
982 	{
983 	  time_t t;
984 	  time (&t);
985 	  *tm = *localtime (&t);
986 	}
987 
988       if (tz)
989 	mu_datetime_tz_local (tz);
990       flags = 0;
991     }
992   if (pflags)
993     *pflags = flags;
994 }
995 
996 /*     sec        date     integer  seconds of the minute*/
997 static void
builtin_sec(struct mh_fvm * mach)998 builtin_sec (struct mh_fvm *mach)
999 {
1000   struct tm tm;
1001 
1002   _parse_date (mach, &tm, NULL, NULL);
1003   mach->num[R_REG] = tm.tm_sec;
1004 }
1005 
1006 /*     min        date     integer  minutes of the hour*/
1007 static void
builtin_min(struct mh_fvm * mach)1008 builtin_min (struct mh_fvm *mach)
1009 {
1010   struct tm tm;
1011 
1012   _parse_date (mach, &tm, NULL, NULL);
1013 
1014   mach->num[R_REG] = tm.tm_min;
1015 }
1016 
1017 /*     hour       date     integer  hours of the day (0-23)*/
1018 static void
builtin_hour(struct mh_fvm * mach)1019 builtin_hour (struct mh_fvm *mach)
1020 {
1021   struct tm tm;
1022 
1023   _parse_date (mach, &tm, NULL, NULL);
1024 
1025   mach->num[R_REG] = tm.tm_hour;
1026 }
1027 
1028 /*     wday       date     integer  day of the week (Sun=0)*/
1029 static void
builtin_wday(struct mh_fvm * mach)1030 builtin_wday (struct mh_fvm *mach)
1031 {
1032   struct tm tm;
1033 
1034   _parse_date (mach, &tm, NULL, NULL);
1035 
1036   mach->num[R_REG] = tm.tm_wday;
1037 }
1038 
1039 /*     day        date     string   day of the week (abbrev.)*/
1040 static void
builtin_day(struct mh_fvm * mach)1041 builtin_day (struct mh_fvm *mach)
1042 {
1043   struct tm tm;
1044   char buf[80];
1045 
1046   _parse_date (mach, &tm, NULL, NULL);
1047 
1048   strftime (buf, sizeof buf, "%a", &tm);
1049   mh_string_load (&mach->str[R_REG], buf);
1050 }
1051 
1052 /*     weekday    date     string   day of the week */
1053 static void
builtin_weekday(struct mh_fvm * mach)1054 builtin_weekday (struct mh_fvm *mach)
1055 {
1056   struct tm tm;
1057   char buf[80];
1058 
1059   _parse_date (mach, &tm, NULL, NULL);
1060 
1061   strftime (buf, sizeof buf, "%A", &tm);
1062   mh_string_load (&mach->str[R_REG], buf);
1063 }
1064 
1065 /*      sday       date     integer  day of the week known?
1066 	(1=explicit,0=implicit,-1=unknown) */
1067 static void
builtin_sday(struct mh_fvm * mach)1068 builtin_sday (struct mh_fvm *mach)
1069 {
1070   int flags;
1071   _parse_date (mach, NULL, NULL, &flags);
1072   mach->num[R_REG] = !!(flags & MU_PD_MASK_DOW); /* FIXME: how about unknown? */
1073 }
1074 
1075 /*     mday       date     integer  day of the month*/
1076 static void
builtin_mday(struct mh_fvm * mach)1077 builtin_mday (struct mh_fvm *mach)
1078 {
1079   struct tm tm;
1080 
1081   _parse_date (mach, &tm, NULL, NULL);
1082 
1083   mach->num[R_REG] = tm.tm_mday;
1084 }
1085 
1086 /*      yday       date     integer  day of the year */
1087 static void
builtin_yday(struct mh_fvm * mach)1088 builtin_yday (struct mh_fvm *mach)
1089 {
1090   struct tm tm;
1091 
1092   _parse_date (mach, &tm, NULL, NULL);
1093 
1094   mach->num[R_REG] = tm.tm_yday;
1095 }
1096 
1097 /*     mon        date     integer  month of the year*/
1098 static void
builtin_mon(struct mh_fvm * mach)1099 builtin_mon (struct mh_fvm *mach)
1100 {
1101   struct tm tm;
1102 
1103   _parse_date (mach, &tm, NULL, NULL);
1104 
1105   mach->num[R_REG] = tm.tm_mon + 1;
1106 }
1107 
1108 /*     month      date     string   month of the year (abbrev.) */
1109 static void
builtin_month(struct mh_fvm * mach)1110 builtin_month (struct mh_fvm *mach)
1111 {
1112   struct tm tm;
1113   char buf[80];
1114 
1115   _parse_date (mach, &tm, NULL, NULL);
1116 
1117   strftime (buf, sizeof buf, "%b", &tm);
1118   mh_string_load (&mach->str[R_REG], buf);
1119 }
1120 
1121 /*      lmonth     date     string   month of the year*/
1122 static void
builtin_lmonth(struct mh_fvm * mach)1123 builtin_lmonth (struct mh_fvm *mach)
1124 {
1125   struct tm tm;
1126   char buf[80];
1127 
1128   _parse_date (mach, &tm, NULL, NULL);
1129 
1130   strftime (buf, sizeof buf, "%B", &tm);
1131   mh_string_load (&mach->str[R_REG], buf);
1132 }
1133 
1134 /*     year       date     integer  year (may be > 100)*/
1135 static void
builtin_year(struct mh_fvm * mach)1136 builtin_year (struct mh_fvm *mach)
1137 {
1138   struct tm tm;
1139 
1140   _parse_date (mach, &tm, NULL, NULL);
1141 
1142   mach->num[R_REG] = tm.tm_year + 1900;
1143 }
1144 
1145 /*     zone       date     integer  timezone in hours
1146    FIXME: mh and nmh return the value as given, e.g. 0300 is returned as 300 */
1147 static void
builtin_zone(struct mh_fvm * mach)1148 builtin_zone (struct mh_fvm *mach)
1149 {
1150   struct mu_timezone tz;
1151 
1152   _parse_date (mach, NULL, &tz, NULL);
1153 
1154   mach->num[R_REG] = tz.utc_offset / 3600;
1155 }
1156 
1157 /*     tzone      date     string   timezone string */
1158 static void
builtin_tzone(struct mh_fvm * mach)1159 builtin_tzone (struct mh_fvm *mach)
1160 {
1161   struct mu_timezone tz;
1162   char buf[6];
1163   int s;
1164   unsigned hrs;
1165 
1166   _parse_date (mach, NULL, &tz, NULL);
1167 
1168 #if 0
1169   /* FIXME: If symbolic tz representation is needed, we'd do: */
1170   if (tz.tz_name)
1171     mh_string_load (&mach->str[R_REG], tz.tz_name);
1172   else
1173     /* .... */
1174   /* However, MH's tzone function simply formats the timezone */
1175 #endif
1176 
1177   if (tz.utc_offset < 0)
1178     {
1179       s = '-';
1180       tz.utc_offset = - tz.utc_offset;
1181     }
1182   else
1183     s = '+';
1184   hrs = tz.utc_offset / 3600;
1185   snprintf (buf, sizeof buf, "%c%02u%02u", s,
1186 	    hrs, (tz.utc_offset - hrs * 3600) / 60);
1187   mh_string_load (&mach->str[R_REG], buf);
1188 }
1189 
1190 /*      szone      date     integer  timezone explicit?
1191 	(0=implicit,-1=unknown) */
1192 static void
builtin_szone(struct mh_fvm * mach)1193 builtin_szone (struct mh_fvm *mach)
1194 {
1195   int flags;
1196 
1197   _parse_date (mach, NULL, NULL, &flags);
1198   mach->num[R_REG] = !!(flags & MU_PD_MASK_TZ);
1199 }
1200 
1201 static void
builtin_str_noop(struct mh_fvm * mach)1202 builtin_str_noop (struct mh_fvm *mach)
1203 {
1204   mh_string_copy (mach, R_REG, R_ARG);
1205 }
1206 
1207 /*     date2local date              coerce date to local timezone*/
1208 static void
builtin_date2local(struct mh_fvm * mach)1209 builtin_date2local (struct mh_fvm *mach)
1210 {
1211   /*FIXME: Noop*/
1212   builtin_str_noop (mach);
1213 }
1214 
1215 /*     date2gmt   date              coerce date to GMT*/
1216 static void
builtin_date2gmt(struct mh_fvm * mach)1217 builtin_date2gmt (struct mh_fvm *mach)
1218 {
1219   /*FIXME: Noop*/
1220   builtin_str_noop (mach);
1221 }
1222 
1223 /*     dst        date     integer  daylight savings in effect?*/
1224 static void
builtin_dst(struct mh_fvm * mach)1225 builtin_dst (struct mh_fvm *mach)
1226 {
1227 #ifdef HAVE_STRUCT_TM_TM_ISDST
1228   struct tm tm;
1229 
1230   _parse_date (mach, &tm, NULL, NULL);
1231 
1232   mach->num[R_REG] = tm.tm_isdst;
1233 #else
1234   mach->num[R_REG] = 0;
1235 #endif
1236 }
1237 
1238 /*     clock      date     integer  seconds since the UNIX epoch*/
1239 static void
builtin_clock(struct mh_fvm * mach)1240 builtin_clock (struct mh_fvm *mach)
1241 {
1242   struct tm tm;
1243   struct mu_timezone tz;
1244 
1245   _parse_date (mach, &tm, &tz, NULL);
1246 
1247   mach->num[R_REG] = mu_datetime_to_utc (&tm, &tz);
1248 }
1249 
1250 /*     rclock     date     integer  seconds prior to current time*/
1251 void
builtin_rclock(struct mh_fvm * mach)1252 builtin_rclock (struct mh_fvm *mach)
1253 {
1254   struct tm tm;
1255   struct mu_timezone tz;
1256   time_t now = time (NULL);
1257 
1258   _parse_date (mach, &tm, &tz, NULL);
1259 
1260   mach->num[R_REG] = now - mu_datetime_to_utc (&tm, &tz);
1261 }
1262 
1263 struct
1264 {
1265   const char *std;
1266   const char *dst;
1267   int utc_offset;     /* offset from GMT (hours) */
1268 } tzs[] = {
1269   { "GMT", "BST", 0 },
1270   { "EST", "EDT", -5 },
1271   { "CST", "CDT", -6 },
1272   { "MST", "MDT", -7 },
1273   { "PST", "PDT", -8 },
1274   { "EET", "EEST", -2 },
1275   { NULL, 0}
1276 };
1277 
1278 static void
date_cvt(struct mh_fvm * mach,int pretty)1279 date_cvt (struct mh_fvm *mach, int pretty)
1280 {
1281   struct tm tm;
1282   struct mu_timezone tz;
1283   char buf[80];
1284   int i, len;
1285   const char *tzname = NULL;
1286 
1287   _parse_date (mach, &tm, &tz, NULL);
1288 
1289   if (pretty)
1290     {
1291       for (i = 0; tzs[i].std; i++)
1292 	{
1293 	  int offset = tzs[i].utc_offset;
1294 	  int dst = 0;
1295 
1296 #ifdef HAVE_STRUCT_TM_TM_ISDST
1297 	  if (tm.tm_isdst)
1298 	    dst = -1;
1299 #endif
1300 
1301 	  if (tz.utc_offset == (offset + dst) * 3600)
1302 	    {
1303 	      if (dst)
1304 		tzname = tzs[i].dst;
1305 	      else
1306 		tzname = tzs[i].std;
1307 	      break;
1308 	    }
1309 	}
1310     }
1311 
1312   len = strftime (buf, sizeof buf,
1313 		  "%a, %d %b %Y %H:%M:%S ", &tm);
1314   if (tzname)
1315     snprintf (buf + len, sizeof(buf) - len, "%s", tzname);
1316   else
1317     {
1318       int min, hrs, sign;
1319       int offset = tz.utc_offset;
1320 
1321       if (offset < 0)
1322 	{
1323 	  sign = '-';
1324 	  offset = - offset;
1325 	}
1326       else
1327 	sign = '+';
1328       min = offset / 60;
1329       hrs = min / 60;
1330       min %= 60;
1331       snprintf (buf + len, sizeof(buf) - len, "%c%02d%02d", sign, hrs, min);
1332     }
1333   mh_string_load (&mach->str[R_REG], buf);
1334 }
1335 
1336 /*      tws        date     string   official 822 rendering */
1337 static void
builtin_tws(struct mh_fvm * mach)1338 builtin_tws (struct mh_fvm *mach)
1339 {
1340   date_cvt (mach, 0);
1341 }
1342 
1343 /*     pretty     date     string   user-friendly rendering*/
1344 static void
builtin_pretty(struct mh_fvm * mach)1345 builtin_pretty (struct mh_fvm *mach)
1346 {
1347   date_cvt (mach, 1);
1348 }
1349 
1350 /*     nodate     date     integer  str not a date string */
1351 static void
builtin_nodate(struct mh_fvm * mach)1352 builtin_nodate (struct mh_fvm *mach)
1353 {
1354   char const *date = mh_string_value (&mach->str[R_ARG]);
1355 
1356   mach->num[R_REG] =
1357     mu_parse_date_dtl (date, NULL, NULL, NULL, NULL, NULL) != 0;
1358 }
1359 
1360 /*     proper     addr     string   official 822 rendering */
1361 static void
builtin_proper(struct mh_fvm * mach)1362 builtin_proper (struct mh_fvm *mach)
1363 {
1364   int rc;
1365   char const *str;
1366   mu_address_t addr;
1367 
1368   rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1369   if (rc)
1370     {
1371       mh_string_copy (mach, R_REG, R_ARG);
1372       return;
1373     }
1374   if (mu_address_sget_printable (addr, &str) == 0 && str)
1375     mh_string_load (&mach->str[R_REG], str);
1376   else
1377     mh_string_copy (mach, R_REG, R_ARG);
1378   mu_address_destroy (&addr);
1379 }
1380 
1381 /*     friendly   addr     string   user-friendly rendering*/
1382 static void
builtin_friendly(struct mh_fvm * mach)1383 builtin_friendly (struct mh_fvm *mach)
1384 {
1385   mu_address_t addr;
1386   const char *str;
1387   int rc;
1388 
1389   rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1390   if (rc)
1391     return;
1392 
1393   if (mu_address_sget_personal (addr, 1, &str) == 0 && str)
1394     mh_string_load (&mach->str[R_REG], str);
1395   else
1396     mh_string_copy (mach, R_REG, R_ARG);
1397 
1398   mu_address_destroy (&addr);
1399 }
1400 
1401 /*     addr       addr     string   mbox@host or host!mbox rendering*/
1402 static void
builtin_addr(struct mh_fvm * mach)1403 builtin_addr (struct mh_fvm *mach)
1404 {
1405   const char *arg = mh_string_value (&mach->str[R_ARG]);
1406   mu_address_t addr;
1407   const char *str;
1408   int rc;
1409 
1410   rc = mu_address_create (&addr, arg);
1411   if (rc == 0)
1412     {
1413       int rc = mu_address_sget_email (addr, 1, &str);
1414       if (rc == 0)
1415 	mh_string_load (&mach->str[R_REG], mu_prstr (str));
1416       mu_address_destroy (&addr);
1417       if (rc == 0)
1418 	return;
1419     }
1420   mh_string_load (&mach->str[R_REG], arg);
1421 }
1422 
1423 /*     pers       addr     string   the personal name**/
1424 static void
builtin_pers(struct mh_fvm * mach)1425 builtin_pers (struct mh_fvm *mach)
1426 {
1427   char const *arg = mh_string_value (&mach->str[R_ARG]);
1428   mu_address_t addr;
1429   const char *str;
1430   int rc;
1431 
1432   rc = mu_address_create (&addr, arg);
1433   mh_string_clear (&mach->str[R_REG]);
1434   if (rc)
1435     return;
1436 
1437   if (mu_address_sget_personal (addr, 1, &str) == 0 && str)
1438     mh_string_load (&mach->str[R_REG], str);
1439   mu_address_destroy (&addr);
1440 }
1441 
1442 /* FIXME: mu_address_get_comments never returns any comments. */
1443 /*     note       addr     string   commentary text*/
1444 static void
builtin_note(struct mh_fvm * mach)1445 builtin_note (struct mh_fvm *mach)
1446 {
1447   mu_address_t addr;
1448   const char *str;
1449   int rc;
1450 
1451   rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1452   mh_string_clear (&mach->str[R_REG]);
1453   if (rc)
1454     return;
1455 
1456   if (mu_address_sget_comments (addr, 1, &str) == 0 && str)
1457     mh_string_load (&mach->str[R_REG], str);
1458   mu_address_destroy (&addr);
1459 }
1460 
1461 /*     mbox       addr     string   the local mailbox**/
1462 static void
builtin_mbox(struct mh_fvm * mach)1463 builtin_mbox (struct mh_fvm *mach)
1464 {
1465   mu_address_t addr;
1466   char *str;
1467   int rc;
1468 
1469   rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1470   mh_string_clear (&mach->str[R_REG]);
1471   if (rc)
1472     return;
1473 
1474   if (mu_address_aget_email (addr, 1, &str) == 0 && str)
1475     {
1476       char *p = strchr (str, '@');
1477       if (p)
1478 	*p = 0;
1479       mh_string_load (&mach->str[R_REG], str);
1480       free (str);
1481     }
1482   mu_address_destroy (&addr);
1483 }
1484 
1485 /*     mymbox     addr     integer  the user's addresses? (0=no,1=yes)*/
1486 static void
builtin_mymbox(struct mh_fvm * mach)1487 builtin_mymbox (struct mh_fvm *mach)
1488 {
1489   mu_address_t addr;
1490   const char *str;
1491 
1492   if (mu_address_create (&addr, mh_string_value (&mach->str[R_ARG])))
1493     return;
1494 
1495   if (mu_address_sget_email (addr, 1, &str) == 0 && str)
1496     mach->num[R_REG] = mh_is_my_name (str);
1497   else
1498     mach->num[R_REG] = 0;
1499   mu_address_destroy (&addr);
1500 }
1501 
1502 /*     host       addr     string   the host domain**/
1503 static void
builtin_host(struct mh_fvm * mach)1504 builtin_host (struct mh_fvm *mach)
1505 {
1506   mu_address_t addr;
1507   char *str;
1508   int rc;
1509 
1510   rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1511   mh_string_clear (&mach->str[R_REG]);
1512   if (rc)
1513     return;
1514 
1515   if (mu_address_aget_email (addr, 1, &str) == 0 && str)
1516     {
1517       char *p = strchr (str, '@');
1518       if (p)
1519 	mh_string_load (&mach->str[R_REG], p + 1);
1520       free (str);
1521     }
1522   mu_address_destroy (&addr);
1523 }
1524 
1525 /*     nohost     addr     integer  no host was present**/
1526 static void
builtin_nohost(struct mh_fvm * mach)1527 builtin_nohost (struct mh_fvm *mach)
1528 {
1529   struct mu_address hint;
1530   mu_address_t addr;
1531   const char *dom;
1532   int rc;
1533 
1534   hint.domain = NULL;
1535   rc = mu_address_create_hint (&addr, mh_string_value (&mach->str[R_ARG]),
1536 			       &hint, MU_ADDR_HINT_DOMAIN);
1537   mh_string_clear (&mach->str[R_REG]);
1538   if (rc)
1539     mach->num[R_REG] = 1;
1540   else
1541     {
1542       mach->num[R_REG] = !(mu_address_sget_domain (addr, 1, &dom) == 0 && dom);
1543       mu_address_destroy (&addr);
1544     }
1545 }
1546 
1547 /*     type       addr     integer  host type* (0=local,1=network,
1548        -1=uucp,2=unknown)*/
1549 static void
builtin_type(struct mh_fvm * mach)1550 builtin_type (struct mh_fvm *mach)
1551 {
1552   mu_address_t addr;
1553   int rc;
1554   const char *str;
1555 
1556   rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1557   mh_string_clear (&mach->str[R_REG]);
1558   if (rc)
1559     return;
1560 
1561   if (mu_address_sget_email (addr, 1, &str) == 0 && str)
1562     {
1563       if (strchr (str, '@'))
1564 	mach->num[R_REG] = 1;
1565       else if (strchr (str, '!'))
1566 	mach->num[R_REG] = -1;
1567       else
1568 	mach->num[R_REG] = 0; /* assume local */
1569     }
1570   else
1571     mach->num[R_REG] = 2;
1572   mu_address_destroy (&addr);
1573 }
1574 
1575 /*     path       addr     string   any leading host route**/
1576 static void
builtin_path(struct mh_fvm * mach)1577 builtin_path (struct mh_fvm *mach)
1578 {
1579   mu_address_t addr;
1580   const char *str;
1581   int rc = mu_address_create (&addr, mh_string_value (&mach->str[R_ARG]));
1582   mh_string_clear (&mach->str[R_REG]);
1583   if (rc)
1584     return;
1585   if (mu_address_sget_route (addr, 1, &str) && str)
1586     mh_string_load (&mach->str[R_REG], str);
1587   mu_address_destroy (&addr);
1588 }
1589 
1590 /*     ingrp      addr     integer  address was inside a group**/
1591 static void
builtin_ingrp(struct mh_fvm * mach)1592 builtin_ingrp (struct mh_fvm *mach)
1593 {
1594   /*FIXME:*/
1595   builtin_not_implemented ("ingrp");
1596   mach->num[R_REG] = 0;
1597 }
1598 
1599 /*     gname      addr     string   name of group**/
1600 static void
builtin_gname(struct mh_fvm * mach)1601 builtin_gname (struct mh_fvm *mach)
1602 {
1603   /*FIXME:*/
1604   builtin_not_implemented ("gname");
1605   builtin_str_noop (mach);
1606 }
1607 
1608 /*     formataddr expr              append arg to str as a
1609        (comma separated) address list */
1610 static void
builtin_formataddr(struct mh_fvm * mach)1611 builtin_formataddr (struct mh_fvm *mach)
1612 {
1613   mu_address_t addr, dest;
1614   int i;
1615   size_t num;
1616   const char *buf;
1617 
1618   if (mh_string_is_null (&mach->str[R_ACC]))
1619     dest = NULL;
1620   else if (mu_address_create (&dest, mh_string_value (&mach->str[R_ACC])))
1621     return;
1622 
1623   if (!mh_string_is_null (&mach->str[R_ARG])
1624       && mu_address_create (&addr, mh_string_value (&mach->str[R_ARG])) == 0)
1625     {
1626       mu_address_get_count (addr, &num);
1627       for (i = 1; i <= num; i++)
1628 	{
1629 	  if (mu_address_sget_email (addr, i, &buf) == 0 && buf)
1630 	    {
1631 	      if ((rcpt_mask & RCPT_ME) || !mh_is_my_name (buf))
1632 		{
1633 		  mu_address_t subaddr;
1634 		  mu_address_get_nth (addr, i, &subaddr);
1635 		  if (!addrlist_lookup (mach->addrlist, subaddr))
1636 		    {
1637 		      mu_list_append (mach->addrlist, subaddr);
1638 		      mu_address_union (&dest, subaddr);
1639 		    }
1640 		  else
1641 		    mu_address_destroy (&subaddr);
1642 		}
1643 	    }
1644 	}
1645     }
1646 
1647   if (mu_address_sget_printable (dest, &buf) == 0 && buf)
1648     mh_string_load (&mach->str[R_REG], buf);
1649   else
1650     mh_string_clear (&mach->str[R_REG]);
1651   mu_address_destroy (&dest);
1652 }
1653 
1654 /*      putaddr    literal        print str address list with
1655                                   arg as optional label;
1656                                   get line width from num
1657    FIXME: Currently it's the same as puthdr. Possibly it should do
1658    some address-checking as well.
1659 */
1660 static void
builtin_putaddr(struct mh_fvm * mach)1661 builtin_putaddr (struct mh_fvm *mach)
1662 {
1663   if (!mh_string_is_null (&mach->str[R_ARG]))
1664     print_hdr_string (mach, mh_string_value (&mach->str[R_ARG]));
1665   if (!mh_string_is_null (&mach->str[R_REG]))
1666     print_hdr_string (mach, mh_string_value (&mach->str[R_REG]));
1667 }
1668 
1669 /* GNU extension: Strip leading whitespace and eventual Re: (or Re\[[0-9]+\]:)
1670    prefix from the argument */
1671 static void
builtin_unre(struct mh_fvm * mach)1672 builtin_unre (struct mh_fvm *mach)
1673 {
1674   char const *arg = mh_string_value (&mach->str[R_ARG]);
1675   char const *p;
1676   int rc = mu_unre_subject (arg, &p);
1677 
1678   if (rc == 0 && p != arg)
1679     {
1680       char *q = mu_strdup (p); /* Create a copy, since mh_string_load can
1681 			          destroy p */
1682       mh_string_load (&mach->str[R_REG], q);
1683       free (q);
1684     }
1685   else
1686     mh_string_load (&mach->str[R_REG], arg);
1687 }
1688 
1689 static void
builtin_isreply(struct mh_fvm * mach)1690 builtin_isreply (struct mh_fvm *mach)
1691 {
1692   int rc;
1693 
1694   if (mh_string_is_null (&mach->str[R_ARG]))
1695     {
1696       mu_header_t hdr = NULL;
1697       char *value = NULL;
1698       mu_message_get_header (mach->message, &hdr);
1699 
1700       mu_header_aget_value (hdr, MU_HEADER_SUBJECT, &value);
1701       rc = mu_unre_subject (value, NULL);
1702       free (value);
1703     }
1704   else
1705     rc = mu_unre_subject (mh_string_value (&mach->str[R_ARG]), NULL);
1706 
1707   mach->num[R_REG] = !rc;
1708 }
1709 
1710 static void
builtin_decode(struct mh_fvm * mach)1711 builtin_decode (struct mh_fvm *mach)
1712 {
1713   char *tmp;
1714 
1715   if (mh_string_is_null (&mach->str[R_ARG]))
1716     return;
1717 
1718   if (mh_decode_2047 (mh_string_value (&mach->str[R_ARG]), &tmp) == 0)
1719     {
1720       mh_string_load (&mach->str[R_REG], tmp);
1721       free (tmp);
1722     }
1723 }
1724 
1725 static void
builtin_reply_regex(struct mh_fvm * mach)1726 builtin_reply_regex (struct mh_fvm *mach)
1727 {
1728   mh_set_reply_regex (mh_string_value (&mach->str[R_ARG]));
1729 }
1730 
1731 int
mh_decode_rcpt_flag(const char * arg)1732 mh_decode_rcpt_flag (const char *arg)
1733 {
1734   if (strcmp (arg, "to") == 0)
1735     return RCPT_TO;
1736   else if (strcmp (arg, "cc") == 0)
1737     return RCPT_CC;
1738   else if (strcmp (arg, "me") == 0)
1739     return RCPT_ME;
1740   else if (strcmp (arg, "all") == 0)
1741     return RCPT_ALL;
1742 
1743   return RCPT_NONE;
1744 }
1745 
1746 static void
builtin_rcpt(struct mh_fvm * mach)1747 builtin_rcpt (struct mh_fvm *mach)
1748 {
1749   int rc = mh_decode_rcpt_flag (mh_string_value (&mach->str[R_ARG]));
1750   if (rc == RCPT_NONE)
1751     {
1752       mu_error (_("invalid recipient mask"));
1753       /* try to continue anyway */
1754     }
1755   mach->num[R_REG] = !!(rc & rcpt_mask);
1756 }
1757 
1758 static void
builtin_printhdr(struct mh_fvm * mach)1759 builtin_printhdr (struct mh_fvm *mach)
1760 {
1761   char *tmp = NULL;
1762   size_t s = 0;
1763 
1764   if (!mh_string_is_null (&mach->str[R_ARG]))
1765     {
1766       s = mh_string_length (&mach->str[R_ARG]);
1767       tmp = mu_strdup (mh_string_value (&mach->str[R_ARG]));
1768     }
1769 
1770   if (!mh_string_is_null (&mach->str[R_REG]))
1771     {
1772       s += mh_string_length (&mach->str[R_REG]) + 1;
1773       tmp = mu_realloc (tmp, s);
1774       strcat (tmp, mh_string_value (&mach->str[R_REG]));
1775     }
1776 
1777   if (tmp)
1778     {
1779       print_hdr_string (mach, tmp);
1780       free (tmp);
1781     }
1782 }
1783 
1784 static void
builtin_in_reply_to(struct mh_fvm * mach)1785 builtin_in_reply_to (struct mh_fvm *mach)
1786 {
1787   char *value;
1788 
1789   mh_string_clear (&mach->str[R_REG]);
1790   if (mu_rfc2822_in_reply_to (mach->message, &value) == 0)
1791     {
1792       mh_string_load (&mach->str[R_REG], value);
1793       free (value);
1794     }
1795 }
1796 
1797 static void
builtin_references(struct mh_fvm * mach)1798 builtin_references (struct mh_fvm *mach)
1799 {
1800   char *value;
1801 
1802   mh_string_clear (&mach->str[R_REG]);
1803   if (mu_rfc2822_references (mach->message, &value) == 0)
1804     {
1805       mh_string_load (&mach->str[R_REG], value);
1806       free (value);
1807     }
1808 }
1809 
1810 static void
builtin_package(struct mh_fvm * mach)1811 builtin_package (struct mh_fvm *mach)
1812 {
1813   mh_string_load (&mach->str[R_REG], PACKAGE);
1814 }
1815 
1816 static void
builtin_package_string(struct mh_fvm * mach)1817 builtin_package_string (struct mh_fvm *mach)
1818 {
1819   mh_string_load (&mach->str[R_REG], PACKAGE_STRING);
1820 }
1821 
1822 static void
builtin_version(struct mh_fvm * mach)1823 builtin_version (struct mh_fvm *mach)
1824 {
1825   mh_string_load (&mach->str[R_REG], VERSION);
1826 }
1827 
1828 /* Builtin function table */
1829 
1830 mh_builtin_t builtin_tab[] = {
1831   /* Name       Handling function Return type  Arg type      Flags */
1832   { "msg",      builtin_msg,      mhtype_num,  mhtype_none },
1833   { "cur",      builtin_cur,      mhtype_num,  mhtype_none },
1834   { "size",     builtin_size,     mhtype_num,  mhtype_none },
1835   { "strlen",   builtin_strlen,   mhtype_num,  mhtype_none },
1836   { "width",    builtin_width,    mhtype_num,  mhtype_none },
1837   { "charleft", builtin_charleft, mhtype_num,  mhtype_none },
1838   { "timenow",  builtin_timenow,  mhtype_num,  mhtype_none },
1839   { "me",       builtin_me,       mhtype_str,  mhtype_none },
1840   { "myhost",   builtin_myhost,   mhtype_str,  mhtype_none },
1841   { "myname",   builtin_myname,   mhtype_str,  mhtype_none },
1842   { "localmbox",builtin_localmbox,mhtype_str,  mhtype_none },
1843   { "eq",       builtin_eq,       mhtype_num,  mhtype_num,  MHA_LITERAL },
1844   { "ne",       builtin_ne,       mhtype_num,  mhtype_num,  MHA_LITERAL },
1845   { "gt",       builtin_gt,       mhtype_num,  mhtype_num,  MHA_LITERAL },
1846   { "match",    builtin_match,    mhtype_num,  mhtype_str,  MHA_LITERAL },
1847   { "amatch",   builtin_amatch,   mhtype_num,  mhtype_str,  MHA_LITERAL },
1848   { "plus",     builtin_plus,     mhtype_num,  mhtype_num,  MHA_LITERAL },
1849   { "minus",    builtin_minus,    mhtype_num,  mhtype_num,  MHA_LITERAL },
1850   { "divide",   builtin_divide,   mhtype_num,  mhtype_num,  MHA_LITERAL },
1851   { "modulo",   builtin_modulo,   mhtype_num,  mhtype_num,  MHA_LITERAL },
1852   { "num",      NULL,             mhtype_num,  mhtype_num,  MHA_LITERAL|MHA_OPTARG|MHA_OPTARG_NIL|MHA_SPECIAL },
1853   { "lit",      NULL,             mhtype_str,  mhtype_str,  MHA_LITERAL|MHA_OPTARG|MHA_OPTARG_NIL|MHA_SPECIAL },
1854   { "getenv",   builtin_getenv,   mhtype_str,  mhtype_str,  MHA_LITERAL },
1855   { "profile",  builtin_profile,  mhtype_str,  mhtype_str,  MHA_LITERAL },
1856   { "nonzero",  builtin_nonzero,  mhtype_num,  mhtype_num,  MHA_OPTARG },
1857   { "zero",     builtin_zero,     mhtype_num,  mhtype_num,  MHA_OPTARG },
1858   { "null",     builtin_null,     mhtype_num,  mhtype_str,  MHA_OPTARG },
1859   { "nonnull",  builtin_nonnull,  mhtype_num,  mhtype_str,  MHA_OPTARG },
1860   { "comp",     builtin_comp,     mhtype_str,  mhtype_str },
1861   { "compval",  builtin_compval,  mhtype_num,  mhtype_str },
1862   { "trim",     builtin_trim,     mhtype_none, mhtype_str, MHA_OPTARG },
1863   { "putstr",   NULL,             mhtype_str,  mhtype_str, MHA_SPECIAL|MHA_OPTARG|MHA_OPTARG|MHA_IGNOREFMT },
1864   { "putstrf",  NULL,             mhtype_str,  mhtype_str, MHA_SPECIAL|MHA_OPTARG },
1865   { "putnum",   NULL,             mhtype_num,  mhtype_num, MHA_SPECIAL|MHA_OPTARG|MHA_IGNOREFMT },
1866   { "putnumf",  NULL,             mhtype_num,  mhtype_num, MHA_SPECIAL|MHA_OPTARG },
1867   { "sec",      builtin_sec,      mhtype_num,  mhtype_str },
1868   { "min",      builtin_min,      mhtype_num,  mhtype_str },
1869   { "hour",     builtin_hour,     mhtype_num,  mhtype_str },
1870   { "wday",     builtin_wday,     mhtype_num,  mhtype_str },
1871   { "day",      builtin_day,      mhtype_str,  mhtype_str },
1872   { "weekday",  builtin_weekday,  mhtype_str,  mhtype_str },
1873   { "sday",     builtin_sday,     mhtype_num,  mhtype_str },
1874   { "mday",     builtin_mday,     mhtype_num,  mhtype_str },
1875   { "yday",     builtin_yday,     mhtype_num,  mhtype_str },
1876   { "mon",      builtin_mon,      mhtype_num,  mhtype_str },
1877   { "month",    builtin_month,    mhtype_str,  mhtype_str },
1878   { "lmonth",   builtin_lmonth,   mhtype_str,  mhtype_str },
1879   { "year",     builtin_year,     mhtype_num,  mhtype_str },
1880   { "zone",     builtin_zone,     mhtype_num,  mhtype_str },
1881   { "tzone",    builtin_tzone,    mhtype_str,  mhtype_str },
1882   { "szone",    builtin_szone,    mhtype_num,  mhtype_str },
1883   { "date2local", builtin_date2local, mhtype_none, mhtype_str },
1884   { "date2gmt", builtin_date2gmt, mhtype_none,  mhtype_str },
1885   { "dst",      builtin_dst,      mhtype_num,  mhtype_str },
1886   { "clock",    builtin_clock,    mhtype_num,  mhtype_str },
1887   { "rclock",   builtin_rclock,   mhtype_num,  mhtype_str },
1888   { "tws",      builtin_tws,      mhtype_str,  mhtype_str },
1889   { "pretty",   builtin_pretty,   mhtype_str,  mhtype_str },
1890   { "nodate",   builtin_nodate,   mhtype_num,  mhtype_str },
1891   { "proper",   builtin_proper,   mhtype_str,  mhtype_str },
1892   { "friendly", builtin_friendly, mhtype_str,  mhtype_str },
1893   { "addr",     builtin_addr,     mhtype_str,  mhtype_str },
1894   { "pers",     builtin_pers,     mhtype_str,  mhtype_str },
1895   { "note",     builtin_note,     mhtype_str,  mhtype_str },
1896   { "mbox",     builtin_mbox,     mhtype_str,  mhtype_str },
1897   { "mymbox",   builtin_mymbox,   mhtype_num,  mhtype_str },
1898   { "host",     builtin_host,     mhtype_str,  mhtype_str },
1899   { "nohost",   builtin_nohost,   mhtype_num,  mhtype_str },
1900   { "type",     builtin_type,     mhtype_num,  mhtype_str },
1901   { "path",     builtin_path,     mhtype_str,  mhtype_str },
1902   { "ingrp",    builtin_ingrp,    mhtype_num,  mhtype_str },
1903   { "gname",    builtin_gname,    mhtype_str,  mhtype_str},
1904   { "formataddr", builtin_formataddr, mhtype_none, mhtype_str, MHA_ACC },
1905   { "putaddr",  builtin_putaddr,  mhtype_none, mhtype_str, MHA_LITERAL },
1906   { "unre",     builtin_unre,     mhtype_str,  mhtype_str },
1907   { "rcpt",     builtin_rcpt,     mhtype_num,  mhtype_str, MHA_LITERAL },
1908   { "printhdr", builtin_printhdr, mhtype_none, mhtype_str, MHA_LITERAL },
1909   { "in_reply_to", builtin_in_reply_to, mhtype_str,  mhtype_none },
1910   { "references", builtin_references, mhtype_str,  mhtype_none },
1911   { "package",  builtin_package,  mhtype_str, mhtype_none },
1912   { "package_string",  builtin_package_string,  mhtype_str, mhtype_none },
1913   { "version",  builtin_version,  mhtype_str, mhtype_none },
1914   { "reply_regex", builtin_reply_regex, mhtype_none, mhtype_str },
1915   { "isreply", builtin_isreply, mhtype_num, mhtype_str, MHA_OPTARG },
1916   { "decode", builtin_decode, mhtype_str, mhtype_str },
1917   { "void",   NULL, mhtype_none, mhtype_str, MHA_VOID },
1918   { 0 }
1919 };
1920 
1921 mh_builtin_t *
mh_lookup_builtin(char * name,size_t len)1922 mh_lookup_builtin (char *name, size_t len)
1923 {
1924   mh_builtin_t *bp;
1925 
1926   for (bp = builtin_tab; bp->name; bp++)
1927     {
1928       if (strlen (bp->name) == len && memcmp (name, bp->name, len) == 0)
1929 	return bp;
1930     }
1931   return NULL;
1932 }
1933 
1934 char *
_get_builtin_name(mh_builtin_fp ptr)1935 _get_builtin_name (mh_builtin_fp ptr)
1936 {
1937   mh_builtin_t *bp;
1938 
1939   for (bp = builtin_tab; bp->name; bp++)
1940     if (bp->fun == ptr)
1941       return bp->name;
1942   return NULL;
1943 }
1944 
1945 /* Label array is used when disassembling the code, in order to create.
1946    meaningful label names.  The array elements starting from index 1 keep
1947    the code addesses to which branch instructions point, in ascending order.
1948    Element 0 keeps the number of addresses stored.  Thus, the label
1949    array LAB with contents { 3, 2, 5, 7 } declares three labels: "L1" on
1950    address 2, "L2", on address 5, and "L3" on address 7.
1951 */
1952 
1953 /* Find in LAB the index of the label corresponding to the given PC.  Return
1954    0 if no label found. */
1955 size_t
find_label(size_t * lab,size_t pc)1956 find_label (size_t *lab, size_t pc)
1957 {
1958   if (lab)
1959     {
1960       size_t i;
1961       for (i = 1; i <= lab[0]; i++)
1962 	{
1963 	  if (lab[i] == pc)
1964 	    return i;
1965 	}
1966     }
1967   return 0;
1968 }
1969 
1970 static int
comp_pc(const void * a,const void * b)1971 comp_pc (const void *a, const void *b)
1972 {
1973   size_t pca = *(size_t*)a;
1974   size_t pcb = *(size_t*)b;
1975   if (pca < pcb)
1976     return -1;
1977   else if (pca > pcb)
1978     return 1;
1979   return 0;
1980 }
1981 
1982 /* Extract a label array from a compiled format FMT. */
1983 static size_t *
extract_labels(mh_format_t fmt)1984 extract_labels (mh_format_t fmt)
1985 {
1986   size_t *lab;
1987   size_t pc;
1988   long n;
1989 
1990   lab = mu_calloc (fmt->progcnt, sizeof (lab[0]));
1991   lab[0] = 0;
1992   for (pc = 1; pc < fmt->progcnt; )
1993     {
1994       mh_opcode_t opcode = MHI_OPCODE (fmt->prog[pc++]);
1995       if (opcode == mhop_stop)
1996 	break;
1997       switch (opcode)
1998 	{
1999 	case mhop_branch:
2000 	case mhop_brzn:
2001 	case mhop_brzs:
2002 	  n = MHI_NUM (fmt->prog[pc++]);
2003 	  if (!find_label (lab, pc + n - 1))
2004 	    lab[++lab[0]] = pc + n - 1;
2005 	  break;
2006 
2007 	case mhop_setn:
2008 	  pc += 2;
2009 	  break;
2010 
2011 	case mhop_sets:
2012 	case mhop_ldcomp:
2013 	  pc += 2 + MHI_NUM (fmt->prog[pc + 1]);
2014 	  break;
2015 
2016 	case mhop_movn:
2017 	case mhop_movs:
2018 	  pc += 2;
2019 	  break;
2020 
2021 	case mhop_ldbody:
2022 	case mhop_call:
2023 	case mhop_fmtspec:
2024 	  pc++;
2025 	  break;
2026 
2027  	case mhop_printlit:
2028 	  pc += 1 + MHI_NUM (fmt->prog[pc]);
2029 	  break;
2030 
2031 	case mhop_atoi:
2032 	case mhop_itoa:
2033 	case mhop_printn:
2034 	case mhop_prints:
2035 	case mhop_pushn:
2036 	case mhop_popn:
2037 	case mhop_xchgn:
2038 	  break;
2039 
2040 	default:
2041 	  abort ();
2042 	}
2043     }
2044   if (lab[0] > 0)
2045     qsort (lab + 1, lab[0], sizeof lab[0], comp_pc);
2046   return lab;
2047 }
2048 
2049 /* Print to *PBUF (of size *PSZ) the label corresponding to the address PC.
2050    If there's no label having this address (in particular, if LAB==NULL),
2051    format the address itself to *PBUF.
2052    Reallocate *PBUF, updating *PSZ, if necessary.
2053 */
2054 void
format_label(size_t * lab,size_t pc,char ** pbuf,size_t * psz)2055 format_label (size_t *lab, size_t pc, char **pbuf, size_t *psz)
2056 {
2057   size_t ln = find_label (lab, pc);
2058   if (ln)
2059     mu_asnprintf (pbuf, psz, "L%ld", (long) ln);
2060   else
2061     mu_asnprintf (pbuf, psz, "%ld", (long) pc);
2062 }
2063 
2064 /* Dump disassembled code of FMT to stdout.  If ADDR is 0, print label names
2065    where necessary, otherwise, prefix each line of output with its program
2066    counter in decimal.
2067 */
2068 void
mh_format_dump_disass(mh_format_t fmt,int addr)2069 mh_format_dump_disass (mh_format_t fmt, int addr)
2070 {
2071   mh_instr_t *prog = fmt->prog;
2072   size_t pc = 1;
2073   int stop = 0;
2074   static char *regname[] = {
2075     [R_REG] = "reg",
2076     [R_ARG] = "arg",
2077     [R_ACC] = "acc"
2078   };
2079   static char c_trans[] = "\\\\\"\"a\ab\bf\fn\nr\rt\tv\v";
2080   size_t *lab;
2081   size_t lc;
2082   char *lbuf = NULL;
2083   size_t lsz = 0;
2084 
2085   if (!prog)
2086     return;
2087 
2088   if (!addr)
2089     lab = extract_labels (fmt);
2090   else
2091     lab = NULL;
2092   lc = lab ? 1 : 0;
2093 
2094   while (!stop)
2095     {
2096       mh_opcode_t opcode;
2097 
2098       if (addr)
2099 	printf ("% 4.4ld: ", (long) pc);
2100       else
2101 	{
2102 	  int w = 0;
2103 	  if (lc <= lab[0] && lab[lc] == pc)
2104 	    {
2105 	      w = printf ("L%ld:", (long) lc);
2106 	      lc++;
2107 	    }
2108 	  if (w > 8)
2109 	    {
2110 	      putchar ('\n');
2111 	      w = 0;
2112 	    }
2113 	  while (w < 8)
2114 	    {
2115 	      putchar (' ');
2116 	      w++;
2117 	    }
2118 	}
2119 
2120       switch (opcode = MHI_OPCODE (prog[pc++]))
2121 	{
2122 	case mhop_stop:
2123 	  printf ("stop");
2124 	  stop = 1;
2125 	  break;
2126 
2127 	case mhop_branch:
2128 	  {
2129 	    long n =  MHI_NUM (prog[pc++]);
2130 	    format_label (lab, pc + n - 1, &lbuf, &lsz);
2131 	    printf ("branch %s", lbuf);
2132 	  }
2133 	  break;
2134 
2135 	case mhop_brzn:
2136 	  {
2137 	    long n =  MHI_NUM (prog[pc++]);
2138 	    format_label (lab, pc + n - 1, &lbuf, &lsz);
2139 	    printf ("brzn %s", lbuf);
2140 	  }
2141 	  break;
2142 
2143 	case mhop_brzs:
2144 	  {
2145 	    long n =  MHI_NUM (prog[pc++]);
2146 	    format_label (lab, pc + n - 1, &lbuf, &lsz);
2147 	    printf ("brzs %s", lbuf);
2148 	  }
2149 	  break;
2150 
2151 	case mhop_setn:
2152 	  {
2153 	    long reg = MHI_NUM (prog[pc++]);
2154 	    long n = MHI_NUM (prog[pc++]);
2155 	    printf ("setn %s, %ld", regname[reg], n);
2156 	  }
2157 	  break;
2158 
2159 	case mhop_sets:
2160 	  {
2161 	    long reg = MHI_NUM (prog[pc++]);
2162 	    size_t skip = MHI_NUM (prog[pc++]);
2163 	    char const *str = MHI_STR (prog[pc]);
2164 	    char *prt;
2165 
2166 	    MU_ASSERT (mu_c_str_escape_trans (str, c_trans, &prt));
2167 
2168 	    pc += skip;
2169 	    printf ("sets %s, \"%s\"", regname[reg], prt);
2170 	    free (prt);
2171 	  }
2172 	  break;
2173 
2174 	case mhop_movn:
2175 	  {
2176 	    long dst = MHI_NUM (prog[pc++]);
2177 	    long src = MHI_NUM (prog[pc++]);
2178 	    printf ("movn %s, %s", regname[dst], regname[src]);
2179 	  }
2180 	  break;
2181 
2182 	case mhop_movs:
2183 	  {
2184 	    long dst = MHI_NUM (prog[pc++]);
2185 	    long src = MHI_NUM (prog[pc++]);
2186 	    printf ("movs %s, %s", regname[dst], regname[src]);
2187 	  }
2188 	  break;
2189 
2190 	case mhop_pushn:
2191 	  printf ("pushn");
2192 	  break;
2193 
2194 	case mhop_popn:
2195 	  printf ("popn");
2196 	  break;
2197 
2198 	case mhop_xchgn:
2199 	  printf ("xchgn");
2200 	  break;
2201 
2202 	case mhop_ldcomp:
2203 	  {
2204 	    long reg = MHI_NUM (prog[pc++]);
2205 	    size_t skip = MHI_NUM (prog[pc++]);
2206 	    char const *comp = MHI_STR (prog[pc]);
2207 	    pc += skip;
2208 	    printf ("ldcomp %s, \"%s\"", regname[reg], comp);
2209 	  }
2210 	  break;
2211 
2212 	case mhop_ldbody:
2213 	  {
2214 	    long reg = MHI_NUM (prog[pc++]);
2215 	    printf ("ldbody %s", regname[reg]);
2216 	  }
2217 	  break;
2218 
2219 	case mhop_call:
2220 	  {
2221 	    char *name = _get_builtin_name (MHI_BUILTIN (prog[pc++]));
2222 	    printf ("call %s", name ? name : "UNKNOWN");
2223 	  }
2224 	  break;
2225 
2226 	case mhop_atoi:
2227 	  printf ("atoi");
2228 	  break;
2229 
2230 	case mhop_itoa:
2231 	  printf ("itoa");
2232 	  break;
2233 
2234 	case mhop_printn:
2235 	  printf ("printn");
2236 	  break;
2237 
2238 	case mhop_prints:
2239 	  printf ("prints");
2240 	  break;
2241 
2242 	case mhop_printlit:
2243 	  {
2244 	    size_t skip = MHI_NUM (prog[pc++]);
2245 	    char const *str = MHI_STR (prog[pc]);
2246 	    char *prt;
2247 	    pc += skip;
2248 	    MU_ASSERT (mu_c_str_escape_trans (str, c_trans, &prt));
2249 	    printf ("printlit \"%s\"", prt);
2250 	    free (prt);
2251 	  }
2252 	  break;
2253 
2254 	case mhop_fmtspec:
2255 	  {
2256 	    int fmtspec = MHI_NUM (prog[pc++]);
2257 	    printf ("fmtspec ");
2258 	    mh_print_fmtspec (fmtspec);
2259 	    printf(", %d", fmtspec & MH_WIDTH_MASK);
2260 	  }
2261 	  break;
2262 
2263 	default:
2264 	  abort ();
2265 	}
2266       printf ("\n");
2267     }
2268   free (lbuf);
2269   free (lab);
2270 }
2271 
2272 void
mh_fvm_create(mh_fvm_t * fvmp,int flags)2273 mh_fvm_create (mh_fvm_t *fvmp, int flags)
2274 {
2275   mh_fvm_t fvm;
2276   const char *charset;
2277 
2278   fvm = mu_zalloc (sizeof *fvm);
2279 
2280   fvm->flags = flags;
2281 
2282   fvm->output = mu_strout;
2283   mu_stream_ref (fvm->output);
2284 
2285   MU_ASSERT (mu_list_create (&fvm->addrlist));
2286 
2287   charset = mh_global_profile_get ("Charset", NULL);
2288   if (charset && strcmp (charset, "auto"))
2289     {
2290       /* Try to set LC_CTYPE according to the value of Charset variable.
2291 	 If Charset is `auto', there's no need to do anything, since it
2292 	 is already set. Otherwise, we need to construct a valid locale
2293 	 value with Charset as its codeset part. The problem is, what
2294 	 language and territory to use for that locale.
2295 
2296 	 Neither LANG nor any other environment variable is of any use,
2297 	 because if it were, the user would have set "Charset: auto".
2298 	 It would be logical to use 'C' or 'POSIX', but these do not
2299 	 work with '.UTF-8'. So, in the absence of any viable alternative,
2300 	 'en_US' is selected. This choice may be overridden by setting
2301 	 the LC_BASE mh_profile variable to the desired base part.
2302       */
2303       const char *lc_base = mh_global_profile_get ("LC_BASE", "en_US");
2304       char *locale = mu_alloc (strlen (lc_base) + 1 + strlen (charset) + 1);
2305       strcpy (locale, lc_base);
2306       strcat (locale, ".");
2307       strcat (locale, charset);
2308       if (!setlocale (LC_CTYPE, locale))
2309         mu_error (_("cannot set LC_CTYPE %s"), locale);
2310       free (locale);
2311     }
2312   //FIXME fvm->charset = charset;
2313 
2314   *fvmp = fvm;
2315 }
2316 
2317 void
mh_fvm_destroy(mh_fvm_t * fvmp)2318 mh_fvm_destroy (mh_fvm_t *fvmp)
2319 {
2320   if (fvmp)
2321     {
2322       mh_fvm_t fvm = *fvmp;
2323 
2324       free (fvm->prog);
2325       free (fvm->numstack);
2326       mh_string_free (&fvm->str[R_REG]);
2327       mh_string_free (&fvm->str[R_ARG]);
2328       mh_string_free (&fvm->str[R_ACC]);
2329       addrlist_destroy (&fvm->addrlist);
2330 
2331       mu_stream_unref (fvm->output);
2332 
2333       free (fvm);
2334       *fvmp = fvm;
2335     }
2336 }
2337 
2338 void
mh_fvm_set_output(mh_fvm_t fvm,mu_stream_t str)2339 mh_fvm_set_output (mh_fvm_t fvm, mu_stream_t str)
2340 {
2341   mu_stream_unref (fvm->output);
2342   fvm->output = str;
2343   mu_stream_ref (fvm->output);
2344 }
2345 
2346 void
mh_fvm_set_width(mh_fvm_t fvm,size_t width)2347 mh_fvm_set_width (mh_fvm_t fvm, size_t width)
2348 {
2349   fvm->width = width - 1;
2350 }
2351 
2352 void
mh_fvm_set_format(mh_fvm_t fvm,mh_format_t fmt)2353 mh_fvm_set_format (mh_fvm_t fvm, mh_format_t fmt)
2354 {
2355   size_t sz = fmt->progcnt * sizeof (fvm->prog[0]);
2356   fvm->prog = mu_realloc (fvm->prog, sz);
2357   memcpy (fvm->prog, fmt->prog, sz);
2358 }
2359 
2360 
2361 
2362 
2363