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