1 /* GNU Mailutils -- a suite of utilities for electronic mail
2    Copyright (C) 2010-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 /* MH prompter command */
18 
19 #include <mh.h>
20 #include "prompter.h"
21 
22 static char prog_doc[] = N_("Prompting editor front-end for GNU MH");
23 static char args_doc[] = N_("FILE");
24 
25 char *erase_seq;
26 char *kill_seq;
27 int prepend_option;
28 int rapid_option;
29 int doteof_option;
30 
31 static struct mu_option options[] = {
32   { "erase",         0,     N_("CHAR"), MU_OPTION_DEFAULT,
33     N_("set erase character"),
34     mu_c_string, &erase_seq },
35   { "kill",          0,     N_("CHAR"), MU_OPTION_DEFAULT,
36     N_("set kill character"),
37     mu_c_string, &kill_seq },
38   { "prepend",       0,     NULL, MU_OPTION_DEFAULT,
39     N_("prepend user input to the message body"),
40     mu_c_bool, &prepend_option },
41   { "rapid",         0,     NULL, MU_OPTION_DEFAULT,
42     N_("do not display message body"),
43     mu_c_bool, &rapid_option },
44   { "doteof",        0,     NULL, MU_OPTION_DEFAULT,
45     N_("a period on a line marks end-of-file"),
46     mu_c_bool, &doteof_option },
47   MU_OPTION_END
48 };
49 
50 static int
is_empty_string(const char * str)51 is_empty_string (const char *str)
52 {
53   if (!str)
54     return 1;
55   return mu_str_skip_class (str, MU_CTYPE_BLANK)[0] == 0;
56 }
57 
58 char *
doteof_filter(char * val)59 doteof_filter (char *val)
60 {
61   if (!val)
62     return NULL;
63   if (doteof_option && val[0] == '.')
64     {
65       if (val[1] == 0)
66 	{
67 	  free (val);
68 	  return NULL;
69 	}
70       memmove (val, val + 1, strlen (val + 1) + 1);
71     }
72   return val;
73 }
74 
75 mu_stream_t strout;
76 
77 int
main(int argc,char ** argv)78 main (int argc, char **argv)
79 {
80   int rc;
81   mu_stream_t in, tmp;
82   mu_message_t msg;
83   mu_header_t hdr;
84   mu_iterator_t itr;
85   const char *file;
86   char *newval;
87   mu_off_t size;
88   mu_body_t body;
89   mu_stream_t bstr;
90 
91   mh_getopt (&argc, &argv, options, 0, args_doc, prog_doc, NULL);
92 
93   if (argc == 0)
94     {
95       mu_error (_("file name not given"));
96       exit (1);
97     }
98   else if (argc > 1)
99     {
100       mu_error (_("too many arguments"));
101       exit (1);
102     }
103 
104   file = argv[0];
105 
106   prompter_init ();
107   if (erase_seq)
108     prompter_set_erase (erase_seq);
109   if (kill_seq)
110     prompter_set_erase (kill_seq);
111 
112   if ((rc = mu_stdio_stream_create (&strout, MU_STDOUT_FD, MU_STREAM_WRITE)))
113     {
114       mu_error (_("cannot open stdout: %s"), mu_strerror (rc));
115       return 1;
116     }
117 
118   if ((rc = mu_file_stream_create (&in, file, MU_STREAM_RDWR)))
119     {
120       mu_error (_("cannot open input file `%s': %s"),
121 		file, mu_strerror (rc));
122       return 1;
123     }
124   rc = mu_stream_to_message (in, &msg);
125   if (rc)
126     {
127       mu_error (_("input stream %s is not a message (%s)"),
128 		file, mu_strerror (rc));
129       return 1;
130     }
131 
132   if ((rc = mu_temp_stream_create (&tmp, 0)))
133     {
134       mu_error (_("Cannot open temporary stream: %s"),
135 		mu_strerror (rc));
136       return 1;
137     }
138 
139   /* Copy headers */
140   mu_message_get_header (msg, &hdr);
141   mu_header_get_iterator (hdr, &itr);
142   for (mu_iterator_first (itr); !mu_iterator_is_done (itr);
143        mu_iterator_next (itr))
144     {
145       const char *name, *val;
146 
147       mu_iterator_current_kv (itr, (const void **)&name, (void**)&val);
148       if (!is_empty_string (val))
149 	{
150 	  mu_stream_printf (tmp, "%s: %s\n", name, val);
151 	  mu_stream_printf (strout, "%s: %s\n", name, val);
152 	}
153       else
154 	{
155 	  int cont = 0;
156 	  mu_opool_t opool;
157 	  const char *prompt = name;
158 
159 	  mu_opool_create (&opool, MU_OPOOL_ENOMEMABRT);
160 	  do
161 	    {
162 	      size_t len;
163 	      char *p;
164 	      p = prompter_get_value (prompt);
165 	      if (!p)
166 		return 1;
167 	      prompt = NULL;
168 	      if (cont)
169 		{
170 		  mu_opool_append_char (opool, '\n');
171 		  if (!mu_isspace (p[0]))
172 		    mu_opool_append_char (opool, '\t');
173 		}
174 	      len = strlen (p);
175 	      if (len > 0 && p[len-1] == '\\')
176 		{
177 		  len--;
178 		  cont = 1;
179 		}
180 	      else
181 		cont = 0;
182 	      mu_opool_append (opool, p, len);
183 	      free (p);
184 	    }
185 	  while (cont);
186 
187 	  mu_opool_append_char (opool, 0);
188 	  newval = mu_opool_finish (opool, NULL);
189 	  if (!is_empty_string (newval))
190 	    mu_stream_printf (tmp, "%s: %s\n", name, newval);
191 	  mu_opool_destroy (&opool);
192 	}
193     }
194   mu_iterator_destroy (&itr);
195   mu_stream_printf (strout, "--------\n");
196   mu_stream_write (tmp, "\n", 1, NULL);
197 
198   /* Copy body */
199 
200   if (prepend_option)
201     {
202       mu_stream_printf (strout, "\n--------%s\n\n", _("Enter initial text"));
203       while ((newval = prompter_get_line ()))
204 	{
205 	  mu_stream_write (tmp, newval, strlen (newval), NULL);
206 	  free (newval);
207 	  mu_stream_write (tmp, "\n", 1, NULL);
208 	}
209     }
210 
211   mu_message_get_body (msg, &body);
212   mu_body_get_streamref (body, &bstr);
213 
214   if (!prepend_option && !rapid_option)
215     {
216       mu_stream_copy (strout, bstr, 0, NULL);
217       mu_stream_seek (bstr, 0, MU_SEEK_SET, NULL);
218     }
219 
220   mu_stream_copy (tmp, bstr, 0, NULL);
221   mu_stream_unref (bstr);
222 
223   if (!prepend_option && !rapid_option)
224     {
225       mu_stream_printf (strout, "\n--------%s\n\n", _("Enter additional text"));
226       while ((newval = prompter_get_line ()))
227 	{
228 	  mu_stream_write (tmp, newval, strlen (newval), NULL);
229 	  free (newval);
230 	  mu_stream_write (tmp, "\n", 1, NULL);
231 	}
232     }
233 
234   /* Destroy the message */
235   mu_message_destroy (&msg, mu_message_get_owner (msg));
236 
237   /* Rewind the streams and copy data back to in. */
238   mu_stream_seek (in, 0, MU_SEEK_SET, NULL);
239   mu_stream_seek (tmp, 0, MU_SEEK_SET, NULL);
240   mu_stream_copy (in, tmp, 0, &size);
241   mu_stream_truncate (in, size);
242 
243   mu_stream_destroy (&in);
244   mu_stream_destroy (&tmp);
245   mu_stream_destroy (&strout);
246 
247   prompter_done ();
248 
249   return 0;
250 }
251 
252