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