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