Updated #include's to point to new location of mts.h on mts/generic
[mmh] / uip / whatnowsbr.c
1
2 /*
3  * whatnowsbr.c -- the WhatNow shell
4  *
5  * $Id$
6  */
7
8 #include <h/mh.h>
9 #include <fcntl.h>
10 #include <signal.h>
11 #include <h/mime.h>
12
13 static struct swit whatnowswitches[] = {
14 #define DFOLDSW                 0
15     { "draftfolder +folder", 0 },
16 #define DMSGSW                  1
17     { "draftmessage msg", 0 },
18 #define NDFLDSW                 2
19     { "nodraftfolder", 0 },
20 #define EDITRSW                 3
21     { "editor editor", 0 },
22 #define NEDITSW                 4
23     { "noedit", 0 },
24 #define PRMPTSW                 5
25     { "prompt string", 4 },
26 #define VERSIONSW               6
27     { "version", 0 },
28 #define HELPSW                  7
29     { "help", 0 },
30     { NULL, 0 }
31 };
32
33 /*
34  * Options at the "whatnow" prompt
35  */
36 static struct swit aleqs[] = {
37 #define EDITSW                         0
38     { "edit [<editor> <switches>]", 0 },
39 #define REFILEOPT                      1
40     { "refile [<switches>] +folder", 0 },
41 #define BUILDMIMESW                    2
42     { "mime [<switches>]", 0 },
43 #define DISPSW                         3
44     { "display [<switches>]", 0 },
45 #define LISTSW                         4
46     { "list [<switches>]", 0 },
47 #define SENDSW                         5
48     { "send [<switches>]", 0 },
49 #define PUSHSW                         6
50     { "push [<switches>]", 0 },
51 #define WHOMSW                         7
52     { "whom [<switches>]", 0 },
53 #define QUITSW                         8
54     { "quit [-delete]", 0 },
55 #define DELETESW                       9
56     { "delete", 0 },
57     { NULL, 0 }
58 };
59
60 static char *myprompt = "\nWhat now? ";
61
62 /*
63  * static prototypes
64  */
65 static int editfile (char **, char **, char *, int, struct msgs *,
66         char *, char *, int);
67 static int sendfile (char **, char *, int);
68 static void sendit (char *, char **, char *, int);
69 static int buildfile (char **, char *);
70 static int check_draft (char *);
71 static int whomfile (char **, char *);
72 static int removefile (char *);
73
74 #ifdef HAVE_LSTAT
75 static int copyf (char *, char *);
76 #endif
77
78
79 int
80 WhatNow (int argc, char **argv)
81 {
82     int isdf = 0, nedit = 0, use = 0;
83     char *cp, *dfolder = NULL, *dmsg = NULL;
84     char *ed = NULL, *drft = NULL, *msgnam = NULL;
85     char buf[BUFSIZ], prompt[BUFSIZ];
86     char **argp, **arguments;
87     struct stat st;
88
89     invo_name = r1bindex (argv[0], '/');
90
91     /* read user profile/context */
92     context_read();
93
94     arguments = getarguments (invo_name, argc, argv, 1);
95     argp = arguments;
96
97     while ((cp = *argp++)) {
98         if (*cp == '-') {
99             switch (smatch (++cp, whatnowswitches)) {
100             case AMBIGSW: 
101                 ambigsw (cp, whatnowswitches);
102                 done (1);
103             case UNKWNSW: 
104                 adios (NULL, "-%s unknown", cp);
105
106             case HELPSW: 
107                 snprintf (buf, sizeof(buf), "%s [switches] [file]", invo_name);
108                 print_help (buf, whatnowswitches, 1);
109                 done (1);
110             case VERSIONSW:
111                 print_version(invo_name);
112                 done (1);
113
114             case DFOLDSW: 
115                 if (dfolder)
116                     adios (NULL, "only one draft folder at a time!");
117                 if (!(cp = *argp++) || *cp == '-')
118                     adios (NULL, "missing argument to %s", argp[-2]);
119                 dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
120                                 *cp != '@' ? TFOLDER : TSUBCWF);
121                 continue;
122             case DMSGSW: 
123                 if (dmsg)
124                     adios (NULL, "only one draft message at a time!");
125                 if (!(dmsg = *argp++) || *dmsg == '-')
126                     adios (NULL, "missing argument to %s", argp[-2]);
127                 continue;
128             case NDFLDSW: 
129                 dfolder = NULL;
130                 isdf = NOTOK;
131                 continue;
132
133             case EDITRSW: 
134                 if (!(ed = *argp++) || *ed == '-')
135                     adios (NULL, "missing argument to %s", argp[-2]);
136                 nedit = 0;
137                 continue;
138             case NEDITSW: 
139                 nedit++;
140                 continue;
141
142             case PRMPTSW:
143                 if (!(myprompt = *argp++) || *myprompt == '-')
144                     adios (NULL, "missing argument to %s", argp[-2]);
145                 continue;
146             }
147         }
148         if (drft)
149             adios (NULL, "only one draft at a time!");
150         else
151             drft = cp;
152     }
153
154     if ((drft == NULL && (drft = getenv ("mhdraft")) == NULL) || *drft == 0)
155         drft = getcpy (m_draft (dfolder, dmsg, 1, &isdf));
156
157     msgnam = (cp = getenv ("mhaltmsg")) && *cp ? getcpy (cp) : NULL;
158
159     if ((cp = getenv ("mhuse")) && *cp)
160         use = atoi (cp);
161
162     if (ed == NULL && ((ed = getenv ("mheditor")) == NULL || *ed == 0)) {
163         ed = NULL;
164         nedit++;
165     }
166
167     /* start editing the draft, unless -noedit was given */
168     if (!nedit && editfile (&ed, NULL, drft, use, NULL, msgnam, NULL, 1) < 0)
169         done (1);
170
171     snprintf (prompt, sizeof(prompt), myprompt, invo_name);
172     for (;;) {
173         if (!(argp = getans (prompt, aleqs))) {
174             unlink (LINK);
175             done (1);
176         }
177         switch (smatch (*argp, aleqs)) {
178         case DISPSW:
179             /* display the message being replied to, or distributed */
180             if (msgnam)
181                 showfile (++argp, msgnam);
182             else
183                 advise (NULL, "no alternate message to display");
184             break;
185
186         case BUILDMIMESW:
187             /* Translate MIME composition file */
188             buildfile (++argp, drft);
189             break;
190
191         case EDITSW:
192             /* Call an editor on the draft file */
193             if (*++argp)
194                 ed = *argp++;
195             if (editfile (&ed, argp, drft, NOUSE, NULL, msgnam, NULL, 1) == NOTOK)
196                 done (1);
197             break;
198
199         case LISTSW: 
200             /* display the draft file */
201             showfile (++argp, drft);
202             break;
203
204         case WHOMSW:
205             /* Check to whom the draft would be sent */
206             whomfile (++argp, drft);
207             break;
208
209         case QUITSW:
210             /* Quit, and possibly delete the draft */
211             if (*++argp && (*argp[0] == 'd' ||
212                 ((*argp)[0] == '-' && (*argp)[1] == 'd'))) {
213                 removefile (drft);
214             } else {
215                 if (stat (drft, &st) != NOTOK)
216                     advise (NULL, "draft left on %s", drft);
217             }
218             done (1);
219
220         case DELETESW:
221             /* Delete draft and exit */
222             removefile (drft);
223             done (1);
224
225         case PUSHSW:
226             /* Send draft in background */
227             if (sendfile (++argp, drft, 1))
228                 done (1);
229             break;
230
231         case SENDSW: 
232             /* Send draft */
233             sendfile (++argp, drft, 0);
234             break;
235
236         case REFILEOPT: 
237             /* Refile the draft */
238             if (refile (++argp, drft) == 0)
239                 done (0);
240             break;
241
242         default: 
243             /* Unknown command */
244             advise (NULL, "say what?");
245             break;
246         }
247     }
248     /*NOTREACHED*/
249 }
250
251 /*
252  * EDIT
253  */
254
255 static int  reedit = 0;         /* have we been here before?     */
256 static char *edsave = NULL;     /* the editor we used previously */
257
258
259 static int
260 editfile (char **ed, char **arg, char *file, int use, struct msgs *mp,
261           char *altmsg, char *cwd, int save_editor)
262 {
263     int pid, status, vecp;
264     char altpath[BUFSIZ], linkpath[BUFSIZ];
265     char *cp, *vec[MAXARGS];
266     struct stat st;
267
268 #ifdef HAVE_LSTAT
269     int slinked;
270 #if 0
271     int oumask; /* PJS: for setting permissions on symlinks. */
272 #endif
273 #endif /* HAVE_LSTAT */
274
275     /* Was there a previous edit session? */
276     if (reedit) {
277         if (!*ed) {             /* no explicit editor      */
278             *ed = edsave;       /* so use the previous one */
279             if ((cp = r1bindex (*ed, '/')) == NULL)
280                 cp = *ed;
281
282             /* unless we've specified it via "editor-next" */
283             cp = concat (cp, "-next", NULL);
284             if ((cp = context_find (cp)) != NULL)
285                 *ed = cp;
286         }
287     } else {
288         /* set initial editor */
289         if (*ed == NULL && (*ed = context_find ("editor")) == NULL)
290             *ed = defaulteditor;
291     }
292
293     if (altmsg) {
294         if (mp == NULL || *altmsg == '/' || cwd == NULL)
295             strncpy (altpath, altmsg, sizeof(altpath));
296         else
297             snprintf (altpath, sizeof(altpath), "%s/%s", mp->foldpath, altmsg);
298         if (cwd == NULL)
299             strncpy (linkpath, LINK, sizeof(linkpath));
300         else
301             snprintf (linkpath, sizeof(linkpath), "%s/%s", cwd, LINK);
302     }
303
304     if (altmsg) {
305         unlink (linkpath);
306 #ifdef HAVE_LSTAT
307         if (link (altpath, linkpath) == NOTOK) {
308 #if 0
309             /* I don't think permission on symlinks matters /JLR */
310             oumask = umask(0044);       /* PJS: else symlinks are world 'r' */
311 #endif
312             symlink (altpath, linkpath);
313 #if 0
314             umask(oumask);              /* PJS: else symlinks are world 'r' */
315 #endif
316             slinked = 1;
317         } else {
318             slinked = 0;
319         }
320 #else /* not HAVE_LSTAT */
321         link (altpath, linkpath);
322 #endif /* not HAVE_LSTAT */
323     }
324
325     context_save ();    /* save the context file */
326     fflush (stdout);
327
328     switch (pid = vfork ()) {
329         case NOTOK: 
330             advise ("fork", "unable to");
331             status = NOTOK;
332             break;
333
334         case OK: 
335             if (cwd)
336                 chdir (cwd);
337             if (altmsg) {
338                 if (mp)
339                     m_putenv ("mhfolder", mp->foldpath);
340                 m_putenv ("editalt", altpath);
341             }
342
343             vecp = 0;
344             vec[vecp++] = r1bindex (*ed, '/');
345             if (arg)
346                 while (*arg)
347                     vec[vecp++] = *arg++;
348             vec[vecp++] = file;
349             vec[vecp] = NULL;
350
351             execvp (*ed, vec);
352             fprintf (stderr, "unable to exec ");
353             perror (*ed);
354             _exit (-1);
355
356         default: 
357             if ((status = pidwait (pid, NOTOK))) {
358 #ifdef ATTVIBUG
359                 if ((cp = r1bindex (*ed, '/'))
360                         && strcmp (cp, "vi") == 0
361                         && (status & 0x00ff) == 0)
362                     status = 0;
363                 else {
364 #endif
365                 if (((status & 0xff00) != 0xff00)
366                     && (!reedit || (status & 0x00ff))) {
367                     if (!use && (status & 0xff00) &&
368                             (rename (file, cp = m_backup (file)) != NOTOK)) {
369                         advise (NULL, "problems with edit--draft left in %s", cp);
370                     } else {
371                         advise (NULL, "problems with edit--%s preserved", file);
372                     }
373                 }
374                 status = -2;    /* maybe "reedit ? -2 : -1"? */
375                 break;
376 #ifdef ATTVIBUG
377                 }
378 #endif
379             }
380
381             reedit++;
382 #ifdef HAVE_LSTAT
383             if (altmsg
384                     && mp
385                     && !is_readonly(mp)
386                     && (slinked
387                            ? lstat (linkpath, &st) != NOTOK
388                                 && S_ISREG(st.st_mode)
389                                 && copyf (linkpath, altpath) == NOTOK
390                            : stat (linkpath, &st) != NOTOK
391                                 && st.st_nlink == 1
392                                 && (unlink (altpath) == NOTOK
393                                         || link (linkpath, altpath) == NOTOK)))
394                 advise (linkpath, "unable to update %s from", altmsg);
395 #else /* HAVE_LSTAT */
396             if (altmsg
397                     && mp
398                     && !is_readonly(mp)
399                     && stat (linkpath, &st) != NOTOK
400                     && st.st_nlink == 1
401                     && (unlink (altpath) == NOTOK
402                         || link (linkpath, altpath) == NOTOK))
403                 advise (linkpath, "unable to update %s from", altmsg);
404 #endif /* HAVE_LSTAT */
405     }
406
407     /* normally, we remember which editor we used */
408     if (save_editor)
409         edsave = getcpy (*ed);
410
411     *ed = NULL;
412     if (altmsg)
413         unlink (linkpath);
414
415     return status;
416 }
417
418
419 #ifdef HAVE_LSTAT
420 static int
421 copyf (char *ifile, char *ofile)
422 {
423     int i, in, out;
424     char buffer[BUFSIZ];
425
426     if ((in = open (ifile, O_RDONLY)) == NOTOK)
427         return NOTOK;
428     if ((out = open (ofile, O_WRONLY | O_TRUNC)) == NOTOK) {
429         admonish (ofile, "unable to open and truncate");
430         close (in);
431         return NOTOK;
432     }
433
434     while ((i = read (in, buffer, sizeof(buffer))) > OK)
435         if (write (out, buffer, i) != i) {
436             advise (ofile, "may have damaged");
437             i = NOTOK;
438             break;
439         }
440
441     close (in);
442     close (out);
443     return i;
444 }
445 #endif /* HAVE_LSTAT */
446
447
448 /*
449  * SEND
450  */
451
452 static int
453 sendfile (char **arg, char *file, int pushsw)
454 {
455     pid_t child_id;
456     int i, vecp;
457     char *cp, *sp, *vec[MAXARGS];
458
459     /* Translate MIME composition file, if necessary */
460     if ((cp = context_find ("automimeproc"))
461             && (!strcmp (cp, "1"))
462             && !getenv ("NOMHNPROC")
463             && check_draft (file)
464             && (buildfile (NULL, file) == NOTOK))
465         return 0;
466
467     /* For backwards compatibility */
468     if ((cp = context_find ("automhnproc"))
469             && !getenv ("NOMHNPROC")
470             && check_draft (file)
471             && (i = editfile (&cp, NULL, file, NOUSE, NULL, NULL, NULL, 0)))
472         return 0;
473
474     /*
475      * If the sendproc is the nmh command `send', then we call
476      * those routines directly rather than exec'ing the command.
477      */
478     if (strcmp (sp = r1bindex (sendproc, '/'), "send") == 0) {
479         cp = invo_name;
480         sendit (invo_name = sp, arg, file, pushsw);
481         invo_name = cp;
482         return 1;
483     }
484
485     context_save ();    /* save the context file */
486     fflush (stdout);
487
488     for (i = 0; (child_id = vfork()) == NOTOK && i < 5; i++)
489         sleep (5);
490     switch (child_id) {
491         case NOTOK: 
492             advise (NULL, "unable to fork, so sending directly...");
493         case OK: 
494             vecp = 0;
495             vec[vecp++] = invo_name;
496             if (pushsw)
497                 vec[vecp++] = "-push";
498             if (arg)
499                 while (*arg)
500                     vec[vecp++] = *arg++;
501             vec[vecp++] = file;
502             vec[vecp] = NULL;
503
504             execvp (sendproc, vec);
505             fprintf (stderr, "unable to exec ");
506             perror (sendproc);
507             _exit (-1);
508
509         default: 
510             if (pidwait(child_id, OK) == 0)
511                 done (0);
512             return 1;
513     }
514 }
515
516
517 /*
518  * Translate MIME composition file (call buildmimeproc)
519  */
520
521 static int
522 buildfile (char **argp, char *file)
523 {
524     int i;
525     char **args, *ed;
526
527     ed = buildmimeproc;
528
529     /* allocate space for arguments */
530     i = 0;
531     if (argp) {
532         while (argp[i])
533             i++;
534     }
535     if ((args = (char **) malloc((i + 2) * sizeof(char *))) == NULL)
536         adios (NULL, "unable to malloc memory");
537
538     /*
539      * For backward compatibility, we need to add -build
540      * if we are using mhn as buildmimeproc
541      */
542     i = 0;
543     if (strcmp (r1bindex (ed, '/'), "mhn") == 0)
544         args[i++] = "-build";
545
546     /* copy any other arguments */
547     while (argp && *argp)
548         args[i++] = *argp++;
549     args[i] = NULL;
550
551     i = editfile (&ed, args, file, NOUSE, NULL, NULL, NULL, 0);
552     free (args);
553
554     return (i ? NOTOK : OK);
555 }
556
557
558 /*
559  *  Check if draft is a mhbuild composition file
560  */
561
562 static int
563 check_draft (char *msgnam)
564 {
565     int state;
566     char buf[BUFSIZ], name[NAMESZ];
567     FILE *fp;
568
569     if ((fp = fopen (msgnam, "r")) == NULL)
570         return 0;
571     for (state = FLD;;)
572         switch (state = m_getfld (state, name, buf, sizeof(buf), fp)) {
573             case FLD:
574             case FLDPLUS:
575             case FLDEOF:
576                 /*
577                  * If draft already contains any of the
578                  * Content-XXX fields, then assume it already
579                  * been converted.
580                  */
581                 if (uprf (name, XXX_FIELD_PRF)) {
582                     fclose (fp);
583                     return 0;
584                 }
585                 while (state == FLDPLUS)
586                     state = m_getfld (state, name, buf, sizeof(buf), fp);
587                 break;
588
589             case BODY:
590                 do {
591                     char *bp;
592
593                     for (bp = buf; *bp; bp++)
594                         if (*bp != ' ' && *bp != '\t' && *bp != '\n') {
595                             fclose (fp);
596                             return 1;
597                         }
598
599                     state = m_getfld (state, name, buf, sizeof(buf), fp);
600                 } while (state == BODY);
601                 /* and fall... */
602
603             default:
604                 fclose (fp);
605                 return 0;
606         }
607 }
608
609
610 #ifndef CYRUS_SASL
611 # define SASLminc(a) (a)
612 #else /* CYRUS_SASL */
613 # define SASLminc(a)  0
614 #endif /* CYRUS_SASL */
615
616 static struct swit  sendswitches[] = {
617 #define ALIASW            0
618     { "alias aliasfile", 0 },
619 #define DEBUGSW           1
620     { "debug", -5 },
621 #define FILTSW            2
622     { "filter filterfile", 0 },
623 #define NFILTSW           3
624     { "nofilter", 0 },
625 #define FRMTSW            4
626     { "format", 0 },
627 #define NFRMTSW           5
628     { "noformat", 0 },
629 #define FORWSW            6
630     { "forward", 0 },
631 #define NFORWSW           7
632     { "noforward", 0 },
633 #define MIMESW            8
634     { "mime", 0 },
635 #define NMIMESW           9
636     { "nomime", 0 },
637 #define MSGDSW           10
638     { "msgid", 0 },
639 #define NMSGDSW          11
640     { "nomsgid", 0 },
641 #define SPSHSW           12
642     { "push", 0 },
643 #define NSPSHSW          13
644     { "nopush", 0 },
645 #define SPLITSW          14
646     { "split seconds", 0 },
647 #define UNIQSW           15
648     { "unique", -6 },
649 #define NUNIQSW          16
650     { "nounique", -8 },
651 #define VERBSW           17
652     { "verbose", 0 },
653 #define NVERBSW          18
654     { "noverbose", 0 },
655 #define WATCSW           19
656     { "watch", 0 },
657 #define NWATCSW          20
658     { "nowatch", 0 },
659 #define WIDTHSW          21
660     { "width columns", 0 },
661 #define SVERSIONSW       22
662     { "version", 0 },
663 #define SHELPSW          23
664     { "help", 0 },
665 #define BITSTUFFSW       24
666     { "dashstuffing", -12 },
667 #define NBITSTUFFSW      25
668     { "nodashstuffing", -14 },
669 #define MAILSW           26
670     { "mail", -4 },
671 #define SAMLSW           27
672     { "saml", -4 },
673 #define SSNDSW           28
674     { "send", -4 },
675 #define SOMLSW           29
676     { "soml", -4 },
677 #define CLIESW           30
678     { "client host", -6 },
679 #define SERVSW           31
680     { "server host", -6 },
681 #define SNOOPSW          32
682     { "snoop", -5 },
683 #define SDRFSW           33
684     { "draftfolder +folder", -6 },
685 #define SDRMSW           34
686     { "draftmessage msg", -6 },
687 #define SNDRFSW          35
688     { "nodraftfolder", -3 },
689 #define SASLSW           36
690     { "sasl", SASLminc(-4) },
691 #define SASLMECHSW       37
692     { "saslmech", SASLminc(-5) },
693 #define USERSW           38
694     { "user", SASLminc(-4) },
695     { NULL, 0 }
696 };
697
698
699 extern int debugsw;             /* from sendsbr.c */
700 extern int forwsw;
701 extern int inplace;
702 extern int pushsw;
703 extern int splitsw;
704 extern int unique;
705 extern int verbsw;
706
707 extern char *altmsg;            /*  .. */
708 extern char *annotext;
709 extern char *distfile;
710
711
712 static void
713 sendit (char *sp, char **arg, char *file, int pushed)
714 {
715     int vecp, n = 1;
716     char *cp, buf[BUFSIZ], **argp;
717     char **arguments, *vec[MAXARGS];
718     struct stat st;
719
720 #ifndef lint
721     int distsw = 0;
722 #endif
723 #ifdef UCI
724     FILE *fp;
725 #endif
726
727     /*
728      * Make sure these are defined.  In particular, we need
729      * vec[1] to be NULL, in case "arg" is NULL below.  It
730      * doesn't matter what is the value of vec[0], but we
731      * set it to NULL, to help catch "off-by-one" errors.
732      */
733     vec[0] = NULL;
734     vec[1] = NULL;
735
736     /*
737      * Temporarily copy arg to vec, since the brkstring() call in
738      * getarguments() will wipe it out before it is merged in.
739      * Also, we skip the first element of vec, since getarguments()
740      * skips it.  Then we count the number of arguments
741      * copied.  The value of "n" will be one greater than
742      * this in order to simulate the standard argc/argv.
743      */
744     if (arg) {
745         char **bp;
746
747         copyip (arg, vec+1, MAXARGS-1);
748         bp = vec+1;
749         while (*bp++)
750             n++;
751     }
752
753     /*
754      * Merge any arguments from command line (now in vec)
755      * and arguments from profile.
756      */
757     arguments = getarguments (sp, n, vec, 1);
758     argp = arguments;
759
760     debugsw = 0;
761     forwsw = 1;
762     inplace = 1;
763     unique = 0;
764
765     altmsg = NULL;
766     annotext = NULL;
767     distfile = NULL;
768
769     vecp = 1;                   /* we'll get the zero'th element later */
770     vec[vecp++] = "-library";
771     vec[vecp++] = getcpy (m_maildir (""));
772
773     while ((cp = *argp++)) {
774         if (*cp == '-') {
775             switch (smatch (++cp, sendswitches)) {
776                 case AMBIGSW: 
777                     ambigsw (cp, sendswitches);
778                     return;
779                 case UNKWNSW: 
780                     advise (NULL, "-%s unknown\n", cp);
781                     return;
782
783                 case SHELPSW: 
784                     snprintf (buf, sizeof(buf), "%s [switches]", sp);
785                     print_help (buf, sendswitches, 1);
786                     return;
787                 case SVERSIONSW:
788                     print_version (invo_name);
789                     return;
790
791                 case SPSHSW: 
792                     pushed++;
793                     continue;
794                 case NSPSHSW: 
795                     pushed = 0;
796                     continue;
797
798                 case SPLITSW: 
799                     if (!(cp = *argp++) || sscanf (cp, "%d", &splitsw) != 1) {
800                         advise (NULL, "missing argument to %s", argp[-2]);
801                         return;
802                     }
803                     continue;
804
805                 case UNIQSW: 
806                     unique++;
807                     continue;
808                 case NUNIQSW: 
809                     unique = 0;
810                     continue;
811                 case FORWSW: 
812                     forwsw++;
813                     continue;
814                 case NFORWSW: 
815                     forwsw = 0;
816                     continue;
817
818                 case VERBSW: 
819                     verbsw++;
820                     vec[vecp++] = --cp;
821                     continue;
822                 case NVERBSW:
823                     verbsw = 0;
824                     vec[vecp++] = --cp;
825                     continue;
826
827                 case DEBUGSW: 
828                     debugsw++;  /* fall */
829                 case NFILTSW: 
830                 case FRMTSW: 
831                 case NFRMTSW: 
832                 case BITSTUFFSW:
833                 case NBITSTUFFSW:
834                 case MIMESW: 
835                 case NMIMESW: 
836                 case MSGDSW: 
837                 case NMSGDSW: 
838                 case WATCSW: 
839                 case NWATCSW: 
840                 case MAILSW: 
841                 case SAMLSW: 
842                 case SSNDSW: 
843                 case SOMLSW: 
844                 case SNOOPSW: 
845                 case SASLSW:
846                     vec[vecp++] = --cp;
847                     continue;
848
849                 case ALIASW: 
850                 case FILTSW: 
851                 case WIDTHSW: 
852                 case CLIESW: 
853                 case SERVSW: 
854                 case SASLMECHSW:
855                 case USERSW:
856                     vec[vecp++] = --cp;
857                     if (!(cp = *argp++) || *cp == '-') {
858                         advise (NULL, "missing argument to %s", argp[-2]);
859                         return;
860                     }
861                     vec[vecp++] = cp;
862                     continue;
863
864                 case SDRFSW: 
865                 case SDRMSW: 
866                     if (!(cp = *argp++) || *cp == '-') {
867                         advise (NULL, "missing argument to %s", argp[-2]);
868                         return;
869                     }
870                 case SNDRFSW: 
871                     continue;
872             }
873         }
874         advise (NULL, "usage: %s [switches]", sp);
875         return;
876     }
877
878     /* allow Aliasfile: profile entry */
879     if ((cp = context_find ("Aliasfile"))) {
880         char **ap, *dp;
881
882         dp = getcpy (cp);
883         for (ap = brkstring (dp, " ", "\n"); ap && *ap; ap++) {
884             vec[vecp++] = "-alias";
885             vec[vecp++] = *ap;
886         }
887     }
888
889     if ((cp = getenv ("SIGNATURE")) == NULL || *cp == 0)
890         if ((cp = context_find ("signature")) && *cp)
891             m_putenv ("SIGNATURE", cp);
892 #ifdef UCI
893         else {
894             snprintf (buf, sizeof(buf), "%s/.signature", mypath);
895             if ((fp = fopen (buf, "r")) != NULL
896                 && fgets (buf, sizeof(buf), fp) != NULL) {
897                     fclose (fp);
898                     if (cp = strchr (buf, '\n'))
899                         *cp = 0;
900                     m_putenv ("SIGNATURE", buf);
901             }
902         }
903 #endif /* UCI */
904
905     if ((annotext = getenv ("mhannotate")) == NULL || *annotext == 0)
906         annotext = NULL;
907     if ((altmsg = getenv ("mhaltmsg")) == NULL || *altmsg == 0)
908         altmsg = NULL;
909     if (annotext && ((cp = getenv ("mhinplace")) != NULL && *cp != 0))
910         inplace = atoi (cp);
911
912     if ((cp = getenv ("mhdist"))
913             && *cp
914 #ifndef lint
915             && (distsw = atoi (cp))
916 #endif /* not lint */
917             && altmsg) {
918         vec[vecp++] = "-dist";
919         distfile = getcpy (m_scratch (altmsg, invo_name));
920         if (link (altmsg, distfile) == NOTOK)
921             adios (distfile, "unable to link %s to", altmsg);
922     } else {
923         distfile = NULL;
924     }
925
926     if (altmsg == NULL || stat (altmsg, &st) == NOTOK) {
927         st.st_mtime = 0;
928         st.st_dev = 0;
929         st.st_ino = 0;
930     }
931     if ((pushsw = pushed))
932         push ();
933
934     vec[0] = r1bindex (postproc, '/');
935     closefds (3);
936
937     if (sendsbr (vec, vecp, file, &st, 1) == OK)
938         done (0);
939 }
940
941 /*
942  * WHOM
943  */
944
945 static int
946 whomfile (char **arg, char *file)
947 {
948     pid_t pid;
949     int vecp;
950     char *vec[MAXARGS];
951
952     context_save ();    /* save the context file */
953     fflush (stdout);
954
955     switch (pid = vfork ()) {
956         case NOTOK: 
957             advise ("fork", "unable to");
958             return 1;
959
960         case OK: 
961             vecp = 0;
962             vec[vecp++] = r1bindex (whomproc, '/');
963             vec[vecp++] = file;
964             if (arg)
965                 while (*arg)
966                     vec[vecp++] = *arg++;
967             vec[vecp] = NULL;
968
969             execvp (whomproc, vec);
970             fprintf (stderr, "unable to exec ");
971             perror (whomproc);
972             _exit (-1);         /* NOTREACHED */
973
974         default: 
975             return (pidwait (pid, NOTOK) & 0377 ? 1 : 0);
976     }
977 }
978
979
980 /*
981  * Remove the draft file
982  */
983
984 static int
985 removefile (char *drft)
986 {
987     if (unlink (drft) == NOTOK)
988         adios (drft, "unable to unlink");
989
990     return OK;
991 }