]> diplodocus.org Git - nmh/blob - uip/comp.c
aadc17ed978a920827deca702d30bc17713f821f
[nmh] / uip / comp.c
1 /* comp.c -- compose a message
2 *
3 * This code is Copyright (c) 2002, by the authors of nmh. See the
4 * COPYRIGHT file in the root directory of the nmh distribution for
5 * complete copyright information.
6 */
7
8 #include "h/mh.h"
9 #include "sbr/print_help.h"
10 #include "sbr/error.h"
11 #include "h/utils.h"
12 #include "h/fmt_scan.h"
13 #include "h/done.h"
14 #include "sbr/m_maildir.h"
15 #include <fcntl.h>
16
17 #define COMP_SWITCHES \
18 X("draftfolder +folder", 0, DFOLDSW) \
19 X("draftmessage msg", 0, DMSGSW) \
20 X("nodraftfolder", 0, NDFLDSW) \
21 X("editor editor", 0, EDITRSW) \
22 X("noedit", 0, NEDITSW) \
23 X("file file", 0, FILESW) \
24 X("form formfile", 0, FORMSW) \
25 X("use", 0, USESW) \
26 X("nouse", 0, NUSESW) \
27 X("whatnowproc program", 0, WHATSW) \
28 X("nowhatnowproc", 0, NWHATSW) \
29 X("build", 5, BILDSW) \
30 X("version", 0, VERSIONSW) \
31 X("help", 0, HELPSW) \
32 X("to address", 0, TOSW) \
33 X("cc address", 0, CCSW) \
34 X("from address", 0, FROMSW) \
35 X("fcc mailbox", 0, FCCSW) \
36 X("width columns", 0, WIDTHSW) \
37 X("subject text", 0, SUBJECTSW) \
38
39 #define X(sw, minchars, id) id,
40 DEFINE_SWITCH_ENUM(COMP);
41 #undef X
42
43 #define X(sw, minchars, id) { sw, minchars, id },
44 DEFINE_SWITCH_ARRAY(COMP, switches);
45 #undef X
46
47 #define DISPO_SWITCHES \
48 X("quit", 0, NOSW) \
49 X("replace", 0, YESW) \
50 X("use", 0, USELSW) \
51 X("list", 0, LISTDSW) \
52 X("refile +folder", 0, REFILSW) \
53 X("new", 0, NEWSW) \
54
55 #define X(sw, minchars, id) id,
56 DEFINE_SWITCH_ENUM(DISPO);
57 #undef X
58
59 #define X(sw, minchars, id) { sw, minchars, id },
60 DEFINE_SWITCH_ARRAY(DISPO, aqrunl);
61 #undef X
62
63 static struct swit aqrul[] = {
64 { "quit", 0, NOSW },
65 { "replace", 0, YESW },
66 { "use", 0, USELSW },
67 { "list", 0, LISTDSW },
68 { "refile", 0, REFILSW },
69 { NULL, 0, 0 }
70 };
71
72 int
73 main (int argc, char **argv)
74 {
75 int use = NOUSE;
76 bool nedit = false;
77 bool nwhat = false;
78 bool build = false;
79 int i, in = NOTOK, isdf = 0, out, dat[5], format_len = 0;
80 int outputlinelen = OUTPUTLINELEN;
81 char *cp, *cwd, *maildir, *dfolder = NULL;
82 char *ed = NULL, *file = NULL, *form = NULL;
83 char *folder = NULL, *msg = NULL, buf[BUFSIZ];
84 char *to = NULL, *from = NULL, *cc = NULL, *fcc = NULL, *dp;
85 char *subject = NULL;
86 char drft[BUFSIZ], **argp, **arguments;
87 struct msgs *mp = NULL;
88 struct format *fmt;
89 struct stat st;
90
91 if (nmh_init(argv[0], true, true)) { return 1; }
92
93 arguments = getarguments (invo_name, argc, argv, 1);
94 argp = arguments;
95
96 while ((cp = *argp++)) {
97 if (*cp == '-') {
98 switch (smatch (++cp, switches)) {
99 case AMBIGSW:
100 ambigsw (cp, switches);
101 done (1);
102 case UNKWNSW:
103 die("-%s unknown", cp);
104
105 case HELPSW:
106 snprintf (buf, sizeof(buf), "%s [+folder] [msg] [switches]",
107 invo_name);
108 print_help (buf, switches, 1);
109 done (0);
110 case VERSIONSW:
111 print_version(invo_name);
112 done (0);
113
114 case EDITRSW:
115 if (!(ed = *argp++) || *ed == '-')
116 die("missing argument to %s", argp[-2]);
117 nedit = false;
118 continue;
119 case NEDITSW:
120 nedit = true;
121 continue;
122
123 case WHATSW:
124 if (!(whatnowproc = *argp++) || *whatnowproc == '-')
125 die("missing argument to %s", argp[-2]);
126 nwhat = false;
127 continue;
128
129 case BILDSW:
130 build = true;
131 /* FALLTHRU */
132 case NWHATSW:
133 nwhat = true;
134 continue;
135
136 case FORMSW:
137 if (!(form = *argp++) || *form == '-')
138 die("missing argument to %s", argp[-2]);
139 continue;
140
141 case USESW:
142 use++;
143 continue;
144 case NUSESW:
145 use = NOUSE;
146 continue;
147
148 case FILESW: /* compatibility */
149 if (file)
150 die("only one file at a time!");
151 if (!(file = *argp++) || *file == '-')
152 die("missing argument to %s", argp[-2]);
153 isdf = NOTOK;
154 continue;
155
156 case DFOLDSW:
157 if (dfolder)
158 die("only one draft folder at a time!");
159 if (!(cp = *argp++) || *cp == '-')
160 die("missing argument to %s", argp[-2]);
161 dfolder = path (*cp == '+' || *cp == '@' ? cp + 1 : cp,
162 *cp != '@' ? TFOLDER : TSUBCWF);
163 continue;
164 case DMSGSW:
165 if (file)
166 die("only one draft message at a time!");
167 if (!(file = *argp++) || *file == '-')
168 die("missing argument to %s", argp[-2]);
169 continue;
170 case NDFLDSW:
171 dfolder = NULL;
172 isdf = NOTOK;
173 continue;
174
175 case TOSW:
176 if (!(cp = *argp++) || *cp == '-')
177 die("missing argument to %s", argp[-2]);
178 to = addlist(to, cp);
179 continue;
180
181 case CCSW:
182 if (!(cp = *argp++) || *cp == '-')
183 die("missing argument to %s", argp[-2]);
184 cc = addlist(cc, cp);
185 continue;
186
187 case FROMSW:
188 if (!(cp = *argp++) || *cp == '-')
189 die("missing argument to %s", argp[-2]);
190 from = addlist(from, cp);
191 continue;
192
193 case FCCSW:
194 if (!(cp = *argp++) || *cp == '-')
195 die("missing argument to %s", argp[-2]);
196 dp = NULL;
197 if (*cp == '@')
198 cp = dp = path(cp + 1, TSUBCWF);
199 fcc = addlist(fcc, cp);
200 free(dp);
201 continue;
202
203 case WIDTHSW:
204 if (!(cp = *argp++) || *cp == '-')
205 die("missing argument to %s", argp[-2]);
206 if ((outputlinelen = atoi(cp)) < 10)
207 die("impossible width %d", outputlinelen);
208 continue;
209
210 case SUBJECTSW:
211 if (!(cp = *argp++) || *cp == '-')
212 die("missing argument to %s", argp[-2]);
213 subject = cp;
214 continue;
215 }
216 }
217 if (*cp == '+' || *cp == '@') {
218 if (folder)
219 die("only one folder at a time!");
220 folder = pluspath (cp);
221 } else {
222 if (msg)
223 die("only one message at a time!");
224 msg = cp;
225 }
226 }
227
228 cwd = mh_xstrdup(pwd ());
229
230 if (!context_find ("path"))
231 free (path ("./", TFOLDER));
232
233 /*
234 * Check if we are using a draft folder
235 * and have specified a message in it.
236 */
237 if ((dfolder || context_find ("Draft-Folder")) && !folder && msg && !file) {
238 file = msg;
239 msg = NULL;
240 }
241 if (form && (folder || msg))
242 die("can't mix forms and folders/msgs");
243
244 cp = NULL;
245
246 if (folder || msg) {
247 /*
248 * Use a message as the "form" for the new message.
249 */
250 if (!msg)
251 msg = "cur";
252 if (!folder)
253 folder = getfolder (1);
254 maildir = m_maildir (folder);
255
256 if (chdir (maildir) == NOTOK)
257 adios (maildir, "unable to change directory to");
258
259 /* read folder and create message structure */
260 if (!(mp = folder_read (folder, 1)))
261 die("unable to read folder %s", folder);
262
263 /* check for empty folder */
264 if (mp->nummsg == 0)
265 die("no messages in %s", folder);
266
267 /* parse the message range/sequence/name and set SELECTED */
268 if (!m_convert (mp, msg))
269 done (1);
270 seq_setprev (mp); /* set the previous-sequence */
271 seq_save (mp);
272
273 if (mp->numsel > 1)
274 die("only one message at a time!");
275
276 if ((in = open (form = mh_xstrdup(m_name (mp->lowsel)), O_RDONLY)) == NOTOK)
277 adios (form, "unable to open message");
278 } else {
279 struct comp *cptr;
280
281 if (! form)
282 form = components;
283
284 cp = new_fs(form, NULL, NULL);
285 format_len = strlen(cp);
286 fmt_compile(cp, &fmt, 1);
287
288 /*
289 * Set up any components that were fed to us on the command line
290 */
291
292 if (from) {
293 cptr = fmt_findcomp("from");
294 if (cptr)
295 cptr->c_text = from;
296 }
297 if (to) {
298 cptr = fmt_findcomp("to");
299 if (cptr)
300 cptr->c_text = to;
301 }
302 if (cc) {
303 cptr = fmt_findcomp("cc");
304 if (cptr)
305 cptr->c_text = cc;
306 }
307 if (fcc) {
308 cptr = fmt_findcomp("fcc");
309 if (cptr)
310 cptr->c_text = fcc;
311 }
312 if (subject) {
313 cptr = fmt_findcomp("subject");
314 if (cptr)
315 cptr->c_text = subject;
316 }
317 }
318
319 try_it_again:
320 strncpy (drft, build ? m_maildir ("draft")
321 : m_draft (dfolder, file, use, &isdf), sizeof(drft));
322
323 /*
324 * Check if we have an existing draft
325 */
326 if (!build && (out = open (drft, O_RDONLY)) != NOTOK) {
327 i = fdcompare (in, out);
328 close (out);
329
330 /*
331 * If we have given -use flag, or if the
332 * draft is just the same as the components
333 * file, then no need to ask any questions.
334 */
335 if (use || i)
336 goto edit_it;
337
338 if (stat (drft, &st) == NOTOK)
339 adios (drft, "unable to stat");
340 printf ("Draft \"%s\" exists (%ld bytes).", drft, (long) st.st_size);
341 for (i = LISTDSW; i != YESW;) {
342 if (!(argp = read_switch_multiword ("\nDisposition? ",
343 isdf ? aqrunl : aqrul)))
344 done (1);
345 switch (i = smatch (*argp, isdf ? aqrunl : aqrul)) {
346 case NOSW:
347 done (0);
348 case NEWSW:
349 file = NULL;
350 use = NOUSE;
351 goto try_it_again;
352 case YESW:
353 break;
354 case USELSW:
355 use++;
356 goto edit_it;
357 case LISTDSW:
358 showfile (++argp, drft);
359 break;
360 case REFILSW:
361 if (refile (++argp, drft) == 0)
362 i = YESW;
363 break;
364 default:
365 inform("say what?");
366 break;
367 }
368 }
369 } else {
370 if (use)
371 adios (drft, "unable to open");
372 }
373
374 if ((out = creat (drft, m_gmprot ())) == NOTOK)
375 adios (drft, "unable to create");
376 if (cp) {
377 charstring_t scanl;
378
379 i = format_len + 1024;
380 scanl = charstring_create (i + 2);
381 dat[0] = 0;
382 dat[1] = 0;
383 dat[2] = 0;
384 dat[3] = outputlinelen;
385 dat[4] = 0;
386 fmt_scan(fmt, scanl, i, dat, NULL);
387 if (write(out, charstring_buffer (scanl),
388 charstring_bytes (scanl)) < 0) {
389 advise (drft, "write");
390 }
391 charstring_free(scanl);
392 } else {
393 cpydata (in, out, form, drft);
394 close (in);
395 }
396 close (out);
397
398 edit_it:
399 context_save (); /* save the context file */
400
401 if (nwhat)
402 done (0);
403 what_now (ed, nedit, use, drft, NULL, 0, NULL, NULL, 0, cwd, 0);
404 done (1);
405 return 1;
406 }