Folded fileproc to constant `refile'.
[mmh] / uip / whatnow.c
1 /*
2 ** whatnow.c -- the WhatNow shell
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 **  The inclusion of attachments is eased by
9 **  using the header field name mechanism added to anno and send.
10 **  The header field name for attachments is predefined.
11 **
12 **  Several commands have been added at the whatnow prompt:
13 **
14 **        cd [ directory ]        This option works just like the shell's
15 **                                cd command and lets the user change the
16 **                                directory from which attachments are
17 **                                taken so that long path names are not
18 **                                needed with every file.
19 **
20 **        ls [ ls-options ]       This option works just like the normal
21 **                                ls command and exists to allow the user
22 **                                to verify file names in the directory.
23 **
24 **        pwd                     This option works just like the normal
25 **                                pwd command and exists to allow the user
26 **                                to verify the directory.
27 **
28 **        attach files            This option attaches the named files to
29 **                                the draft.
30 **
31 **        alist                   This option lists the attachments on the
32 **                                draft.
33 **
34 **        detach files            This option removes attachments from the
35 **        detach -n numbers       draft.  This can be done by file name or
36 **                                by attachment number.
37 */
38
39 #include <h/mh.h>
40 #include <fcntl.h>
41 #include <signal.h>
42 #include <h/mime.h>
43 #include <h/utils.h>
44
45 static struct swit whatnowswitches[] = {
46 #define EDITRSW  0
47         { "editor editor", 0 },
48 #define NEDITSW  1
49         { "noedit", 0 },
50 #define PRMPTSW  2
51         { "prompt string", 4 },
52 #define VERSIONSW  3
53         { "version", 0 },
54 #define HELPSW  4
55         { "help", 0 },
56         { NULL, 0 }
57 };
58
59 /*
60 ** Options at the "whatnow" prompt
61 */
62 static struct swit aleqs[] = {
63 #define EDITSW  0
64         { "edit [<editor> <switches>]", 0 },
65 #define REFILEOPT  1
66         { "refile [<switches>] +folder", 0 },
67 #define DISPSW  2
68         { "display", 0 },
69 #define LISTSW  3
70         { "list", 0 },
71 #define SENDSW  4
72         { "send [<switches>]", 0 },
73 #define PUSHSW  5
74         { "push [<switches>]", 0 },
75 #define QUITSW  6
76         { "quit", 0 },
77 #define DELETESW  7
78         { "delete", 0 },
79 #define CDCMDSW  8
80         { "cd [directory]", 0 },
81 #define PWDCMDSW  9
82         { "pwd", 0 },
83 #define LSCMDSW  10
84         { "ls", 0 },
85 #define ATTACHCMDSW  11
86         { "attach", 0 },
87 #define DETACHCMDSW  12
88         { "detach [-n]", 0 },
89 #define ALISTCMDSW  13
90         { "alist", 0 },
91         { NULL, 0 }
92 };
93
94 static char *myprompt = "\nWhat now? ";
95
96 /*
97 ** static prototypes
98 */
99 static int editfile(char **, char **, char *, int, struct msgs *,
100         char *, char *, int);
101 static int sendfile(char **, char *, int);
102 static int refile(char **, char *);
103 static int removefile(char *);
104 static void writelscmd(char *, int, char **);
105 static void writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp);
106 static FILE* popen_in_dir(const char *dir, const char *cmd, const char *type);
107 static int system_in_dir(const char *dir, const char *cmd);
108
109
110 #ifdef HAVE_LSTAT
111 static int copyf(char *, char *);
112 #endif
113
114
115 int
116 main(int argc, char **argv)
117 {
118         int nedit = 0, use = 0;
119         char *cp;
120         char *ed = NULL, *drft = NULL, *msgnam = NULL;
121         char buf[BUFSIZ], prompt[BUFSIZ];
122         char **argp, **arguments;
123         struct stat st;
124         char cwd[MAXPATHLEN + 1];  /* current working directory */
125         char file[MAXPATHLEN + 1];  /* file name buffer */
126         char shell[MAXPATHLEN + 1];  /* shell response buffer */
127         FILE *f;  /* read pointer for bgnd proc */
128         int n;  /* set on -n to detach command */
129
130 #ifdef LOCALE
131         setlocale(LC_ALL, "");
132 #endif
133
134         invo_name = mhbasename(argv[0]);
135
136         /* read user profile/context */
137         context_read();
138
139         arguments = getarguments(invo_name, argc, argv, 1);
140         argp = arguments;
141
142         /*
143         ** Get the initial current working directory.
144         */
145
146         if (!getcwd(cwd, sizeof (cwd))) {
147                 adios("getcwd", "could not get working directory");
148         }
149
150         while ((cp = *argp++)) {
151                 if (*cp == '-') {
152                         switch (smatch(++cp, whatnowswitches)) {
153                         case AMBIGSW:
154                                 ambigsw(cp, whatnowswitches);
155                                 done(1);
156                         case UNKWNSW:
157                                 adios(NULL, "-%s unknown", cp);
158
159                         case HELPSW:
160                                 snprintf(buf, sizeof(buf),
161                                                 "%s [switches] [file]",
162                                                 invo_name);
163                                 print_help(buf, whatnowswitches, 1);
164                                 done(1);
165                         case VERSIONSW:
166                                 print_version(invo_name);
167                                 done(1);
168
169                         case EDITRSW:
170                                 if (!(ed = *argp++) || *ed == '-')
171                                         adios(NULL, "missing argument to %s",
172                                                         argp[-2]);
173                                 nedit = 0;
174                                 continue;
175                         case NEDITSW:
176                                 nedit++;
177                                 continue;
178
179                         case PRMPTSW:
180                                 if (!(myprompt = *argp++) || *myprompt == '-')
181                                         adios(NULL, "missing argument to %s",
182                                                         argp[-2]);
183                                 continue;
184
185                         }
186                 }
187                 if (drft)
188                         adios(NULL, "only one draft at a time!");
189                 else
190                         drft = cp;
191         }
192
193         if ((!drft && !(drft = getenv("mhdraft"))) || !*drft)
194                 drft = getcpy(m_draft(seq_cur));
195
196         msgnam = (cp = getenv("mhaltmsg")) && *cp ? getcpy(cp) : NULL;
197
198         if ((cp = getenv("mhuse")) && *cp)
199                 use = atoi(cp);
200
201         if (!ed && (!(ed = getenv("mheditor")) || !*ed)) {
202                 ed = NULL;
203                 nedit++;
204         }
205
206         /* start editing the draft, unless -noedit was given */
207         if (!nedit && editfile(&ed, NULL, drft, use, NULL, msgnam, NULL, 1)
208                         < 0)
209                 done(1);
210
211         snprintf(prompt, sizeof(prompt), myprompt, invo_name);
212         for (;;) {
213                 if (!(argp = getans(prompt, aleqs))) {
214                         unlink(altmsglink);
215                         done(1);
216                 }
217                 switch (smatch(*argp, aleqs)) {
218                 case DISPSW:
219                         /* display the msg being replied to or distributed */
220                         if (msgnam) {
221                                 snprintf(buf, sizeof buf, "%s '%s'",
222                                                 listproc, msgnam);
223                                 system(buf);
224                         } else {
225                                 advise(NULL, "no alternate message to display");
226                         }
227                         break;
228
229                 case EDITSW:
230                         /* Call an editor on the draft file */
231                         if (*++argp)
232                                 ed = *argp++;
233                         if (editfile(&ed, argp, drft, NOUSE, NULL,
234                                         msgnam, NULL, 1) == NOTOK)
235                                 done(1);
236                         break;
237
238                 case LISTSW:
239                         /* display the draft file */
240                         snprintf(buf, sizeof buf, "%s '%s'", listproc, drft);
241                         system(buf);
242                         break;
243
244                 case QUITSW:
245                         /* quit */
246                         if (stat(drft, &st) != NOTOK) {
247                                 advise(NULL, "draft left on %s", drft);
248                         }
249                         done(1);
250
251                 case DELETESW:
252                         /* Delete draft and exit */
253                         removefile(drft);
254                         done(1);
255
256                 case PUSHSW:
257                         /* Send draft in background */
258                         if (sendfile(++argp, drft, 1))
259                                 done(1);
260                         break;
261
262                 case SENDSW:
263                         /* Send draft */
264                         sendfile(++argp, drft, 0);
265                         break;
266
267                 case REFILEOPT:
268                         /* Refile the draft */
269                         if (refile(++argp, drft) == 0) {
270                                 done(0);
271                         }
272                         break;
273
274                 case CDCMDSW:
275                         /*
276                         ** Change the working directory for attachments
277                         **
278                         ** Run the directory through the user's shell
279                         ** so that we can take advantage of any syntax
280                         ** that the user is accustomed to.  Read back
281                         ** the absolute path.
282                         */
283
284                         if (*(argp+1) == NULL) {
285                                 sprintf(buf, "$SHELL -c \"cd;pwd\"");
286                         } else {
287                                 writesomecmd(buf, BUFSIZ, "cd", "pwd", argp);
288                         }
289                         if ((f = popen_in_dir(cwd, buf, "r"))) {
290                                 fgets(cwd, sizeof (cwd), f);
291
292                                 if (strchr(cwd, '\n'))
293                                         *strchr(cwd, '\n') = '\0';
294
295                                 pclose(f);
296                         } else {
297                                 advise("popen", "could not get directory");
298                         }
299
300                         break;
301
302                 case PWDCMDSW:
303                         /* Print the working directory for attachments */
304                         printf("%s\n", cwd);
305                         break;
306
307                 case LSCMDSW:
308                         /*
309                         ** List files in the current attachment working
310                         ** directory
311                         **
312                         ** Use the user's shell so that we can take
313                         ** advantage of any syntax that the user is
314                         ** accustomed to.
315                         */
316                         writelscmd(buf, sizeof(buf), argp);
317                         system_in_dir(cwd, buf);
318                         break;
319
320                 case ALISTCMDSW:
321                         /*
322                         ** List attachments on current draft.
323                         */
324                         snprintf(buf, sizeof buf, "anno -list -comp '%s' "
325                                         "-number all -text IGNORE '%s'",
326                                         attach_hdr, drft);
327                         if (system(buf) != 0) {
328                                 advise(NULL, "Could not list attachment headers.");
329                         }
330                         break;
331
332                 case ATTACHCMDSW:
333                         /*
334                         ** Attach files to current draft.
335                         */
336
337                         if (*(argp+1) == NULL) {
338                                 advise(NULL, "attach command requires file argument(s).");
339                                 break;
340                         }
341
342                         /*
343                         ** Build a command line that causes the user's
344                         ** shell to list the file name arguments.
345                         ** This handles and wildcard expansion, tilde
346                         ** expansion, etc.
347                         */
348                         writelscmd(buf, sizeof(buf), argp);
349
350                         /*
351                         ** Read back the response from the shell,
352                         ** which contains a number of lines with one
353                         ** file name per line.  Remove off the newline.
354                         ** Determine whether we have an absolute or
355                         ** relative path name.  Prepend the current
356                         ** working directory to relative path names.
357                         ** Add the attachment annotation to the draft.
358                         */
359                         if ((f = popen_in_dir(cwd, buf, "r"))) {
360                                 char buf[BUFSIZ];
361
362                                 while (fgets(shell, sizeof(shell), f)) {
363                                         *(strchr(shell, '\n')) = '\0';
364
365                                         if (*shell == '/')
366                                                 sprintf(file, "%s", shell);
367                                         else {
368                                                 sprintf(file, "%s/%s", cwd,
369                                                                 shell);
370                                         }
371                                         snprintf(buf, sizeof buf,
372                                                         "anno -nodate -append "
373                                                         "-comp '%s' -text '%s'"
374                                                         " '%s'",
375                                                         attach_hdr, file,
376                                                         drft);
377                                         if (system(buf) != 0) {
378                                                 advise(NULL, "Could not add attachment header.");
379                                         }
380                                 }
381
382                                 pclose(f);
383                         } else {
384                                 advise("popen", "could not get file from shell");
385                         }
386
387                         break;
388
389                 case DETACHCMDSW:
390                         /*
391                         ** Detach files from current draft.
392                         **
393                         ** Scan the arguments for a -n.  Mixed file
394                         ** names and numbers aren't allowed, so this
395                         ** catches a -n anywhere in the argument list.
396                         */
397                         for (n = 0, arguments = argp + 1;
398                                         *arguments != NULL;
399                                         arguments++) {
400                                 if (strcmp(*arguments, "-n") == 0) {
401                                                 n = 1;
402                                                 break;
403                                 }
404                         }
405
406                         /*
407                         ** A -n was found so interpret the arguments as
408                         ** attachment numbers.  Decrement any remaining
409                         ** argument number that is greater than the one
410                         ** just processed after processing each one so
411                         ** that the numbering stays correct.
412                         */
413                         if (n == 1) {
414                                 for (arguments=argp+1; *arguments;
415                                                 arguments++) {
416                                         if (strcmp(*arguments, "-n") == 0)
417                                                 continue;
418
419                                         if (**arguments != '\0') {
420                                                 char buf[BUFSIZ];
421
422                                                 n = atoi(*arguments);
423                                                 snprintf(buf, sizeof buf, "anno -delete -comp '%s' -number '%d' '%s'", attach_hdr, n, drft);
424                                                 if (system(buf) != 0) {
425                                                         advise(NULL, "Could not delete attachment header.");
426                                                 }
427
428                                                 for (argp=arguments+1; *argp;
429                                                                 argp++) {
430                                                         if (atoi(*argp) > n) {
431                                                                 if (atoi(*argp) == 1)
432                                                                         *argp = "";
433                                                                 else
434                                                                         sprintf(*argp, "%d", atoi(*argp) - 1);
435                                                         }
436                                                 }
437                                         }
438                                 }
439                                 break;
440                         }
441                         /* else */
442
443                         /*
444                         ** The arguments are interpreted as file names.
445                         ** Run them through the user's shell for wildcard
446                         ** expansion and other goodies.  Do this from
447                         ** the current working directory if the argument
448                         ** is not an absolute path name (does not begin
449                         ** with a /).
450                         **
451                         ** We feed all the file names to the shell at
452                         ** once, otherwise you can't provide a file name
453                         ** with a space in it.
454                         */
455                         writelscmd(buf, sizeof(buf), argp);
456                         if ((f = popen_in_dir(cwd, buf, "r"))) {
457                                 while (fgets(shell, sizeof (shell), f)) {
458                                         *(strchr(shell, '\n')) = '\0';
459                                         snprintf(buf, sizeof buf,
460                                                         "anno -delete -comp "
461                                                         "'%s' -text '%s' '%s'",
462                                                         attach_hdr, shell,
463                                                         drft);
464                                         if (system(buf) != 0) {
465                                                 advise(NULL, "Could not delete attachment header.");
466                                         }
467                                 }
468                                 pclose(f);
469                         } else {
470                                 advise("popen", "could not get file from shell");
471                         }
472
473                         break;
474
475                 default:
476                         /* Unknown command */
477                         advise(NULL, "say what?");
478                         break;
479                 }
480         }
481         /*NOTREACHED*/
482 }
483
484
485
486 /*
487 ** Build a command line of the form $SHELL -c "cd 'cwd'; cmd argp ... ;
488 ** trailcmd".
489 */
490 static void
491 writesomecmd(char *buf, int bufsz, char *cmd, char *trailcmd, char **argp)
492 {
493         char *cp;
494         /*
495         ** Note that we do not quote -- the argp from the user
496         ** is assumed to be quoted as they desire. (We can't treat
497         ** it as pure literal as that would prevent them using ~,
498         ** wildcards, etc.) The buffer produced by this function
499         ** should be given to popen_in_dir() or system_in_dir() so
500         ** that the current working directory is set correctly.
501         */
502         int ln = snprintf(buf, bufsz, "$SHELL -c \"%s", cmd);
503         /*
504         ** NB that some snprintf() return -1 on overflow rather than the
505         ** new C99 mandated 'number of chars that would have been written'
506         */
507         /*
508         ** length checks here and inside the loop allow for the
509         ** trailing ';', trailcmd, '"' and NUL
510         */
511         int trailln = strlen(trailcmd) + 3;
512         if (ln < 0 || ln + trailln > bufsz)
513                 adios(NULL, "arguments too long");
514
515         cp = buf + ln;
516
517         while (*++argp) {
518                 ln = strlen(*argp);
519                 /* +1 for leading space */
520                 if (ln + trailln + 1 > bufsz - (cp-buf))
521                         adios(NULL, "arguments too long");
522                 *cp++ = ' ';
523                 memcpy(cp, *argp, ln+1);
524                 cp += ln;
525         }
526         if (*trailcmd) {
527                 *cp++ = ';';
528                 strcpy(cp, trailcmd);
529                 cp += trailln - 3;
530         }
531         *cp++ = '"';
532         *cp = 0;
533 }
534
535 /*
536 ** Build a command line that causes the user's shell to list the file name
537 ** arguments.  This handles and wildcard expansion, tilde expansion, etc.
538 */
539 static void
540 writelscmd(char *buf, int bufsz, char **argp)
541 {
542         writesomecmd(buf, bufsz, "ls", "", argp);
543 }
544
545 /*
546 ** Like system(), but run the command in directory dir.
547 ** This assumes the program is single-threaded!
548 */
549 static int
550 system_in_dir(const char *dir, const char *cmd)
551 {
552         char olddir[BUFSIZ];
553         int r;
554         if (getcwd(olddir, sizeof(olddir)) == 0)
555                 adios("getcwd", "could not get working directory");
556         if (chdir(dir) != 0)
557                 adios("chdir", "could not change working directory");
558         r = system(cmd);
559         if (chdir(olddir) != 0)
560                 adios("chdir", "could not change working directory");
561         return r;
562 }
563
564 /* ditto for popen() */
565 static FILE*
566 popen_in_dir(const char *dir, const char *cmd, const char *type)
567 {
568         char olddir[BUFSIZ];
569         FILE *f;
570         if (getcwd(olddir, sizeof(olddir)) == 0)
571                 adios("getcwd", "could not get working directory");
572         if (chdir(dir) != 0)
573                 adios("chdir", "could not change working directory");
574         f = popen(cmd, type);
575         if (chdir(olddir) != 0)
576                 adios("chdir", "could not change working directory");
577         return f;
578 }
579
580
581 /*
582 ** EDIT
583 */
584
585 static int  reedit = 0;  /* have we been here before? */
586 static char *edsave = NULL;  /* the editor we used previously */
587
588
589 static int
590 editfile(char **ed, char **arg, char *file, int use, struct msgs *mp,
591         char *altmsg, char *cwd, int save_editor)
592 {
593         int pid, status, vecp;
594         char altpath[BUFSIZ], linkpath[BUFSIZ];
595         char *cp, *vec[MAXARGS];
596         struct stat st;
597
598 #ifdef HAVE_LSTAT
599         int slinked = 0;
600 #endif /* HAVE_LSTAT */
601
602         /* Was there a previous edit session? */
603         if (reedit) {
604                 if (!*ed) {  /* no explicit editor */
605                         *ed = edsave;  /* so use the previous one */
606                         if (!(cp = mhbasename(*ed)))
607                                 cp = *ed;
608
609                         /* unless we've specified it via "editor-next" */
610                         cp = concat(cp, "-next", NULL);
611                         if ((cp = context_find(cp)))
612                                 *ed = cp;
613                 }
614         } else if (!*ed) {
615                 /* set initial editor */
616                 *ed = defaulteditor;
617         }
618
619         if (altmsg) {
620                 if (!mp || *altmsg == '/' || !cwd)
621                         strncpy(altpath, altmsg, sizeof(altpath));
622                 else
623                         snprintf(altpath, sizeof(altpath), "%s/%s",
624                                         mp->foldpath, altmsg);
625                 if (!cwd)
626                         strncpy(linkpath, altmsglink, sizeof(linkpath));
627                 else
628                         snprintf(linkpath, sizeof(linkpath), "%s/%s",
629                                         cwd, altmsglink);
630                 unlink(linkpath);
631 #ifdef HAVE_LSTAT
632                 if (link(altpath, linkpath) == NOTOK) {
633                         symlink(altpath, linkpath);
634                         slinked = 1;
635                 } else {
636                         slinked = 0;
637                 }
638 #else /* not HAVE_LSTAT */
639                 link(altpath, linkpath);
640 #endif /* not HAVE_LSTAT */
641         }
642
643         context_save();  /* save the context file */
644         fflush(stdout);
645
646         switch (pid = fork()) {
647         case NOTOK:
648                 advise("fork", "unable to");
649                 status = NOTOK;
650                 break;
651
652         case OK:
653                 if (cwd)
654                         chdir(cwd);
655                 if (altmsg) {
656                         if (mp)
657                                 m_putenv("mhfolder", mp->foldpath);
658                         m_putenv("editalt", altpath);
659                 }
660
661                 vecp = 0;
662                 vec[vecp++] = mhbasename(*ed);
663                 if (arg)
664                         while (*arg)
665                                 vec[vecp++] = *arg++;
666                 vec[vecp++] = file;
667                 vec[vecp] = NULL;
668
669                 execvp(*ed, vec);
670                 fprintf(stderr, "unable to exec ");
671                 perror(*ed);
672                 _exit(-1);
673
674         default:
675                 if ((status = pidwait(pid, NOTOK))) {
676                         if (((status & 0xff00) != 0xff00) &&
677                                         (!reedit || (status & 0x00ff))) {
678                                 if (!use && (status & 0xff00) && (rename(file, cp = m_backup(file)) != NOTOK)) {
679                                         advise(NULL, "problems with edit--draft left in %s", cp);
680                                 } else {
681                                         advise(NULL, "problems with edit--%s preserved", file);
682                                 }
683                         }
684                         status = -2;  /* maybe "reedit ? -2 : -1"? */
685                         break;
686                 }
687
688                 reedit++;
689 #ifdef HAVE_LSTAT
690                 if (altmsg && mp && !is_readonly(mp) && (slinked ?
691                                 lstat (linkpath, &st) != NOTOK &&
692                                 S_ISREG(st.st_mode) &&
693                                 copyf(linkpath, altpath) == NOTOK :
694                                 stat(linkpath, &st) != NOTOK &&
695                                 st.st_nlink == 1 &&
696                                 (unlink(altpath) == NOTOK ||
697                                 link(linkpath, altpath) == NOTOK)))
698                         advise(linkpath, "unable to update %s from", altmsg);
699 #else /* HAVE_LSTAT */
700                 if (altmsg && mp && !is_readonly(mp) &&
701                                 stat(linkpath, &st) != NOTOK &&
702                                 st.st_nlink == 1 &&
703                                 (unlink(altpath) == NOTOK ||
704                                 link(linkpath, altpath) == NOTOK))
705                         advise(linkpath, "unable to update %s from", altmsg);
706 #endif /* HAVE_LSTAT */
707         }
708
709         /* normally, we remember which editor we used */
710         if (save_editor)
711                 edsave = getcpy(*ed);
712
713         *ed = NULL;
714         if (altmsg)
715                 unlink(linkpath);
716
717         return status;
718 }
719
720
721 #ifdef HAVE_LSTAT
722 static int
723 copyf(char *ifile, char *ofile)
724 {
725         int i, in, out;
726         char buffer[BUFSIZ];
727
728         if ((in = open(ifile, O_RDONLY)) == NOTOK)
729                 return NOTOK;
730         if ((out = open(ofile, O_WRONLY | O_TRUNC)) == NOTOK) {
731                 admonish(ofile, "unable to open and truncate");
732                 close(in);
733                 return NOTOK;
734         }
735
736         while ((i = read(in, buffer, sizeof(buffer))) > OK)
737                 if (write(out, buffer, i) != i) {
738                         advise(ofile, "may have damaged");
739                         i = NOTOK;
740                         break;
741                 }
742
743         close(in);
744         close(out);
745         return i;
746 }
747 #endif /* HAVE_LSTAT */
748
749
750 /*
751 ** SEND
752 */
753
754 static int
755 sendfile(char **arg, char *file, int pushsw)
756 {
757         pid_t child_id;
758         int vecp;
759         char *vec[MAXARGS];
760
761         context_save();  /* save the context file */
762         fflush(stdout);
763
764         switch (child_id = fork()) {
765         case NOTOK:
766                 advise(NULL, "unable to fork, so sending directly...");
767                 /* fall */
768         case OK:
769                 vecp = 0;
770                 vec[vecp++] = invo_name;
771                 if (pushsw)
772                         vec[vecp++] = "-push";
773                 if (arg)
774                         while (*arg)
775                                 vec[vecp++] = *arg++;
776                 vec[vecp++] = file;
777                 vec[vecp] = NULL;
778
779                 execvp("send", vec);
780                 fprintf(stderr, "unable to exec ");
781                 perror("send");
782                 _exit(-1);
783
784         default:
785                 if (pidwait(child_id, OK) == 0)
786                         done(0);
787                 return 1;
788         }
789 }
790
791
792 /*
793 ** refile msg into another folder
794 */
795 static int
796 refile(char **arg, char *file)
797 {
798         pid_t pid;
799         register int vecp;
800         char *vec[MAXARGS];
801
802         vecp = 0;
803         vec[vecp++] = "refile";
804         vec[vecp++] = "-nolink";  /* override bad .mh_profile defaults */
805         vec[vecp++] = "-file";
806         vec[vecp++] = file;
807
808         while (arg && *arg) {
809                 vec[vecp++] = *arg++;
810         }
811         vec[vecp] = NULL;
812
813         context_save();  /* save the context file */
814         fflush(stdout);
815
816         switch (pid = fork()) {
817         case -1:
818                 advise("fork", "unable to");
819                 return -1;
820
821         case 0:
822                 execvp(*vec, vec);
823                 fprintf(stderr, "unable to exec ");
824                 perror(*vec);
825                 _exit(-1);
826
827         default:
828                 return (pidwait(pid, -1));
829         }
830 }
831
832
833 /*
834 ** Remove the draft file
835 */
836
837 static int
838 removefile(char *drft)
839 {
840         if (unlink(drft) == NOTOK)
841                 adios(drft, "unable to unlink");
842
843         return OK;
844 }