1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2003-2021 Free Software Foundation, Inc.
3 
4    GNU Mailutils is free software; you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation; either version 3, or (at your option)
7    any later version.
8 
9    GNU Mailutils is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with GNU Mailutils.  If not, see <http://www.gnu.org/licenses/>. */
16 
17 #include <mh.h>
18 
19 typedef int (*handler_fp) (struct mh_whatnow_env *wh,
20 			   int argc, char **argv,
21 			   int *status);
22 
23 /* ********************* Auxiliary functions *********************** */
24 /* Auxiliary function for option comparisons */
25 static char
strnulcmp(const char * str,const char * pattern)26 strnulcmp (const char *str, const char *pattern)
27 {
28   return strncmp (str, pattern, strlen (str));
29 }
30 
31 /* Dispatcher functions */
32 
33 struct action_tab {
34   char *name;
35   handler_fp fp;
36 };
37 
38 static handler_fp
func(struct action_tab * p,const char * name)39 func (struct action_tab *p, const char *name)
40 {
41   int len;
42 
43   if (!name)
44     return func (p, "help");
45 
46   len = strlen (name);
47   for (; p->name; p++)
48     {
49       int min = strlen (p->name);
50       if (min > len)
51 	min = len;
52       if (strncmp (p->name, name, min) == 0)
53 	return p->fp;
54     }
55 
56   mu_error (_("%s is unknown. Hit <CR> for help"), name);
57   return NULL;
58 }
59 
60 struct helpdata {
61   char *name;
62   char *descr;
63 };
64 
65 /* Functions for printing help information */
66 
67 #define OPT_DOC_COL  29		/* column in which option text starts */
68 #define RMARGIN      79		/* right margin used for wrapping */
69 
70 static int
print_short(const char * str)71 print_short (const char *str)
72 {
73   int n;
74   const char *s;
75 
76   for (n = 0; *str; str++, n++)
77     {
78       switch (*str)
79 	{
80 	case '+':
81 	  putchar ('+');
82 	  s = _("FOLDER");
83 	  n += printf ("%s", s);
84 	  break;
85 
86 	case '<':
87 	  switch (str[1])
88 	    {
89 	    case '>':
90 	      s = _("SWITCHES");
91 	      n += printf ("%s", s) - 1;
92 	      str++;
93 	      break;
94 
95 	    case 'e':
96 	      s = _("EDITOR");
97 	      n += printf ("%s", s) - 1;
98 	      str++;
99 	      break;
100 
101 	    default:
102 	      putchar (*str);
103 	    }
104 	  break;
105 
106 	default:
107 	  putchar (*str);
108 	}
109     }
110   return n;
111 }
112 
113 static void
print_descr(int n,const char * s)114 print_descr (int n, const char *s)
115 {
116   do
117     {
118       const char *p;
119       const char *space = NULL;
120 
121       for (; n < OPT_DOC_COL; n++)
122 	putchar (' ');
123 
124       for (p = s; *p && p < s + (RMARGIN - OPT_DOC_COL); p++)
125 	if (mu_isspace (*p))
126 	  space = p;
127 
128       if (!space || p < s + (RMARGIN - OPT_DOC_COL))
129 	{
130 	  printf ("%s", s);
131 	  s += strlen (s);
132 	}
133       else
134 	{
135 	  for (; s < space; s++)
136 	    putchar (*s);
137 	  for (; *s && mu_isspace (*s); s++)
138 	    ;
139 	}
140       putchar ('\n');
141       n = 1;
142     }
143   while (*s);
144 }
145 
146 static int
_help(struct helpdata * helpdata,char * argname)147 _help (struct helpdata *helpdata, char *argname)
148 {
149   struct helpdata *p;
150 
151   printf ("%s\n", _("Options are:"));
152   if (argname == NULL || argname[0] == '?')
153     {
154       /* Short version */
155       for (p = helpdata; p->name; p++)
156 	{
157 	  printf ("  ");
158 	  print_short (p->name);
159 	  putchar ('\n');
160 	}
161     }
162   else
163     {
164       for (p = helpdata; p->name; p++)
165 	{
166 	  int n;
167 
168 	  n = printf ("  ");
169 	  n += print_short (p->name);
170 
171 	  print_descr (n+1, _(p->descr));
172 	}
173     }
174   return 0;
175 }
176 
177 /* Display the contents of the given file on the terminal */
178 static void
display_file(const char * name)179 display_file (const char *name)
180 {
181   const char *pager = mh_global_profile_get ("moreproc", getenv ("PAGER"));
182 
183   if (pager)
184     mh_spawnp (pager, name);
185   else
186     {
187       mu_stream_t stream;
188       int rc;
189       size_t n;
190       char buffer[512];
191 
192       rc = mu_file_stream_create (&stream, name, MU_STREAM_READ);
193       if (rc)
194 	{
195 	  mu_error ("mu_file_stream_create: %s", mu_strerror (rc));
196 	  return;
197 	}
198 
199       mu_stream_seek (stream, 0, MU_SEEK_SET, NULL);
200       while (mu_stream_read (stream, buffer, sizeof buffer - 1, &n) == 0
201 	     && n != 0)
202 	{
203 	  buffer[n] = '\0';
204 	  printf ("%s", buffer);
205 	}
206       mu_stream_destroy (&stream);
207     }
208 }
209 
210 static int
check_exit_status(const char * progname,int status)211 check_exit_status (const char *progname, int status)
212 {
213   if (WIFEXITED (status))
214     {
215       if (WEXITSTATUS (status))
216 	{
217 	  mu_error (_("command `%s' exited with status %d"),
218 		    progname, WEXITSTATUS(status));
219 	  return 1;
220 	}
221       return 0;
222     }
223   else if (WIFSIGNALED (status))
224     mu_error (_("command `%s' terminated on signal %d"),
225 	      progname, WTERMSIG (status));
226   else
227     mu_error (_("command `%s' terminated abnormally"), progname);
228   return 1;
229 }
230 
231 static int
invoke(const char * compname,const char * defval,int argc,char ** argv,const char * extra0,const char * extra1)232 invoke (const char *compname, const char *defval, int argc, char **argv,
233 	const char *extra0, const char *extra1)
234 {
235   int i, rc;
236   char **xargv;
237   const char *progname;
238   int status;
239 
240   progname = mh_global_profile_get (compname, defval);
241   if (!progname)
242     return -1;
243 
244   xargv = calloc (argc+3, sizeof (*xargv));
245   if (!xargv)
246     {
247       mh_err_memory (0);
248       return -1;
249     }
250 
251   xargv[0] = (char*) progname;
252   for (i = 1; i < argc; i++)
253     xargv[i] = argv[i];
254   if (extra0)
255     xargv[i++] = (char*) extra0;
256   if (extra1)
257     xargv[i++] = (char*) extra1;
258   xargv[i++] = NULL;
259   rc = mu_spawnvp (xargv[0], xargv, &status);
260   free (xargv);
261   return rc ? rc : check_exit_status (progname, status);
262 }
263 
264 struct anno_data
265 {
266   const char *field;
267   const char *value;
268   int date;
269 };
270 
271 static int
anno(void * item,void * data)272 anno (void *item, void *data)
273 {
274   struct anno_data *d = item;
275   mh_annotate (item, d->field, d->value, d->date);
276   return 0;
277 }
278 
279 static void
annotate(struct mh_whatnow_env * wh)280 annotate (struct mh_whatnow_env *wh)
281 {
282   mu_message_t msg;
283   mu_address_t addr = NULL;
284   size_t i, count;
285 
286   if (!wh->anno_field || !wh->anno_list)
287     return;
288 
289   msg = mh_file_to_message (NULL, wh->file);
290   if (!msg)
291     return;
292 
293   mh_expand_aliases (msg, &addr, NULL, NULL);
294   mu_address_get_count (addr, &count);
295   for (i = 1; i <= count; i++)
296     {
297       mu_address_t subaddr;
298 
299       if (mu_address_get_nth (addr, i, &subaddr) == 0)
300 	{
301 	  struct anno_data d;
302 
303 	  d.field = wh->anno_field;
304 	  d.date = i == 1;
305 	  if (mu_address_sget_printable (subaddr, &d.value) == 0)
306 	    mu_list_foreach (wh->anno_list, anno, &d);
307 	  mu_address_destroy (&subaddr);
308 	}
309     }
310   mu_address_destroy (&addr);
311   mu_message_destroy (&msg, NULL);
312 }
313 
314 
315 /* ************************ Shell Function ************************* */
316 
317 static int
_whatnow(struct mh_whatnow_env * wh,struct action_tab * tab)318 _whatnow (struct mh_whatnow_env *wh, struct action_tab *tab)
319 {
320   int rc, status = 0;
321   char *line = NULL;
322   size_t size = 0;
323   struct mu_wordsplit ws;
324   int wsflags = MU_WRDSF_DEFFLAGS|MU_WRDSF_COMMENT;
325 
326   wh->reedit = 0;
327   wh->last_ed = NULL;
328 
329   do
330     {
331       size_t n;
332       handler_fp fun;
333 
334       mu_printf ("%s ", wh->prompt);
335       mu_stream_flush (mu_strout);
336       rc = mu_stream_getline (mu_strin, &line, &size, &n);
337       if (rc)
338 	{
339 	  mu_error (_("cannot read input stream: %s"), mu_strerror (rc));
340 	  status = 1;
341 	  break;
342 	}
343       if (n == 0)
344 	break;
345 
346       ws.ws_comment = "#";
347       rc = mu_wordsplit (line, &ws, wsflags);
348       if (rc)
349 	{
350 	  mu_error (_("cannot split line `%s': %s"), line,
351 		    mu_wordsplit_strerror (&ws));
352 	  status = 1;
353 	  break;
354 	}
355       wsflags |= MU_WRDSF_REUSE;
356       fun = func (tab, ws.ws_wordv[0]);
357       if (fun)
358 	rc = fun (wh, ws.ws_wordc, ws.ws_wordv, &status);
359       else
360 	rc = 0;
361     }
362   while (rc == 0);
363   if (wsflags & MU_WRDSF_REUSE)
364     mu_wordsplit_free (&ws);
365   free (wh->last_ed);
366   wh->last_ed = NULL;
367   free (line);
368   return status;
369 }
370 
371 
372 /* ************************** Actions ****************************** */
373 
374 /* Display action */
375 static int
display(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)376 display (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
377 {
378   if (!wh->msg)
379     mu_error (_("no alternate message to display"));
380   else
381     display_file (wh->msg);
382   return 0;
383 }
384 
385 /* Edit action */
386 static int
edit(struct mh_whatnow_env * wh,int argc,char ** argv,int * whs)387 edit (struct mh_whatnow_env *wh, int argc, char **argv, int *whs)
388 {
389   char const *ed = wh->last_ed ? wh->last_ed : wh->editor;
390   int i, rc, status;
391   char *p;
392 
393   if (wh->reedit)
394     {
395       if (argc > 1)
396 	ed = argv[1];
397       else
398 	{
399 	  char *name;
400 	  char const *newed;
401 	  mu_asprintf (&name, "%s-next", wh->editor);
402 	  newed = mh_global_profile_get (name, NULL);
403 	  free (name);
404 	  if (newed)
405 	    ed = newed;
406 	}
407     }
408   else if (argc > 1)
409     ed = argv[1];
410 
411   if (argc > 1)
412     {
413       char **xargv;
414 
415       xargv = mu_calloc (argc+2, sizeof (*xargv));
416       xargv[0] = (char *)ed;
417       for (i = 1; i + 1 < argc; i++)
418 	xargv[i] = argv[i+1];
419       xargv[i++] = wh->file;
420       xargv[i] = NULL;
421       rc = mu_spawnvp (xargv[0], xargv, &status);
422       free (xargv);
423     }
424   else
425     {
426       struct mu_wordsplit ws;
427       extern char **environ;
428       ws.ws_env = (char const **)environ;
429       if (mu_wordsplit (ed, &ws,
430 			MU_WRDSF_QUOTE
431 			| MU_WRDSF_SQUEEZE_DELIMS
432 			| MU_WRDSF_ENV
433                         | MU_WRDSF_NOCMD))
434 	{
435 	  mu_error (_("cannot split line `%s': %s"), ed,
436 		    mu_wordsplit_strerror (&ws));
437 	  rc = MU_ERR_FAILURE;
438 	}
439       else
440 	{
441 	  char *xargv[2];
442 	  xargv[0] = wh->file;
443 	  xargv[1] = NULL;
444 	  if (mu_wordsplit_append (&ws, 1, xargv))
445 	    {
446 	      mu_error (_("cannot append arguments: %s"),
447 			mu_wordsplit_strerror (&ws));
448 	      rc = ENOMEM;
449 	    }
450 	  else
451 	    rc = mu_spawnvp (ws.ws_wordv[0], ws.ws_wordv, &status);
452 	  mu_wordsplit_free (&ws);
453 	}
454     }
455 
456   if (rc || check_exit_status (ed, status))
457     {
458       if (wh->file)
459 	mu_error (_("problems with edit--%s preserved"), wh->file);
460       else
461 	mu_error (_("problems with edit"));
462     }
463 
464   p = mu_strdup (ed);
465   free (wh->last_ed);
466   wh->last_ed = p;
467   wh->reedit = 1;
468 
469   return 0;
470 }
471 
472 /* List action */
473 static int
list(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)474 list (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
475 {
476   if (!wh->file)
477     mu_error (_("no draft file to display"));
478   else
479     display_file (wh->file);
480   return 0;
481 }
482 
483 /* Push action */
484 static int
push(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)485 push (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
486 {
487   if (invoke ("sendproc", MHBINDIR "/send", argc, argv, "-push", wh->file) == 0)
488     annotate (wh);
489   return 0;
490 }
491 
492 /* Quit action */
493 static int
quit(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)494 quit (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
495 {
496   *status = 0;
497 
498   if (wh->draftfile)
499     {
500       if (argc == 2 && strnulcmp (argv[1], "-delete") == 0)
501 	unlink (wh->draftfile);
502       else
503 	{
504 	  mu_printf (_("draft left on \"%s\"."), wh->draftfile);
505 	  if (strcmp (wh->file, wh->draftfile))
506 	    {
507 	      int rc;
508 	      rc = mu_rename_file (wh->file, wh->draftfile, MU_COPY_OVERWRITE);
509 	      if (rc)
510 		mu_error (_("can't rename %s to %s: %s"),
511 			  wh->file, wh->draftfile, mu_strerror (rc));
512 	    }
513 	}
514     }
515   mu_printf ("\n");
516   return 1;
517 }
518 
519 /* Refile action */
520 static int
refile(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)521 refile (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
522 {
523   invoke ("fileproc", MHBINDIR "/refile", argc, argv, "-file", wh->file);
524   return 0;
525 }
526 
527 /* Send action */
528 static int
call_send(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)529 call_send (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
530 {
531   if (invoke ("sendproc", MHBINDIR "/send", argc, argv, wh->file, NULL) == 0)
532     {
533       annotate (wh);
534       return 1;
535     }
536   return 0;
537 }
538 
539 /* Whom action */
540 static int
whom(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)541 whom (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
542 {
543   if (!wh->file)
544     mu_error (_("no draft file to display"));
545   else
546     mh_whom_file (wh->file,
547 		  (argc == 2
548 		   && (strcmp (argv[1], "-check") == 0
549 		       || strcmp (argv[1], "--check") == 0)));
550   return 0;
551 }
552 
553 /* Help table for whatnow */
554 static struct helpdata whatnow_helptab[] = {
555   { "display [<>]",
556     N_("List the message being distributed/replied-to on the terminal.") },
557   { "edit [<e <>]",
558     N_("Edit the message. If EDITOR is omitted use the one that was used on"
559        " the preceding round unless the profile entry \"LASTEDITOR-next\""
560        " names an alternate editor.") },
561   { "list [<>]",
562     N_("List the draft on the terminal.") },
563   { "push [<>]",
564     N_("Send the message in the background.") },
565   { "quit [-delete]",
566     N_("Terminate the session. Preserve the draft, unless -delete flag is given.") },
567   { "refile [<>] +",
568     N_("Refile the draft into the given FOLDER.") },
569   { "send [-watch] [<>]",
570     N_("Send the message. The -watch flag causes the delivery process to be "
571        "monitored. SWITCHES are passed to send program verbatim.") },
572   { "whom [-check] [<>]",
573     N_("List the addresses and verify that they are acceptable to the "
574        "transport service.") },
575   { NULL },
576 };
577 
578 /* Help action for whatnow shell */
579 static int
whatnow_help(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)580 whatnow_help (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
581 {
582   return _help (whatnow_helptab, argv[0]);
583 }
584 
585 /* Actions specific for the ``disposition'' shell */
586 
587 /* Help table for ``disposition'' shell */
588 static struct helpdata disposition_helptab[] = {
589   { "quit",           N_("Terminate the session. Preserve the draft.") },
590   { "replace",        N_("Replace the draft with the newly created one") },
591   { "use",            N_("Use this draft") },
592   { "list",           N_("List the draft on the terminal.") },
593   { "refile [<>] +",  N_("Refile the draft into the given FOLDER.") },
594   { NULL },
595 };
596 
597 /* Help action */
598 static int
disp_help(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)599 disp_help (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
600 {
601   return _help (disposition_helptab, argv[0]);
602 }
603 
604 /* Use action */
605 static int
use(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)606 use (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
607 {
608   *status = DISP_USE;
609   return 1;
610 }
611 
612 /* Replace action */
613 static int
replace(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)614 replace (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
615 {
616   *status = DISP_REPLACE;
617   return 1;
618 }
619 
620 
621 /* *********************** Interfaces ****************************** */
622 
623 /* Whatnow shell */
624 static struct action_tab whatnow_tab[] = {
625   { "help", whatnow_help },
626   { "?", whatnow_help },
627   { "display", display },
628   { "edit", edit },
629   { "list", list },
630   { "push", push },
631   { "quit", quit },
632   { "refile", refile },
633   { "send", call_send },
634   { "whom", whom },
635   { NULL }
636 };
637 
638 static void
set_default_editor(struct mh_whatnow_env * wh)639 set_default_editor (struct mh_whatnow_env *wh)
640 {
641   if (!wh->editor)
642     {
643       char *p;
644       wh->editor = mh_global_profile_get ("Editor",
645 					  (p = getenv ("VISUAL")) ?
646 					    p : (p = (getenv ("EDITOR"))) ?
647 					          p : "prompter");
648     }
649 }
650 
651 int
mh_whatnow(struct mh_whatnow_env * wh,int initial_edit)652 mh_whatnow (struct mh_whatnow_env *wh, int initial_edit)
653 {
654   set_default_editor (wh);
655 
656   if (initial_edit && wh->file)
657     {
658       char *argv[2] = { "edit", NULL };
659       int status;
660       edit (wh, 1, argv, &status);
661     }
662 
663   if (!wh->prompt)
664     wh->prompt = (char*) _("What now?");
665 
666   return _whatnow (wh, whatnow_tab);
667 }
668 
669 int
mh_whatnowproc(struct mh_whatnow_env * wh,int initial_edit,const char * prog)670 mh_whatnowproc (struct mh_whatnow_env *wh, int initial_edit, const char *prog)
671 {
672   int rc;
673   pid_t pid;
674 
675   if (wh->nowhatnowproc)
676     return 0;
677 
678   if (!prog)
679     return mh_whatnow (wh, initial_edit);
680 
681   pid = fork ();
682   if (pid == -1)
683     {
684       mu_diag_funcall (MU_DIAG_ERROR, "fork", NULL, errno);
685       return 1;
686     }
687 
688   if (pid == 0)
689     {
690       struct mu_wordsplit ws;
691 
692       if (mu_wordsplit (prog, &ws,
693 			MU_WRDSF_DEFFLAGS & ~MU_WRDSF_CESCAPES))
694 	{
695 	  mu_error (_("cannot parse command line (%s): %s"), prog,
696 		    mu_wordsplit_strerror (&ws));
697 	  _exit (127);
698 	}
699 
700       set_default_editor (wh);
701       mh_whatnow_env_to_environ (wh);
702       mu_close_fds (3);
703       execvp (ws.ws_wordv[0], ws.ws_wordv);
704       mu_diag_funcall (MU_DIAG_ERROR, "execvp", prog, errno);
705       _exit (127);
706     }
707 
708   /* Master */
709   rc = 0;
710   while (1)
711     {
712       int status;
713 
714       if (waitpid (pid, &status, 0) == (pid_t)-1)
715 	{
716 	  if (errno == EINTR)
717 	    continue;
718 	  mu_diag_funcall (MU_DIAG_ERROR, "waitpid", prog, errno);
719 	  rc = 1;
720 	}
721       break;
722     }
723   return rc;
724 }
725 
726 /* Disposition shell */
727 static struct action_tab disp_tab[] = {
728   { "help", disp_help },
729   { "?", disp_help },
730   { "quit", quit },
731   { "replace", replace },
732   { "use", use },
733   { "list", list },
734   { "refile",  refile },
735   { NULL }
736 };
737 
738 int
mh_disposition(const char * filename)739 mh_disposition (const char *filename)
740 {
741   struct mh_whatnow_env wh;
742   int rc;
743   memset (&wh, 0, sizeof (wh));
744   wh.file = mu_strdup (filename);
745   wh.prompt = (char*) _("Disposition?");
746   rc = _whatnow (&wh, disp_tab);
747   free (wh.file);
748   return rc;
749 }
750 
751 /* Use draft shell */
752 
753 /* Help table for ``use draft'' shell */
754 static struct helpdata usedraft_helptab[] = {
755   { "no",             N_("Don't use the draft.") },
756   { "yes",            N_("Use the draft.") },
757   { "list",           N_("List the draft on the terminal.") },
758   { NULL },
759 };
760 
761 static int
usedraft_help(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)762 usedraft_help (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
763 {
764   return _help (usedraft_helptab, argv[0]);
765 }
766 
767 static int
yes(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)768 yes (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
769 {
770   *status = 1;
771   return 1;
772 }
773 
774 static int
no(struct mh_whatnow_env * wh,int argc,char ** argv,int * status)775 no (struct mh_whatnow_env *wh, int argc, char **argv, int *status)
776 {
777   *status = 0;
778   return 1;
779 }
780 
781 static struct action_tab usedraft_tab[] = {
782   { "help", usedraft_help },
783   { "?", usedraft_help },
784   { "yes", yes },
785   { "no", no },
786   { "list", list },
787   { NULL }
788 };
789 
790 int
mh_usedraft(const char * filename)791 mh_usedraft (const char *filename)
792 {
793   struct mh_whatnow_env wh;
794   int rc;
795 
796   memset (&wh, 0, sizeof (wh));
797   wh.file = mu_strdup (filename);
798   mu_asprintf (&wh.prompt, _("Use \"%s\"?"), filename);
799   rc = _whatnow (&wh, usedraft_tab);
800   free (wh.prompt);
801   free (wh.file);
802   return rc;
803 }
804 
805