Removed `-format string' switches but made -form accept `=formatstring'.
[mmh] / uip / rcvdist.c
1 /*
2 ** rcvdist.c -- asynchronously redistribute messages
3 **
4 ** This code is Copyright (c) 2002, by the authors of nmh.  See the
5 ** COPYRIGHT file in the root directory of the nmh distribution for
6 ** complete copyright information.
7 */
8
9 #include <h/mh.h>
10 #include <h/fmt_scan.h>
11 #include <h/rcvmail.h>
12 #include <h/tws.h>
13 #include <h/utils.h>
14
15 static struct swit switches[] = {
16 #define FORMSW  0
17         { "form formfile",  4 },
18 #define VERSIONSW  1
19         { "version", 0 },
20 #define HELPSW  2
21         { "help", 0 },
22         { NULL, 0 }
23 };
24
25 static char backup[BUFSIZ] = "";
26 static char drft[BUFSIZ] = "";
27 static char tmpfil[BUFSIZ] = "";
28
29 /*
30 ** prototypes
31 */
32 static void rcvdistout(FILE *, char *, char *);
33 static void unlink_done(int) NORETURN;
34
35
36 int
37 main(int argc, char **argv)
38 {
39         pid_t child_id;
40         int i, vecp = 1;
41         char *addrs = NULL, *cp, *form = NULL, buf[BUFSIZ];
42         char **argp, **arguments, *vec[MAXARGS];
43         FILE *fp;
44         char *tfile = NULL;
45
46         done=unlink_done;
47
48 #ifdef LOCALE
49         setlocale(LC_ALL, "");
50 #endif
51         invo_name = mhbasename(argv[0]);
52
53         /* read user profile/context */
54         context_read();
55
56         arguments = getarguments(invo_name, argc, argv, 1);
57         argp = arguments;
58
59         while ((cp = *argp++)) {
60                 if (*cp == '-') {
61                         switch (smatch(++cp, switches)) {
62                         case AMBIGSW:
63                                 ambigsw(cp, switches);
64                                 done(1);
65                         case UNKWNSW:
66                                 vec[vecp++] = --cp;
67                                 continue;
68
69                         case HELPSW:
70                                 snprintf(buf, sizeof(buf), "%s [switches] [switches for postproc] address ...", invo_name);
71                                 print_help(buf, switches, 1);
72                                 done(1);
73                         case VERSIONSW:
74                                 print_version(invo_name);
75                                 done(1);
76
77                         case FORMSW:
78                                 if (!(form = *argp++) || *form == '-')
79                                         adios(NULL, "missing argument to %s",
80                                                         argp[-2]);
81                                 continue;
82                         }
83                 }
84                 addrs = addrs ? add(cp, add(", ", addrs)) : getcpy(cp);
85         }
86
87         if (addrs == NULL)
88                 adios(NULL, "usage: %s [switches] [switches for postproc] address ...", invo_name);
89
90         umask(~m_gmprot());
91
92         tfile = m_mktemp2(NULL, invo_name, NULL, &fp);
93         if (tfile == NULL) adios("rcvdist", "unable to create temporary file");
94         strncpy(tmpfil, tfile, sizeof(tmpfil));
95
96         cpydata(fileno(stdin), fileno(fp), "message", tmpfil);
97         fseek(fp, 0L, SEEK_SET);
98
99         tfile = m_mktemp2(NULL, invo_name, NULL, NULL);
100         if (tfile == NULL) adios("forw", "unable to create temporary file");
101         strncpy(drft, tfile, sizeof(tmpfil));
102
103         rcvdistout(fp, form, addrs);
104         fclose(fp);
105
106         if (distout(drft, tmpfil, backup) == NOTOK)
107                 done(1);
108
109         vec[0] = mhbasename(postproc);
110         vec[vecp++] = "-dist";
111         vec[vecp++] = drft;
112         vec[vecp] = NULL;
113
114         for (i = 0; (child_id = fork()) == NOTOK && i < 5; i++)
115                 sleep(5);
116         switch (child_id) {
117         case NOTOK:
118                 admonish(NULL, "unable to fork");/* fall */
119         case OK:
120                 execvp(postproc, vec);
121                 fprintf(stderr, "unable to exec ");
122                 perror(postproc);
123                 _exit(1);
124
125         default:
126                 done(pidXwait(child_id, postproc));
127         }
128
129         return 0;  /* dead code to satisfy the compiler */
130 }
131
132 /* very similar to routine in replsbr.c */
133
134 #define SBUFSIZ 256
135
136 static struct format *fmt;
137
138 static int ncomps = 0;
139 static char **compbuffers = 0;
140 static struct comp **used_buf = 0;
141
142 static int dat[5];
143
144 static char *addrcomps[] = {
145         "from",
146         "sender",
147         "reply-to",
148         "to",
149         "cc",
150         "bcc",
151         "resent-from",
152         "resent-sender",
153         "resent-reply-to",
154         "resent-to",
155         "resent-cc",
156         "resent-bcc",
157         NULL
158 };
159
160
161 static void
162 rcvdistout(FILE *inb, char *form, char *addrs)
163 {
164         register int char_read = 0, format_len, i, state;
165         register char *tmpbuf, **nxtbuf, **ap;
166         char *cp, *scanl, name[NAMESZ];
167         register struct comp *cptr, **savecomp;
168         FILE *out;
169
170         if (!(out = fopen(drft, "w")))
171                 adios(drft, "unable to create");
172
173         /* get new format string */
174         cp = new_fs(form ? form : rcvdistcomps, NULL);
175         format_len = strlen(cp);
176         ncomps = fmt_compile(cp, &fmt) + 1;
177         if (!(nxtbuf = compbuffers =
178                         (char **) calloc((size_t) ncomps, sizeof(char *))))
179                 adios(NULL, "unable to allocate component buffers");
180         if (!(savecomp = used_buf =
181                         (struct comp **) calloc((size_t) (ncomps + 1),
182                         sizeof(struct comp *))))
183                 adios(NULL, "unable to allocate component buffer stack");
184         savecomp += ncomps + 1;
185         *--savecomp = 0;
186
187         for (i = ncomps; i--;)
188                 *nxtbuf++ = mh_xmalloc(SBUFSIZ);
189         nxtbuf = compbuffers;
190         tmpbuf = *nxtbuf++;
191
192         for (ap = addrcomps; *ap; ap++) {
193                 FINDCOMP(cptr, *ap);
194                 if (cptr)
195                         cptr->c_type |= CT_ADDR;
196         }
197
198         FINDCOMP(cptr, "addresses");
199         if (cptr)
200                 cptr->c_text = addrs;
201
202         for (state = FLD;;) {
203                 switch (state = m_getfld(state, name, tmpbuf, SBUFSIZ, inb)) {
204                 case FLD:
205                 case FLDPLUS:
206                         if ((cptr = wantcomp[CHASH(name)]))
207                                 do {
208                                         if (!mh_strcasecmp(name, cptr->c_name)) {
209                                                 char_read += msg_count;
210                                                 if (!cptr->c_text) {
211                                                         cptr->c_text = tmpbuf;
212                                                         *--savecomp = cptr;
213                                                         tmpbuf = *nxtbuf++;
214                                                 } else {
215                                                         i = strlen(cp = cptr->c_text) - 1;
216                                                         if (cp[i] == '\n') {
217                                                                 if (cptr->c_type & CT_ADDR) {
218                                                                         cp[i] = 0;
219                                                                         cp = add(",\n\t", cp);
220                                                                 } else
221                                                                         cp = add("\t", cp);
222                                                         }
223                                                         cptr->c_text = add(tmpbuf, cp);
224                                                 }
225                                                 while (state == FLDPLUS) {
226                                                         state = m_getfld(state, name, tmpbuf, SBUFSIZ, inb);
227                                                         cptr->c_text = add(tmpbuf, cptr->c_text);
228                                                         char_read += msg_count;
229                                                 }
230                                                 break;
231                                         }
232                                 } while ((cptr = cptr->c_next));
233
234                         while (state == FLDPLUS)
235                                 state = m_getfld(state, name, tmpbuf, SBUFSIZ, inb);
236                         break;
237
238                 case LENERR:
239                 case FMTERR:
240                 case BODY:
241                 case FILEEOF:
242                         goto finished;
243
244                 default:
245                         adios(NULL, "m_getfld() returned %d", state);
246                 }
247         }
248 finished: ;
249
250         i = format_len + char_read + 256;
251         scanl = mh_xmalloc((size_t) i + 2);
252         dat[0] = dat[1] = dat[2] = dat[4] = 0;
253         dat[3] = OUTPUTLINELEN;
254         fmt_scan(fmt, scanl, i, dat);
255         fputs(scanl, out);
256
257         if (ferror(out))
258                 adios(drft, "error writing");
259         fclose(out);
260
261         free(scanl);
262         for (nxtbuf = compbuffers, i = ncomps; (cptr = *savecomp++);
263                         nxtbuf++, i--)
264                 free(cptr->c_text);
265         while (i-- > 0)
266                 free(*nxtbuf++);
267         free((char *) compbuffers);
268         free((char *) used_buf);
269 }
270
271
272 static void
273 unlink_done(int status)
274 {
275         if (backup[0])
276                 unlink(backup);
277         if (drft[0])
278                 unlink(drft);
279         if (tmpfil[0])
280                 unlink(tmpfil);
281
282         exit(status ? RCV_MBX : RCV_MOK);
283 }