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