]> diplodocus.org Git - nmh/blob - uip/fmttest.c
picksbr.c: Specify parameters of nexus's n_action function pointer.
[nmh] / uip / fmttest.c
1 /* fmttest.c -- A program to help test and debug format instructions
2 *
3 * This code is Copyright (c) 2012, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include <h/mh.h>
9 #include <h/fmt_scan.h>
10 #include <h/fmt_compile.h>
11 #include <h/utils.h>
12 #include <h/scansbr.h>
13 #include <h/addrsbr.h>
14 #include "../sbr/m_maildir.h"
15
16 #define FMTTEST_SWITCHES \
17 X("form formatfile", 0, FORMSW) \
18 X("format string", 5, FMTSW) \
19 X("address", 0, ADDRSW) \
20 X("raw", 0, RAWSW) \
21 X("date", 0, DATESW) \
22 X("message", 0, MESSAGESW) \
23 X("file", 0, FILESW) \
24 X("nofile", 0, NFILESW) \
25 X("-component-name component-text", 0, OTHERSW) \
26 X("dupaddrs", 0, DUPADDRSW) \
27 X("nodupaddrs", 0, NDUPADDRSW) \
28 X("ccme", 0, CCMESW) \
29 X("noccme", 0, NCCMESW) \
30 X("outsize size-in-characters", 0, OUTSIZESW) \
31 X("width column-width", 0, WIDTHSW) \
32 X("msgnum number", 0, MSGNUMSW) \
33 X("msgcur flag", 0, MSGCURSW) \
34 X("msgsize size", 0, MSGSIZESW) \
35 X("unseen flag", 0, UNSEENSW) \
36 X("dump", 0, DUMPSW) \
37 X("nodump", 0, NDUMPSW) \
38 X("trace", 0, TRACESW) \
39 X("notrace", 0, NTRACESW) \
40 X("version", 0, VERSIONSW) \
41 X("help", 0, HELPSW) \
42
43 #define X(sw, minchars, id) id,
44 DEFINE_SWITCH_ENUM(FMTTEST);
45 #undef X
46
47 #define X(sw, minchars, id) { sw, minchars, id },
48 DEFINE_SWITCH_ARRAY(FMTTEST, switches);
49 #undef X
50
51 /*
52 * An array containing labels used for branch instructions
53 */
54
55 static struct format **lvec = NULL;
56 static int lused = 0;
57 static int lallocated = 0;
58
59 enum mode_t { MESSAGE, ADDRESS, DATE, RAW };
60 #define DEFADDRFORMAT "%<{error}%{error}: %{text}%|%(putstr(proper{text}))%>"
61 #define DEFDATEFORMAT "%<(nodate{text})error: %{text}%|%(putstr(pretty{text}))%>"
62
63 /*
64 * Context structure used by the tracing routines
65 */
66
67 struct trace_context {
68 int num;
69 char *str;
70 char *outbuf;
71 };
72
73 /*
74 * static prototypes
75 */
76 static void fmt_dump (char *, struct format *);
77 static void dumpone(struct format *);
78 static void initlabels(struct format *);
79 static int findlabel(struct format *);
80 static void assignlabel(struct format *);
81 static char *f_typestr(int);
82 static char *c_typestr(int);
83 static char *c_flagsstr(int);
84 static void litputs(const char *);
85 static void litputc(char);
86 static void process_addresses(struct format *, struct msgs_array *,
87 charstring_t, int, int *,
88 struct fmt_callbacks *);
89 static void process_raw(struct format *, struct msgs_array *, charstring_t,
90 int, int *, struct fmt_callbacks *);
91 static void process_messages(struct format *, struct msgs_array *,
92 struct msgs_array *, charstring_t, char *, int,
93 int, int *, struct fmt_callbacks *);
94 static void process_single_file(FILE *, struct msgs_array *, int *, int,
95 struct format *, charstring_t, int,
96 struct fmt_callbacks *);
97 static void test_trace(void *, struct format *, int, char *, const char *);
98 static char *test_formataddr(char *, char *);
99 static char *test_concataddr(char *, char *);
100 static int insert(struct mailname *);
101 static void mlistfree(void);
102
103 static int nodupcheck = 0; /* If set, no check for duplicates */
104 static int ccme = 0; /* Should I cc myself? */
105 static struct mailname mq; /* Mail addresses to check for duplicates */
106 static char *dummy = "dummy";
107
108 int
109 main (int argc, char **argv)
110 {
111 char *cp, *form = NULL, *format = NULL, *defformat = FORMAT, *folder = NULL;
112 char buf[BUFSIZ], *nfs, **argp, **arguments;
113 charstring_t buffer;
114 struct format *fmt;
115 struct comp *cptr;
116 struct msgs_array msgs = { 0, 0, NULL }, compargs = { 0, 0, NULL};
117 int dump = 0, i;
118 int outputsize = 0, dupaddrs = 1, trace = 0, files = 0;
119 int colwidth = -1, msgnum = -1, msgcur = -1, msgsize = -1, msgunseen = -1;
120 enum mode_t mode = MESSAGE;
121 int dat[5];
122 struct fmt_callbacks cb, *cbp = NULL;
123
124 if (nmh_init(argv[0], 1)) { return 1; }
125
126 arguments = getarguments (invo_name, argc, argv, 1);
127 argp = arguments;
128
129 while ((cp = *argp++)) {
130 if (*cp == '-') {
131 /*
132 * A -- means that we have a component name (like pick);
133 * save the component name and the next argument for the text.
134 */
135 if (*++cp == '-') {
136 if (*++cp == '\0')
137 adios(NULL, "missing component name after --");
138 app_msgarg(&compargs, cp);
139 /* Grab next argument for component text */
140 if (!(cp = *argp++))
141 adios(NULL, "missing argument to %s", argp[-2]);
142 app_msgarg(&compargs, cp);
143 continue;
144 }
145 switch (smatch (cp, switches)) {
146 case AMBIGSW:
147 ambigsw (cp, switches);
148 done (1);
149 case UNKWNSW:
150 adios (NULL, "-%s unknown", cp);
151
152 case HELPSW:
153 snprintf (buf, sizeof(buf), "%s [switches]", invo_name);
154 print_help (buf, switches, 1);
155 done (0);
156 case VERSIONSW:
157 print_version(invo_name);
158 done (0);
159 case OTHERSW:
160 adios(NULL, "internal argument error!");
161 continue;
162
163 case OUTSIZESW:
164 if (!(cp = *argp++) || *cp == '-')
165 adios(NULL, "missing argument to %s", argp[-2]);
166 if (strcmp(cp, "max") == 0)
167 outputsize = INT_MAX;
168 else if (strcmp(cp, "width") == 0)
169 outputsize = sc_width();
170 else
171 outputsize = atoi(cp);
172 continue;
173
174 case FORMSW:
175 if (!(form = *argp++) || *form == '-')
176 adios (NULL, "missing argument to %s", argp[-2]);
177 format = NULL;
178 continue;
179 case FMTSW:
180 if (!(format = *argp++) || *format == '-')
181 adios (NULL, "missing argument to %s", argp[-2]);
182 form = NULL;
183 continue;
184
185 case TRACESW:
186 trace++;
187 continue;
188 case NTRACESW:
189 trace = 0;
190 continue;
191
192 case ADDRSW:
193 mode = ADDRESS;
194 defformat = DEFADDRFORMAT;
195 continue;
196 case RAWSW:
197 mode = RAW;
198 continue;
199 case MESSAGESW:
200 mode = MESSAGE;
201 defformat = FORMAT;
202 dupaddrs = 0;
203 continue;
204 case DATESW:
205 mode = DATE;
206 defformat = DEFDATEFORMAT;
207 continue;
208
209 case FILESW:
210 files++;
211 continue;
212 case NFILESW:
213 files = 0;
214 continue;
215
216 case DUPADDRSW:
217 dupaddrs++;
218 continue;
219 case NDUPADDRSW:
220 dupaddrs = 0;
221 continue;
222
223 case CCMESW:
224 ccme++;
225 continue;
226 case NCCMESW:
227 ccme = 0;
228 continue;
229
230 case WIDTHSW:
231 if (!(cp = *argp++) || *cp == '-')
232 adios(NULL, "missing argument to %s", argp[-2]);
233 colwidth = atoi(cp);
234 continue;
235 case MSGNUMSW:
236 if (!(cp = *argp++) || *cp == '-')
237 adios(NULL, "missing argument to %s", argp[-2]);
238 msgnum = atoi(cp);
239 continue;
240 case MSGCURSW:
241 if (!(cp = *argp++) || *cp == '-')
242 adios(NULL, "missing argument to %s", argp[-2]);
243 msgcur = atoi(cp);
244 continue;
245 case MSGSIZESW:
246 if (!(cp = *argp++) || *cp == '-')
247 adios(NULL, "missing argument to %s", argp[-2]);
248 msgsize = atoi(cp);
249 continue;
250 case UNSEENSW:
251 if (!(cp = *argp++) || *cp == '-')
252 adios(NULL, "missing argument to %s", argp[-2]);
253 msgunseen = atoi(cp);
254 continue;
255
256 case DUMPSW:
257 dump++;
258 continue;
259 case NDUMPSW:
260 dump = 0;
261 continue;
262
263 }
264 }
265
266 /*
267 * Only interpret as a folder if we're in message mode
268 */
269
270 if (mode == MESSAGE && !files && (*cp == '+' || *cp == '@')) {
271 if (folder)
272 adios (NULL, "only one folder at a time!");
273 folder = pluspath (cp);
274 } else
275 app_msgarg(&msgs, cp);
276 }
277
278 /*
279 * Here's our weird heuristic:
280 *
281 * - We allow -dump without any other arguments.
282 * - If you've given any component arguments, we don't require any
283 * other arguments.
284 * - The arguments are interpreted as folders/messages _if_ we're in
285 * message mode, otherwise pass as strings in the text component.
286 */
287
288 if (!dump && compargs.size == 0 && msgs.size == 0) {
289 adios(NULL, "usage: [switches] [+folder] msgs | strings...");
290 }
291
292 /*
293 * If you're picking "raw" as a mode, then you have to select
294 * a format.
295 */
296
297 if (mode == RAW && form == NULL && format == NULL) {
298 adios (NULL, "You must specify a format with -form or -format when "
299 "using -raw");
300 }
301
302 /*
303 * Get new format string. Must be before chdir().
304 */
305 nfs = new_fs (form, format, defformat);
306 (void) fmt_compile(nfs, &fmt, 1);
307
308 if (dump || trace) {
309 initlabels(fmt);
310 if (dump) {
311 fmt_dump(nfs, fmt);
312 if (compargs.size == 0 && msgs.size == 0)
313 done(0);
314 }
315 }
316
317 buffer = charstring_create(BUFSIZ);
318
319 if (outputsize == 0) {
320 if (mode == MESSAGE)
321 outputsize = sc_width();
322 else
323 outputsize = INT_MAX;
324 }
325
326 dat[0] = msgnum;
327 dat[1] = msgcur;
328 dat[2] = msgsize;
329 dat[3] = colwidth == -1 ? outputsize : colwidth;
330 dat[4] = msgunseen;
331
332 /*
333 * If we want to provide our own formataddr, concactaddr, or tracing
334 * callback, do that now. Also, prime ismymbox if we use it.
335 */
336
337 if (dupaddrs == 0 || trace) {
338 ZERO(&cb);
339 cbp = &cb;
340
341 if (dupaddrs == 0) {
342 cb.formataddr = test_formataddr;
343 cb.concataddr = test_concataddr;
344 if (!ccme)
345 ismymbox(NULL);
346 }
347
348 if (trace) {
349 struct trace_context *ctx;
350
351 NEW(ctx);
352 ctx->num = -1;
353 ctx->str = dummy;
354 ctx->outbuf = mh_xstrdup("");
355
356 cb.trace_func = test_trace;
357 cb.trace_context = ctx;
358 }
359 }
360
361 if (mode == MESSAGE) {
362 process_messages(fmt, &compargs, &msgs, buffer, folder, outputsize,
363 files, dat, cbp);
364 } else {
365 if (compargs.size) {
366 for (i = 0; i < compargs.size; i += 2) {
367 cptr = fmt_findcomp(compargs.msgs[i]);
368 if (cptr)
369 cptr->c_text = getcpy(compargs.msgs[i + 1]);
370 }
371 }
372
373 if (mode == ADDRESS) {
374 process_addresses(fmt, &msgs, buffer, outputsize, dat, cbp);
375 } else /* Fall-through for RAW or DATE */
376 process_raw(fmt, &msgs, buffer, outputsize, dat, cbp);
377 }
378
379 charstring_free(buffer);
380 fmt_free(fmt, 1);
381
382 done(0);
383 return 1;
384 }
385
386 /*
387 * Process each address with fmt_scan().
388 */
389
390 struct pqpair {
391 char *pq_text;
392 char *pq_error;
393 struct pqpair *pq_next;
394 };
395
396 static void
397 process_addresses(struct format *fmt, struct msgs_array *addrs,
398 charstring_t buffer, int outwidth, int *dat,
399 struct fmt_callbacks *cb)
400 {
401 int i;
402 char *cp, error[BUFSIZ];
403 struct mailname *mp;
404 struct pqpair *p, *q;
405 struct pqpair pq;
406 struct comp *c;
407
408 if (dat[0] == -1)
409 dat[0] = 0;
410 if (dat[1] == -1)
411 dat[1] = 0;
412 if (dat[2] == -1)
413 dat[2] = 0;
414 if (dat[4] == -1)
415 dat[4] = 0;
416
417 for (i = 0; i < addrs->size; i++) {
418 (q = &pq)->pq_next = NULL;
419 while ((cp = getname(addrs->msgs[i]))) {
420 NEW0(p);
421 if ((mp = getm(cp, NULL, 0, error, sizeof(error))) == NULL) {
422 p->pq_text = mh_xstrdup(cp);
423 p->pq_error = mh_xstrdup(error);
424 } else {
425 p->pq_text = getcpy(mp->m_text);
426 mnfree(mp);
427 }
428 q = (q->pq_next = p);
429 }
430
431 for (p = pq.pq_next; p; p = q) {
432 c = fmt_findcomp("text");
433 if (c) {
434 mh_xfree(c->c_text);
435 c->c_text = p->pq_text;
436 p->pq_text = NULL;
437 }
438 c = fmt_findcomp("error");
439 if (c) {
440 mh_xfree(c->c_text);
441 c->c_text = p->pq_error;
442 p->pq_error = NULL;
443 }
444
445 fmt_scan(fmt, buffer, outwidth, dat, cb);
446 fputs(charstring_buffer(buffer), stdout);
447 mlistfree();
448
449 mh_xfree(p->pq_text);
450 mh_xfree(p->pq_error);
451 q = p->pq_next;
452 free(p);
453 }
454 }
455 }
456
457 /*
458 * Process messages and run them through the format engine. A lot taken
459 * from scan.c.
460 */
461
462 static void
463 process_messages(struct format *fmt, struct msgs_array *comps,
464 struct msgs_array *msgs, charstring_t buffer, char *folder,
465 int outwidth, int files, int *dat,
466 struct fmt_callbacks *cb)
467 {
468 int i, msgnum, msgsize = dat[2], num = dat[0], cur = dat[1];
469 int num_unseen_seq = 0;
470 ivector_t seqnum = ivector_create (0);
471 char *maildir, *cp;
472 struct msgs *mp;
473 FILE *in;
474
475 /*
476 * If 'files' is set, short-circuit everything else and just process
477 * everything now.
478 */
479
480 if (files) {
481 for (i = 0; i < msgs->size; i++) {
482 if ((in = fopen(cp = msgs->msgs[i], "r")) == NULL) {
483 admonish(cp, "unable to open file");
484 continue;
485 }
486 process_single_file(in, comps, dat, msgsize, fmt, buffer,
487 outwidth, cb);
488 }
489
490 return;
491 }
492
493 if (! folder)
494 folder = getfolder(1);
495
496 maildir = m_maildir(folder);
497
498 if (chdir(maildir) < 0)
499 adios(maildir, "unable to change directory to");
500
501 if (!(mp = folder_read(folder, 1)))
502 adios(NULL, "unable to read folder %s", folder);
503
504 if (mp->nummsg == 0)
505 adios(NULL, "no messages in %s", folder);
506
507 for (i = 0; i < msgs->size; i++)
508 if (!m_convert(mp, msgs->msgs[i]))
509 done(1);
510 seq_setprev(mp); /* set the Previous-Sequence */
511
512 context_replace(pfolder, folder); /* update current folder */
513 seq_save(mp); /* synchronize message sequences */
514 context_save(); /* save the context file */
515
516 /*
517 * We want to set the unseen flag if requested, so we have to check
518 * the unseen sequence as well.
519 */
520
521 if (dat[4] == -1) {
522 if ((cp = context_find(usequence)) && *cp) {
523 char **ap, *dp;
524
525 dp = mh_xstrdup(cp);
526 ap = brkstring(dp, " ", "\n");
527 for (i = 0; ap && *ap; i++, ap++)
528 ivector_push_back (seqnum, seq_getnum(mp, *ap));
529
530 num_unseen_seq = i;
531 mh_xfree(dp);
532 }
533 }
534
535 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
536 if (is_selected(mp, msgnum)) {
537 if ((in = fopen(cp = m_name(msgnum), "r")) == NULL) {
538 admonish(cp, "unable to open message");
539 continue;
540 }
541
542 fmt_freecomptext();
543
544 if (num == -1)
545 dat[0] = msgnum;
546
547 if (cur == -1)
548 dat[1] = msgnum == mp->curmsg;
549
550 /*
551 * Check to see if this is in the unseen sequence
552 */
553
554 dat[4] = 0;
555 for (i = 0; i < num_unseen_seq; i++) {
556 if (in_sequence(mp, ivector_at (seqnum, i), msgnum)) {
557 dat[4] = 1;
558 break;
559 }
560 }
561
562 /*
563 * Read in the message and process the components
564 */
565
566 process_single_file(in, comps, dat, msgsize, fmt, buffer,
567 outwidth, cb);
568 }
569 }
570
571 ivector_free (seqnum);
572 folder_free(mp);
573 }
574
575 /*
576 * Process a single file in message mode
577 */
578
579 static void
580 process_single_file(FILE *in, struct msgs_array *comps, int *dat, int msgsize,
581 struct format *fmt, charstring_t buffer, int outwidth,
582 struct fmt_callbacks *cb)
583 {
584 int i, state;
585 char name[NAMESZ], rbuf[NMH_BUFSIZ];
586 m_getfld_state_t gstate;
587 struct comp *c;
588 int bufsz;
589
590 /*
591 * Get our size if we didn't include one
592 */
593
594 if (msgsize == -1) {
595 struct stat st;
596
597 if (fstat(fileno(in), &st) < 0)
598 dat[2] = 0;
599 else
600 dat[2] = st.st_size;
601 }
602
603 /*
604 * Initialize everything else
605 */
606
607 if (dat[0] == -1)
608 dat[0] = 0;
609 if (dat[1] == -1)
610 dat[1] = 0;
611 if (dat[4] == -1)
612 dat[4] = 0;
613
614 /*
615 * Read in the message and process the components
616 */
617
618 gstate = m_getfld_state_init(in);
619 for (;;) {
620 bufsz = sizeof(rbuf);
621 state = m_getfld2(&gstate, name, rbuf, &bufsz);
622 switch (state) {
623 case FLD:
624 case FLDPLUS:
625 i = fmt_addcomptext(name, rbuf);
626 if (i != -1) {
627 while (state == FLDPLUS) {
628 bufsz = sizeof(rbuf);
629 state = m_getfld2(&gstate, name, rbuf, &bufsz);
630 fmt_appendcomp(i, name, rbuf);
631 }
632 }
633
634 while (state == FLDPLUS) {
635 bufsz = sizeof(rbuf);
636 state = m_getfld2(&gstate, name, rbuf, &bufsz);
637 }
638 break;
639
640 case BODY:
641 if (fmt_findcomp("body")) {
642 if ((i = strlen(rbuf)) < outwidth) {
643 bufsz = min (outwidth, (int) sizeof rbuf - i);
644 m_getfld2(&gstate, name, rbuf + i, &bufsz);
645 }
646
647 fmt_addcomptext("body", rbuf);
648 }
649 goto finished;
650
651 default:
652 goto finished;
653 }
654 }
655 finished:
656 fclose(in);
657 m_getfld_state_destroy(&gstate);
658
659 /*
660 * Do this now to override any components in the original message
661 */
662 if (comps->size) {
663 for (i = 0; i < comps->size; i += 2) {
664 c = fmt_findcomp(comps->msgs[i]);
665 if (c) {
666 mh_xfree(c->c_text);
667 c->c_text = getcpy(comps->msgs[i + 1]);
668 }
669 }
670 }
671 fmt_scan(fmt, buffer, outwidth, dat, cb);
672 fputs(charstring_buffer (buffer), stdout);
673 mlistfree();
674 }
675
676 /*
677 * Run text through the format engine with no special processing
678 */
679
680 static void
681 process_raw(struct format *fmt, struct msgs_array *text, charstring_t buffer,
682 int outwidth, int *dat, struct fmt_callbacks *cb)
683 {
684 int i;
685 struct comp *c;
686
687 if (dat[0] == -1)
688 dat[0] = 0;
689 if (dat[1] == -1)
690 dat[1] = 0;
691 if (dat[2] == -1)
692 dat[2] = 0;
693 if (dat[4] == -1)
694 dat[4] = 0;
695
696 c = fmt_findcomp("text");
697
698 for (i = 0; i < text->size; i++) {
699 if (c != NULL) {
700 mh_xfree(c->c_text);
701 c->c_text = getcpy(text->msgs[i]);
702 }
703
704 fmt_scan(fmt, buffer, outwidth, dat, cb);
705 fputs(charstring_buffer (buffer), stdout);
706 mlistfree();
707 }
708 }
709
710 /*
711 * Our basic tracing support callback.
712 *
713 * Print out each instruction as it's executed, including the values of
714 * the num and str registers if they've changed.
715 */
716
717 static void
718 test_trace(void *context, struct format *fmt, int num, char *str,
719 const char *outbuf)
720 {
721 struct trace_context *ctx = (struct trace_context *) context;
722 int changed = 0;
723
724 dumpone(fmt);
725
726 if (num != ctx->num) {
727 printf("num=%d", num);
728 ctx->num = num;
729 changed++;
730 }
731
732 if (str != ctx->str) {
733 if (changed++)
734 putchar(' ');
735 printf("str=");
736 litputs(str);
737 ctx->str = str;
738 }
739
740 if (changed)
741 putchar('\n');
742
743 if (strcmp(outbuf, ctx->outbuf) != 0) {
744 printf("outbuf=");
745 litputs(outbuf);
746 putchar('\n');
747 free(ctx->outbuf);
748 ctx->outbuf = mh_xstrdup(outbuf);
749 }
750 }
751
752 static void
753 fmt_dump (char *nfs, struct format *fmth)
754 {
755 struct format *fmt;
756
757 printf("Instruction dump of format string: \n%s\n", nfs);
758
759 /* Dump them out! */
760 for (fmt = fmth; fmt; ++fmt) {
761 dumpone(fmt);
762 if (fmt->f_type == FT_DONE && fmt->f_value == 0)
763 break;
764 }
765 }
766
767 static void
768 dumpone(struct format *fmt)
769 {
770 int i;
771
772 if ((i = findlabel(fmt)) >= 0)
773 printf("L%d:", i);
774 putchar('\t');
775
776 fputs(f_typestr((int)fmt->f_type), stdout);
777
778 switch (fmt->f_type) {
779
780 case FT_COMP:
781 case FT_LS_COMP:
782 case FT_LV_COMPFLAG:
783 case FT_LV_COMP:
784 printf(", comp ");
785 litputs(fmt->f_comp->c_name);
786 if (fmt->f_comp->c_type)
787 printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
788 if (fmt->f_comp->c_flags)
789 printf(", c_flags %s", c_flagsstr(fmt->f_comp->c_flags));
790 break;
791
792 case FT_LV_SEC:
793 case FT_LV_MIN:
794 case FT_LV_HOUR:
795 case FT_LV_MDAY:
796 case FT_LV_MON:
797 case FT_LS_MONTH:
798 case FT_LS_LMONTH:
799 case FT_LS_ZONE:
800 case FT_LV_YEAR:
801 case FT_LV_WDAY:
802 case FT_LS_DAY:
803 case FT_LS_WEEKDAY:
804 case FT_LV_YDAY:
805 case FT_LV_ZONE:
806 case FT_LV_CLOCK:
807 case FT_LV_RCLOCK:
808 case FT_LV_DAYF:
809 case FT_LV_ZONEF:
810 case FT_LV_DST:
811 case FT_LS_822DATE:
812 case FT_LS_PRETTY:
813 case FT_LOCALDATE:
814 case FT_GMTDATE:
815 case FT_PARSEDATE:
816 printf(", c_name ");
817 litputs(fmt->f_comp->c_name);
818 if (fmt->f_comp->c_type)
819 printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
820 if (fmt->f_comp->c_flags)
821 printf(", c_flags %s", c_flagsstr(fmt->f_comp->c_flags));
822 break;
823
824 case FT_LS_ADDR:
825 case FT_LS_PERS:
826 case FT_LS_MBOX:
827 case FT_LS_HOST:
828 case FT_LS_PATH:
829 case FT_LS_GNAME:
830 case FT_LS_NOTE:
831 case FT_LS_822ADDR:
832 case FT_LV_HOSTTYPE:
833 case FT_LV_INGRPF:
834 case FT_LV_NOHOSTF:
835 case FT_LS_FRIENDLY:
836 case FT_PARSEADDR:
837 case FT_MYMBOX:
838 case FT_GETMYMBOX:
839 case FT_GETMYADDR:
840 printf(", c_name ");
841 litputs(fmt->f_comp->c_name);
842 if (fmt->f_comp->c_type)
843 printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
844 if (fmt->f_comp->c_flags)
845 printf(", c_flags %s", c_flagsstr(fmt->f_comp->c_flags));
846 break;
847
848 case FT_COMPF:
849 printf(", width %d, fill '", fmt->f_width);
850 litputc(fmt->f_fill);
851 printf("' name ");
852 litputs(fmt->f_comp->c_name);
853 if (fmt->f_comp->c_type)
854 printf(", c_type %s", c_typestr(fmt->f_comp->c_type));
855 if (fmt->f_comp->c_flags)
856 printf(", c_flags %s", c_flagsstr(fmt->f_comp->c_flags));
857 break;
858
859 case FT_STRF:
860 case FT_NUMF:
861 printf(", width %d, fill '", fmt->f_width);
862 litputc(fmt->f_fill);
863 putchar('\'');
864 break;
865
866 case FT_LIT:
867 putchar(' ');
868 litputs(fmt->f_text);
869 break;
870
871 case FT_LITF:
872 printf(", width %d, fill '", fmt->f_width);
873 litputc(fmt->f_fill);
874 printf("' ");
875 litputs(fmt->f_text);
876 break;
877
878 case FT_CHAR:
879 putchar(' ');
880 putchar('\'');
881 litputc(fmt->f_char);
882 putchar('\'');
883 break;
884
885
886 case FT_IF_S:
887 case FT_IF_S_NULL:
888 case FT_IF_MATCH:
889 case FT_IF_AMATCH:
890 printf(" continue else goto");
891 /* FALLTHRU */
892 case FT_GOTO:
893 i = findlabel(fmt + fmt->f_skip);
894 printf(" L%d", i);
895 break;
896
897 case FT_IF_V_EQ:
898 case FT_IF_V_NE:
899 case FT_IF_V_GT:
900 i = findlabel(fmt + fmt->f_skip);
901 printf(" %d continue else goto L%d", fmt->f_value, i);
902 break;
903
904 case FT_V_EQ:
905 case FT_V_NE:
906 case FT_V_GT:
907 case FT_LV_LIT:
908 case FT_LV_PLUS_L:
909 case FT_LV_MINUS_L:
910 case FT_LV_MULTIPLY_L:
911 case FT_LV_DIVIDE_L:
912 case FT_LV_MODULO_L:
913 printf(" value %d", fmt->f_value);
914 break;
915
916 case FT_LS_LIT:
917 printf(" str ");
918 litputs(fmt->f_text);
919 break;
920
921 case FT_LS_GETENV:
922 printf(" getenv ");
923 litputs(fmt->f_text);
924 break;
925
926 case FT_LS_DECODECOMP:
927 printf(", comp ");
928 litputs(fmt->f_comp->c_name);
929 break;
930
931 case FT_LS_DECODE:
932 break;
933
934 case FT_LS_TRIM:
935 printf(", width %d", fmt->f_width);
936 break;
937
938 case FT_LV_DAT:
939 printf(", value dat[%d]", fmt->f_value);
940 break;
941 }
942 putchar('\n');
943 }
944
945 /*
946 * Iterate over all instructions and assign labels to the targets of
947 * branch statements
948 */
949
950 static void
951 initlabels(struct format *fmth)
952 {
953 struct format *fmt, *addr;
954 int i;
955
956 /* Assign labels */
957 for (fmt = fmth; fmt; ++fmt) {
958 i = fmt->f_type;
959 if (i == FT_IF_S ||
960 i == FT_IF_S_NULL ||
961 i == FT_IF_V_EQ ||
962 i == FT_IF_V_NE ||
963 i == FT_IF_V_GT ||
964 i == FT_IF_MATCH ||
965 i == FT_IF_AMATCH ||
966 i == FT_GOTO) {
967 addr = fmt + fmt->f_skip;
968 if (findlabel(addr) < 0)
969 assignlabel(addr);
970 }
971 if (fmt->f_type == FT_DONE && fmt->f_value == 0)
972 break;
973 }
974 }
975
976
977 static int
978 findlabel(struct format *addr)
979 {
980 int i;
981
982 for (i = 0; i < lused; ++i)
983 if (addr == lvec[i])
984 return(i);
985 return(-1);
986 }
987
988 static void
989 assignlabel(struct format *addr)
990 {
991 if (lused >= lallocated) {
992 lallocated += 64;
993 lvec = (struct format **)
994 mh_xrealloc(lvec, sizeof(struct format *) * lallocated);
995 }
996
997 lvec[lused++] = addr;
998 }
999
1000 static char *
1001 f_typestr(int t)
1002 {
1003 static char buf[32];
1004
1005 switch (t) {
1006 case FT_COMP: return "COMP";
1007 case FT_COMPF: return "COMPF";
1008 case FT_LIT: return "LIT";
1009 case FT_LITF: return "LITF";
1010 case FT_CHAR: return "CHAR";
1011 case FT_NUM: return "NUM";
1012 case FT_NUMF: return "NUMF";
1013 case FT_STR: return "STR";
1014 case FT_STRF: return "STRF";
1015 case FT_STRFW: return "STRFW";
1016 case FT_STRLIT: return "STRLIT";
1017 case FT_STRLITZ: return "STRLITZ";
1018 case FT_PUTADDR: return "PUTADDR";
1019 case FT_LS_COMP: return "LS_COMP";
1020 case FT_LS_LIT: return "LS_LIT";
1021 case FT_LS_GETENV: return "LS_GETENV";
1022 case FT_LS_CFIND: return "LS_CFIND";
1023 case FT_LS_DECODECOMP: return "LS_DECODECOMP";
1024 case FT_LS_DECODE: return "LS_DECODE";
1025 case FT_LS_TRIM: return "LS_TRIM";
1026 case FT_LV_COMP: return "LV_COMP";
1027 case FT_LV_COMPFLAG: return "LV_COMPFLAG";
1028 case FT_LV_LIT: return "LV_LIT";
1029 case FT_LV_DAT: return "LV_DAT";
1030 case FT_LV_STRLEN: return "LV_STRLEN";
1031 case FT_LV_PLUS_L: return "LV_PLUS_L";
1032 case FT_LV_MINUS_L: return "LV_MINUS_L";
1033 case FT_LV_MULTIPLY_L: return "LV_MULTIPLY_L";
1034 case FT_LV_DIVIDE_L: return "LV_DIVIDE_L";
1035 case FT_LV_MODULO_L: return "LV_MODULO_L";
1036 case FT_LV_CHAR_LEFT: return "LV_CHAR_LEFT";
1037 case FT_LS_MONTH: return "LS_MONTH";
1038 case FT_LS_LMONTH: return "LS_LMONTH";
1039 case FT_LS_ZONE: return "LS_ZONE";
1040 case FT_LS_DAY: return "LS_DAY";
1041 case FT_LS_WEEKDAY: return "LS_WEEKDAY";
1042 case FT_LS_822DATE: return "LS_822DATE";
1043 case FT_LS_PRETTY: return "LS_PRETTY";
1044 case FT_LS_KILO: return "LS_KILO";
1045 case FT_LS_KIBI: return "LS_KIBI";
1046 case FT_LV_SEC: return "LV_SEC";
1047 case FT_LV_MIN: return "LV_MIN";
1048 case FT_LV_HOUR: return "LV_HOUR";
1049 case FT_LV_MDAY: return "LV_MDAY";
1050 case FT_LV_MON: return "LV_MON";
1051 case FT_LV_YEAR: return "LV_YEAR";
1052 case FT_LV_YDAY: return "LV_YDAY";
1053 case FT_LV_WDAY: return "LV_WDAY";
1054 case FT_LV_ZONE: return "LV_ZONE";
1055 case FT_LV_CLOCK: return "LV_CLOCK";
1056 case FT_LV_RCLOCK: return "LV_RCLOCK";
1057 case FT_LV_DAYF: return "LV_DAYF";
1058 case FT_LV_DST: return "LV_DST";
1059 case FT_LV_ZONEF: return "LV_ZONEF";
1060 case FT_LS_PERS: return "LS_PERS";
1061 case FT_LS_MBOX: return "LS_MBOX";
1062 case FT_LS_HOST: return "LS_HOST";
1063 case FT_LS_PATH: return "LS_PATH";
1064 case FT_LS_GNAME: return "LS_GNAME";
1065 case FT_LS_NOTE: return "LS_NOTE";
1066 case FT_LS_ADDR: return "LS_ADDR";
1067 case FT_LS_822ADDR: return "LS_822ADDR";
1068 case FT_LS_FRIENDLY: return "LS_FRIENDLY";
1069 case FT_LV_HOSTTYPE: return "LV_HOSTTYPE";
1070 case FT_LV_INGRPF: return "LV_INGRPF";
1071 case FT_LS_UNQUOTE: return "LS_UNQUOTE";
1072 case FT_LV_NOHOSTF: return "LV_NOHOSTF";
1073 case FT_LOCALDATE: return "LOCALDATE";
1074 case FT_GMTDATE: return "GMTDATE";
1075 case FT_PARSEDATE: return "PARSEDATE";
1076 case FT_PARSEADDR: return "PARSEADDR";
1077 case FT_FORMATADDR: return "FORMATADDR";
1078 case FT_CONCATADDR: return "CONCATADDR";
1079 case FT_MYMBOX: return "MYMBOX";
1080 case FT_GETMYMBOX: return "GETMYMBOX";
1081 case FT_GETMYADDR: return "GETMYADDR";
1082 case FT_SAVESTR: return "SAVESTR";
1083 case FT_DONE: return "DONE";
1084 case FT_PAUSE: return "PAUSE";
1085 case FT_NOP: return "NOP";
1086 case FT_GOTO: return "GOTO";
1087 case FT_IF_S_NULL: return "IF_S_NULL";
1088 case FT_IF_S: return "IF_S";
1089 case FT_IF_V_EQ: return "IF_V_EQ";
1090 case FT_IF_V_NE: return "IF_V_NE";
1091 case FT_IF_V_GT: return "IF_V_GT";
1092 case FT_IF_MATCH: return "IF_MATCH";
1093 case FT_IF_AMATCH: return "IF_AMATCH";
1094 case FT_S_NULL: return "S_NULL";
1095 case FT_S_NONNULL: return "S_NONNULL";
1096 case FT_V_EQ: return "V_EQ";
1097 case FT_V_NE: return "V_NE";
1098 case FT_V_GT: return "V_GT";
1099 case FT_V_MATCH: return "V_MATCH";
1100 case FT_V_AMATCH: return "V_AMATCH";
1101 default:
1102 snprintf(buf, sizeof(buf), "/* ??? #%d */", t);
1103 return(buf);
1104 }
1105 }
1106
1107 static char *
1108 c_typestr(int t)
1109 {
1110 static char buf[64];
1111
1112 snprintb(buf, sizeof(buf), t, CT_BITS);
1113 return(buf);
1114 }
1115
1116 static char *
1117 c_flagsstr(int t)
1118 {
1119 static char buf[64];
1120
1121 snprintb(buf, sizeof(buf), t, CF_BITS);
1122 return(buf);
1123 }
1124
1125 static void
1126 litputs(const char *s)
1127 {
1128 if (s) {
1129 putchar('"');
1130 while (*s)
1131 litputc(*s++);
1132 putchar('"');
1133 } else
1134 fputs("<nil>", stdout);
1135 }
1136
1137 static void
1138 litputc(char c)
1139 {
1140 if (c & ~ 0177) {
1141 printf("\\x%02x", (unsigned char) c);
1142 } else if (c < 0x20 || c == 0177) {
1143 if (c == '\b') {
1144 putchar('\\');
1145 putchar('b');
1146 } else if (c == '\f') {
1147 putchar('\\');
1148 putchar('f');
1149 } else if (c == '\n') {
1150 putchar('\\');
1151 putchar('n');
1152 } else if (c == '\r') {
1153 putchar('\\');
1154 putchar('r');
1155 } else if (c == '\t') {
1156 putchar('\\');
1157 putchar('t');
1158 } else {
1159 putchar('^');
1160 putchar(c ^ 0x40); /* DEL to ?, others to alpha */
1161 }
1162 } else
1163 putchar(c);
1164 }
1165
1166 /*
1167 * Routines/code to support the duplicate address suppression code, adapted
1168 * from replsbr.c
1169 */
1170
1171 static char *buf; /* our current working buffer */
1172 static char *bufend; /* end of working buffer */
1173 static char *last_dst; /* buf ptr at end of last call */
1174 static unsigned int bufsiz=0; /* current size of buf */
1175
1176 #define BUFINCR 512 /* how much to expand buf when if fills */
1177
1178 #define CPY(s) { cp = (s); while ((*dst++ = *cp++)) ; --dst; }
1179
1180 /*
1181 * check if there's enough room in buf for str.
1182 * add more mem if needed
1183 */
1184 #define CHECKMEM(str) \
1185 if ((len = strlen (str)) >= bufend - dst) {\
1186 int i = dst - buf;\
1187 int n = last_dst - buf;\
1188 bufsiz += ((dst + len - bufend) / BUFINCR + 1) * BUFINCR;\
1189 buf = mh_xrealloc (buf, bufsiz);\
1190 dst = buf + i;\
1191 last_dst = buf + n;\
1192 bufend = buf + bufsiz;\
1193 }
1194
1195
1196 /*
1197 * These are versions of similar routines from replsbr.c; the purpose is
1198 * to suppress duplicate addresses from being added to a list when building
1199 * up addresses for the %(formataddr) format function. This is used by
1200 * repl to prevent duplicate addresses from being added to the "to" line.
1201 * See replsbr.c for more information.
1202 *
1203 * We can't use the functions in replsbr.c directly because they are slightly
1204 * different and depend on the rest of replsbr.c
1205 */
1206 static char *
1207 test_formataddr (char *orig, char *str)
1208 {
1209 int len;
1210 char error[BUFSIZ];
1211 int isgroup;
1212 char *dst;
1213 char *cp;
1214 char *sp;
1215 struct mailname *mp = NULL;
1216
1217 /* if we don't have a buffer yet, get one */
1218 if (bufsiz == 0) {
1219 buf = mh_xmalloc (BUFINCR);
1220 last_dst = buf; /* XXX */
1221 bufsiz = BUFINCR - 6; /* leave some slop */
1222 bufend = buf + bufsiz;
1223 }
1224 /*
1225 * If "orig" points to our buffer we can just pick up where we
1226 * left off. Otherwise we have to copy orig into our buffer.
1227 */
1228 if (orig == buf)
1229 dst = last_dst;
1230 else if (!orig || !*orig) {
1231 dst = buf;
1232 *dst = '\0';
1233 } else {
1234 dst = last_dst; /* XXX */
1235 CHECKMEM (orig);
1236 CPY (orig);
1237 }
1238
1239 /* concatenate all the new addresses onto 'buf' */
1240 for (isgroup = 0; (cp = getname (str)); ) {
1241 if ((mp = getm (cp, NULL, 0, error, sizeof(error))) == NULL) {
1242 fprintf(stderr, "bad address \"%s\" -- %s\n", cp, error);
1243 continue;
1244 }
1245 if (isgroup && (mp->m_gname || !mp->m_ingrp)) {
1246 *dst++ = ';';
1247 isgroup = 0;
1248 }
1249 if (insert (mp)) {
1250 /* if we get here we're going to add an address */
1251 if (dst != buf) {
1252 *dst++ = ',';
1253 *dst++ = ' ';
1254 }
1255 if (mp->m_gname) {
1256 CHECKMEM (mp->m_gname);
1257 CPY (mp->m_gname);
1258 isgroup++;
1259 }
1260 sp = adrformat (mp);
1261 CHECKMEM (sp);
1262 CPY (sp);
1263 }
1264 }
1265
1266 if (isgroup)
1267 *dst++ = ';';
1268
1269 *dst = '\0';
1270 last_dst = dst;
1271 return (buf);
1272 }
1273
1274
1275 /*
1276 * The companion to test_formataddr(); it behaves the same way, except doesn't
1277 * do duplicate address detection.
1278 */
1279 static char *
1280 test_concataddr(char *orig, char *str)
1281 {
1282 char *cp;
1283
1284 nodupcheck = 1;
1285 cp = test_formataddr(orig, str);
1286 nodupcheck = 0;
1287 return cp;
1288 }
1289
1290 static int
1291 insert (struct mailname *np)
1292 {
1293 struct mailname *mp;
1294
1295 if (nodupcheck)
1296 return 1;
1297
1298 if (np->m_mbox == NULL)
1299 return 0;
1300
1301 for (mp = &mq; mp->m_next; mp = mp->m_next) {
1302 if (!strcasecmp (FENDNULL(np->m_host),
1303 FENDNULL(mp->m_next->m_host)) &&
1304 !strcasecmp (FENDNULL(np->m_mbox),
1305 FENDNULL(mp->m_next->m_mbox)))
1306 return 0;
1307 }
1308 if (!ccme && ismymbox (np))
1309 return 0;
1310
1311 mp->m_next = np;
1312
1313 return 1;
1314 }
1315
1316 /*
1317 * Reset our duplicate address list
1318 */
1319
1320 void
1321 mlistfree(void)
1322 {
1323 struct mailname *mp, *mp2;
1324
1325 for (mp = mq.m_next; mp; mp = mp2) {
1326 mp2 = mp->m_next;
1327 mnfree(mp);
1328 }
1329 }