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