]> diplodocus.org Git - nmh/blob - uip/repl.c
Use va_copy() to get a copy of va_list, instead of using original.
[nmh] / uip / repl.c
1 /* repl.c -- reply to a message
2 *
3 * This code is Copyright (c) 2002, 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/mime.h>
10 #include "h/done.h"
11 #include <h/utils.h>
12 #include "sbr/m_maildir.h"
13 #include "replsbr.h"
14
15 #define REPL_SWITCHES \
16 X("group", 0, GROUPSW) \
17 X("nogroup", 0, NGROUPSW) \
18 X("annotate", 0, ANNOSW) \
19 X("noannotate", 0, NANNOSW) \
20 X("cc all|to|cc|me", 0, CCSW) \
21 X("nocc all|to|cc|me", 0, NCCSW) \
22 X("draftfolder +folder", 0, DFOLDSW) \
23 X("draftmessage msg", 0, DMSGSW) \
24 X("nodraftfolder", 0, NDFLDSW) \
25 X("editor editor", 0, EDITRSW) \
26 X("noedit", 0, NEDITSW) \
27 X("convertargs type argstring", 0, CONVERTARGSW) \
28 X("fcc folder", 0, FCCSW) \
29 X("filter filterfile", 0, FILTSW) \
30 X("form formfile", 0, FORMSW) \
31 X("format", 5, FRMTSW) \
32 X("noformat", 7, NFRMTSW) \
33 X("inplace", 0, INPLSW) \
34 X("noinplace", 0, NINPLSW) \
35 X("mime", 0, MIMESW) \
36 X("nomime", 0, NMIMESW) \
37 X("query", 0, QURYSW) \
38 X("noquery", 0, NQURYSW) \
39 X("whatnowproc program", 0, WHATSW) \
40 X("nowhatnowproc", 0, NWHATSW) \
41 X("width columns", 0, WIDTHSW) \
42 X("version", 0, VERSIONSW) \
43 X("help", 0, HELPSW) \
44 X("file file", 4, FILESW) \
45 X("build", 5, BILDSW) /* interface from mhe */ \
46 X("atfile", 0, ATFILESW) \
47 X("noatfile", 0, NOATFILESW) \
48 X("fmtproc program", 0, FMTPROCSW) \
49 X("nofmtproc", 0, NFMTPROCSW) \
50
51 #define X(sw, minchars, id) id,
52 DEFINE_SWITCH_ENUM(REPL);
53 #undef X
54
55 #define X(sw, minchars, id) { sw, minchars, id },
56 DEFINE_SWITCH_ARRAY(REPL, switches);
57 #undef X
58
59 #define CC_SWITCHES \
60 X("to", 0, CTOSW) \
61 X("cc", 0, CCCSW) \
62 X("me", 0, CMESW) \
63 X("all", 0, CALSW) \
64
65 #define X(sw, minchars, id) id,
66 DEFINE_SWITCH_ENUM(CC);
67 #undef X
68
69 #define X(sw, minchars, id) { sw, minchars, id },
70 DEFINE_SWITCH_ARRAY(CC, ccswitches);
71 #undef X
72
73 #define DISPO_SWITCHES \
74 X("quit", 0, NOSW) \
75 X("replace", 0, YESW) \
76 X("list", 0, LISTDSW) \
77 X("refile +folder", 0, REFILSW) \
78 X("new", 0, NEWSW) \
79
80 #define X(sw, minchars, id) id,
81 DEFINE_SWITCH_ENUM(DISPO);
82 #undef X
83
84 #define X(sw, minchars, id) { sw, minchars, id },
85 DEFINE_SWITCH_ARRAY(DISPO, aqrnl);
86 #undef X
87
88 static struct swit aqrl[] = {
89 { "quit", 0, NOSW },
90 { "replace", 0, YESW },
91 { "list", 0, LISTDSW },
92 { "refile +folder", 0, REFILSW },
93 { NULL, 0, 0 }
94 };
95
96 static short outputlinelen = OUTPUTLINELEN;
97 static bool groupreply; /* Is this a group reply? */
98
99 static bool mime; /* include original as MIME part */
100 static char *form = NULL; /* form (components) file */
101 static char *filter = NULL; /* message filter file */
102 static char *fcc = NULL; /* folders to add to Fcc: header */
103
104
105 /*
106 * prototypes
107 */
108 static void docc (char *, int);
109 static void add_convert_header (const char *, char *, char *, char *);
110
111
112 int
113 main (int argc, char **argv)
114 {
115 int i, isdf = 0;
116 bool anot = false;
117 bool inplace = true;
118 bool nedit = false;
119 bool nwhat = false;
120 bool atfile = false;
121 int fmtproc = -1;
122 char *cp, *cwd, *dp, *maildir, *file = NULL;
123 char *folder = NULL, *msg = NULL, *dfolder = NULL;
124 char *dmsg = NULL, *ed = NULL, drft[BUFSIZ], buf[BUFSIZ];
125 char **argp, **arguments;
126 svector_t convert_types = svector_create (10);
127 svector_t convert_args = svector_create (10);
128 size_t n;
129 struct msgs *mp = NULL;
130 struct stat st;
131 FILE *in;
132 bool buildsw = false;
133
134 if (nmh_init(argv[0], true, true)) { return 1; }
135
136 arguments = getarguments (invo_name, argc, argv, 1);
137 argp = arguments;
138
139 while ((cp = *argp++)) {
140 if (*cp == '-') {
141 switch (smatch (++cp, switches)) {
142 case AMBIGSW:
143 ambigsw (cp, switches);
144 done (1);
145 case UNKWNSW:
146 die("-%s unknown", cp);
147
148 case HELPSW:
149 snprintf (buf, sizeof(buf), "%s: [+folder] [msg] [switches]",
150 invo_name);
151 print_help (buf, switches, 1);
152 done (0);
153 case VERSIONSW:
154 print_version(invo_name);
155 done (0);
156
157 case GROUPSW:
158 groupreply = true;
159 continue;
160 case NGROUPSW:
161 groupreply = false;
162 continue;
163
164 case ANNOSW:
165 anot = true;
166 continue;
167 case NANNOSW:
168 anot = false;
169 continue;
170
171 case CCSW:
172 if (!(cp = *argp++) || *cp == '-')
173 die("missing argument to %s", argp[-2]);
174 docc (cp, 1);
175 continue;
176 case NCCSW:
177 if (!(cp = *argp++) || *cp == '-')
178 die("missing argument to %s", argp[-2]);
179 docc (cp, 0);
180 continue;
181
182 case EDITRSW:
183 if (!(ed = *argp++) || *ed == '-')
184 die("missing argument to %s", argp[-2]);
185 nedit = false;
186 continue;
187 case NEDITSW:
188 nedit = true;
189 continue;
190
191 case CONVERTARGSW: {
192 char *type;
193 size_t i;
194
195 if (!(type = *argp++)) {
196 die("missing type argument to %s", argp[-2]);
197 }
198 if (!(cp = *argp++)) {
199 die("missing argstring argument to %s",
200 argp[-3]);
201 }
202
203 for (i = 0; i < svector_size (convert_types); ++i) {
204 if (! strcmp (svector_at (convert_types, i), type)) {
205 /* Already saw this type, so just update
206 its args. */
207 svector_strs (convert_args)[i] = cp;
208 break;
209 }
210 }
211
212 if (i == svector_size (convert_types)) {
213 svector_push_back (convert_types, type);
214 svector_push_back (convert_args, cp);
215 }
216 continue;
217 }
218
219 case WHATSW:
220 if (!(whatnowproc = *argp++) || *whatnowproc == '-')
221 die("missing argument to %s", argp[-2]);
222 nwhat = false;
223 continue;
224 case BILDSW:
225 buildsw = true;
226 /* FALLTHRU */
227 case NWHATSW:
228 nwhat = true;
229 continue;
230
231 case FCCSW:
232 if (!(cp = *argp++) || *cp == '-')
233 die("missing argument to %s", argp[-2]);
234 dp = NULL;
235 if (*cp == '@')
236 cp = dp = path (cp + 1, TSUBCWF);
237 if (fcc)
238 fcc = add (", ", fcc);
239 fcc = add (cp, fcc);
240 free(dp);
241 continue;
242
243 case FILESW:
244 if (file)
245 die("only one file at a time!");
246 if (!(cp = *argp++) || *cp == '-')
247 die("missing argument to %s", argp[-2]);
248 file = path (cp, TFILE);
249 continue;
250 case FILTSW:
251 if (!(cp = *argp++) || *cp == '-')
252 die("missing argument to %s", argp[-2]);
253 filter = mh_xstrdup(etcpath(cp));
254 mime = false;
255 continue;
256 case FORMSW:
257 if (!(form = *argp++) || *form == '-')
258 die("missing argument to %s", argp[-2]);
259 continue;
260
261 case FRMTSW:
262 filter = mh_xstrdup(etcpath(mhlreply));
263 mime = false;
264 continue;
265 case NFRMTSW:
266 filter = NULL;
267 continue;
268
269 case INPLSW:
270 inplace = true;
271 continue;
272 case NINPLSW:
273 inplace = false;
274 continue;
275
276 case MIMESW:
277 mime = true;
278 filter = NULL;
279 continue;
280 case NMIMESW:
281 mime = false;
282 continue;
283
284 case QURYSW:
285 querysw++;
286 continue;
287 case NQURYSW:
288 querysw = 0;
289 continue;
290
291 case WIDTHSW:
292 if (!(cp = *argp++) || *cp == '-')
293 die("missing argument to %s", argp[-2]);
294 if ((outputlinelen = atoi (cp)) < 10)
295 die("impossible width %d", outputlinelen);
296 continue;
297
298 case DFOLDSW:
299 if (dfolder)
300 die("only one draft folder at a time!");
301 if (!(cp = *argp++) || *cp == '-')
302 die("missing argument to %s", argp[-2]);
303 dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
304 *cp != '@' ? TFOLDER : TSUBCWF);
305 continue;
306 case DMSGSW:
307 if (dmsg)
308 die("only one draft message at a time!");
309 if (!(dmsg = *argp++) || *dmsg == '-')
310 die("missing argument to %s", argp[-2]);
311 continue;
312 case NDFLDSW:
313 dfolder = NULL;
314 isdf = NOTOK;
315 continue;
316
317 case ATFILESW:
318 atfile = true;
319 continue;
320 case NOATFILESW:
321 atfile = false;
322 continue;
323
324 case FMTPROCSW:
325 if (!(formatproc = *argp++) || *formatproc == '-')
326 die("missing argument to %s", argp[-2]);
327 fmtproc = 1;
328 continue;
329 case NFMTPROCSW:
330 fmtproc = 0;
331 continue;
332 }
333 }
334 if (*cp == '+' || *cp == '@') {
335 if (folder)
336 die("only one folder at a time!");
337 folder = pluspath (cp);
338 } else {
339 if (msg)
340 die("only one message at a time!");
341 msg = cp;
342 }
343 }
344
345 if (ccto == -1)
346 ccto = groupreply;
347 if (cccc == -1)
348 cccc = groupreply;
349 if (ccme == -1)
350 ccme = groupreply;
351
352 cwd = mh_xstrdup(pwd ());
353
354 if (!context_find ("path"))
355 free (path ("./", TFOLDER));
356 if (file && (msg || folder))
357 die("can't mix files and folders/msgs");
358
359 try_it_again:
360
361 strncpy (drft, buildsw ? m_maildir ("reply")
362 : m_draft (dfolder, NULL, NOUSE, &isdf), sizeof(drft));
363
364 /* Check if a draft exists */
365 if (!buildsw && stat (drft, &st) != NOTOK) {
366 printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
367 for (i = LISTDSW; i != YESW;) {
368 if (!(argp = read_switch_multiword ("\nDisposition? ",
369 isdf ? aqrnl : aqrl)))
370 done (1);
371 switch (i = smatch (*argp, isdf ? aqrnl : aqrl)) {
372 case NOSW:
373 done (0);
374 case NEWSW:
375 dmsg = NULL;
376 goto try_it_again;
377 case YESW:
378 break;
379 case LISTDSW:
380 showfile (++argp, drft);
381 break;
382 case REFILSW:
383 if (refile (++argp, drft) == 0)
384 i = YESW;
385 break;
386 default:
387 inform("say what?");
388 break;
389 }
390 }
391 }
392
393 if (file) {
394 /*
395 * We are replying to a file.
396 */
397 anot = false; /* we don't want to annotate a file */
398 } else {
399 /*
400 * We are replying to a message.
401 */
402 if (!msg)
403 msg = "cur";
404 if (!folder)
405 folder = getfolder (1);
406 maildir = m_maildir (folder);
407
408 if (chdir (maildir) == NOTOK)
409 adios (maildir, "unable to change directory to");
410
411 /* read folder and create message structure */
412 if (!(mp = folder_read (folder, 1)))
413 die("unable to read folder %s", folder);
414
415 /* check for empty folder */
416 if (mp->nummsg == 0)
417 die("no messages in %s", folder);
418
419 /* parse the message range/sequence/name and set SELECTED */
420 if (!m_convert (mp, msg))
421 done (1);
422 seq_setprev (mp); /* set the previous-sequence */
423
424 if (mp->numsel > 1)
425 die("only one message at a time!");
426
427 context_replace (pfolder, folder); /* update current folder */
428 seq_setcur (mp, mp->lowsel); /* update current message */
429 seq_save (mp); /* synchronize sequences */
430 context_save (); /* save the context file */
431 }
432
433 msg = file ? file : mh_xstrdup(m_name (mp->lowsel));
434
435 if ((in = fopen (msg, "r")) == NULL)
436 adios (msg, "unable to open");
437
438 /* find form (components) file */
439 if (!form) {
440 if (groupreply)
441 form = etcpath (replgroupcomps);
442 else
443 form = etcpath (replcomps);
444 }
445
446 replout (in, msg, drft, mp, outputlinelen, mime, form, filter,
447 fcc, fmtproc);
448 fclose (in);
449
450 {
451 char *filename = file ? file : concat (mp->foldpath, "/", msg, NULL);
452
453 for (n = 0; n < svector_size (convert_types); ++n) {
454 add_convert_header (svector_at (convert_types, n),
455 svector_at (convert_args, n),
456 filename, drft);
457 }
458 if (! file) {
459 free (filename);
460 }
461 }
462
463 if (nwhat)
464 done (0);
465 what_now (ed, nedit, NOUSE, drft, msg, 0, mp, anot ? "Replied" : NULL,
466 inplace, cwd, atfile);
467
468 svector_free (convert_args);
469 svector_free (convert_types);
470
471 done (1);
472 return 1;
473 }
474
475 static void
476 docc (char *cp, int ccflag)
477 {
478 switch (smatch (cp, ccswitches)) {
479 case AMBIGSW:
480 ambigsw (cp, ccswitches);
481 done (1);
482 case UNKWNSW:
483 die("-%scc %s unknown", ccflag ? "" : "no", cp);
484
485 case CTOSW:
486 ccto = ccflag;
487 break;
488
489 case CCCSW:
490 cccc = ccflag;
491 break;
492
493 case CMESW:
494 ccme = ccflag;
495 break;
496
497 case CALSW:
498 ccto = cccc = ccme = ccflag;
499 break;
500 }
501 }
502
503 /*
504 * Add pseudoheaders that will pass the convert arguments to
505 * mhbuild. They have the form:
506 * MHBUILD_FILE_PSEUDOHEADER-text/calendar: /home/user/Mail/inbox/7
507 * MHBUILD_ARGS_PSEUDOHEADER-text/calendar: reply -accept
508 * The ARGS pseudoheader is optional, but we always add it when
509 * -convertargs is used.
510 */
511 static void
512 add_convert_header (const char *convert_type, char *convert_arg,
513 char *filename, char *drft)
514 {
515 char *field_name;
516
517 field_name = concat (MHBUILD_FILE_PSEUDOHEADER, convert_type, NULL);
518 annotate (drft, field_name, filename, 1, 0, -2, 1);
519 free (field_name);
520
521 field_name = concat (MHBUILD_ARGS_PSEUDOHEADER, convert_type, NULL);
522 annotate (drft, field_name, convert_arg, 1, 0, -2, 1);
523 free (field_name);
524 }