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