]> diplodocus.org Git - nmh/blob - uip/mhshow.c
ca540e60df3462887d006ffebbcece8c4b8e7562
[nmh] / uip / mhshow.c
1 /* mhshow.c -- display the contents of MIME messages
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_version.h"
10 #include "sbr/print_help.h"
11 #include "sbr/error.h"
12 #include <fcntl.h>
13 #include "h/signals.h"
14 #include "h/md5.h"
15 #include "h/mts.h"
16 #include "h/tws.h"
17 #include "h/fmt_scan.h"
18 #include "h/mime.h"
19 #include "h/mhparse.h"
20 #include "h/mhcachesbr.h"
21 #include "h/done.h"
22 #include "h/utils.h"
23 #include "mhmisc.h"
24 #include "sbr/m_maildir.h"
25 #include "sbr/m_popen.h"
26 #include "mhfree.h"
27 #include "mhshowsbr.h"
28
29 #define MHSHOW_SWITCHES \
30 X("check", 0, CHECKSW) \
31 X("nocheck", 0, NCHECKSW) \
32 X("verbose", 0, VERBSW) \
33 X("noverbose", 0, NVERBSW) \
34 X("concat", 0, CONCATSW) \
35 X("noconcat", 0, NCONCATSW) \
36 X("textonly", 0, TEXTONLYSW) \
37 X("notextonly", 0, NTEXTONLYSW) \
38 X("inlineonly", 0, INLINESW) \
39 X("noinlineonly", 0, NINLINESW) \
40 X("file file", 0, FILESW) \
41 X("form formfile", 0, FORMSW) \
42 X("header", 0, HEADSW) \
43 X("noheader", 0, NHEADSW) \
44 X("headerform formfile", 0, HEADFORMSW) \
45 X("markform formfile", 0, MARKFORMSW) \
46 X("part number", 0, PARTSW) \
47 X("type content", 0, TYPESW) \
48 X("prefer content", 0, PREFERSW) \
49 X("noprefer", 0, NPREFERSW) \
50 X("rcache policy", 0, RCACHESW) \
51 X("wcache policy", 0, WCACHESW) \
52 X("version", 0, VERSIONSW) \
53 X("help", 0, HELPSW) \
54 /* \
55 * switches for moreproc/mhlproc \
56 */ \
57 X("moreproc program", -4, PROGSW) \
58 X("nomoreproc", -3, NPROGSW) \
59 X("length lines", -4, LENSW) \
60 X("width columns", -4, WIDTHSW) \
61 /* \
62 * switches for debugging \
63 */ \
64 X("debug", -5, DEBUGSW) \
65
66 #define X(sw, minchars, id) id,
67 DEFINE_SWITCH_ENUM(MHSHOW);
68 #undef X
69
70 #define X(sw, minchars, id) { sw, minchars, id },
71 DEFINE_SWITCH_ARRAY(MHSHOW, switches);
72 #undef X
73
74
75 int debugsw = 0;
76 int verbosw = 0;
77
78 #define quitser pipeser
79
80 /*
81 * static prototypes
82 */
83 static void pipeser (int);
84
85
86 int
87 main (int argc, char **argv)
88 {
89 int msgnum, *icachesw, concatsw = -1, textonly = -1, inlineonly = -1;
90 char *cp, *file = NULL;
91 char *maildir, buf[100], **argp;
92 char **arguments;
93 struct msgs_array msgs = { 0, 0, NULL };
94 struct msgs *mp = NULL;
95 CT ct, *ctp;
96 FILE *fp;
97
98 if (nmh_init(argv[0], true, true)) { return 1; }
99
100 set_done(freects_done);
101
102 arguments = getarguments (invo_name, argc, argv, 1);
103 argp = arguments;
104
105 /*
106 * Parse arguments
107 */
108 while ((cp = *argp++)) {
109 if (*cp == '-') {
110 switch (smatch (++cp, switches)) {
111 case AMBIGSW:
112 ambigsw (cp, switches);
113 done (1);
114 case UNKWNSW:
115 die("-%s unknown", cp);
116
117 case HELPSW:
118 snprintf (buf, sizeof(buf), "%s [+folder] [msgs] [switches]",
119 invo_name);
120 print_help (buf, switches, 1);
121 done (0);
122 case VERSIONSW:
123 print_version(invo_name);
124 done (0);
125
126 case RCACHESW:
127 icachesw = &rcachesw;
128 goto do_cache;
129 case WCACHESW:
130 icachesw = &wcachesw;
131 do_cache:
132 if (!(cp = *argp++) || *cp == '-')
133 die("missing argument to %s", argp[-2]);
134 switch (*icachesw = smatch (cp, cache_policy)) {
135 case AMBIGSW:
136 ambigsw (cp, cache_policy);
137 done (1);
138 case UNKWNSW:
139 die("%s unknown", cp);
140 default:
141 break;
142 }
143 continue;
144
145 case CHECKSW:
146 checksw++;
147 continue;
148 case NCHECKSW:
149 checksw = 0;
150 continue;
151
152 case CONCATSW:
153 concatsw = 1;
154 continue;
155 case NCONCATSW:
156 concatsw = 0;
157 continue;
158 case TEXTONLYSW:
159 textonly = 1;
160 continue;
161 case NTEXTONLYSW:
162 textonly = 0;
163 continue;
164 case INLINESW:
165 inlineonly = 1;
166 continue;
167 case NINLINESW:
168 inlineonly = 0;
169 continue;
170
171 case PARTSW:
172 if (!(cp = *argp++) || *cp == '-')
173 die("missing argument to %s", argp[-2]);
174 if (npart >= NPARTS)
175 die("too many parts (starting with %s), %d max",
176 cp, NPARTS);
177 parts[npart++] = cp;
178 continue;
179
180 case TYPESW:
181 if (!(cp = *argp++) || *cp == '-')
182 die("missing argument to %s", argp[-2]);
183 if (ntype >= NTYPES)
184 die("too many types (starting with %s), %d max",
185 cp, NTYPES);
186 types[ntype++] = cp;
187 continue;
188
189 case PREFERSW:
190 if (!(cp = *argp++) || *cp == '-')
191 die("missing argument to %s", argp[-2]);
192 if (npreferred >= NPREFS)
193 die("too many preferred types (starting with %s), %d max",
194 cp, NPREFS);
195 mime_preference[npreferred].type = cp;
196 cp = strchr(cp, '/');
197 if (cp) *cp++ = '\0';
198 mime_preference[npreferred++].subtype = cp;
199 continue;
200
201 case NPREFERSW:
202 npreferred = 0;
203 continue;
204
205 case FILESW:
206 if (!(cp = *argp++) || (*cp == '-' && cp[1]))
207 die("missing argument to %s", argp[-2]);
208 file = *cp == '-' ? cp : path (cp, TFILE);
209 continue;
210
211 case FORMSW:
212 if (!(cp = *argp++) || *cp == '-')
213 die("missing argument to %s", argp[-2]);
214 free(formsw);
215 formsw = mh_xstrdup(etcpath(cp));
216 continue;
217
218 case HEADSW:
219 headersw = 1;
220 continue;
221 case NHEADSW:
222 headersw = 0;
223 continue;
224
225 case HEADFORMSW:
226 if (!(headerform = *argp++) || *headerform == '-')
227 die("missing argument to %s", argp[-2]);
228 continue;
229
230 case MARKFORMSW:
231 if (!(markerform = *argp++) || *markerform == '-')
232 die("missing argument to %s", argp[-2]);
233 continue;
234
235 /*
236 * Switches for moreproc/mhlproc
237 */
238 case PROGSW:
239 if (!(progsw = *argp++) || *progsw == '-')
240 die("missing argument to %s", argp[-2]);
241 continue;
242 case NPROGSW:
243 nomore++;
244 continue;
245
246 case LENSW:
247 case WIDTHSW:
248 if (!(cp = *argp++) || *cp == '-')
249 die("missing argument to %s", argp[-2]);
250 continue;
251
252 case VERBSW:
253 verbosw = 1;
254 continue;
255 case NVERBSW:
256 verbosw = 0;
257 continue;
258 case DEBUGSW:
259 debugsw = 1;
260 continue;
261 }
262 }
263 if (*cp == '+' || *cp == '@') {
264 if (folder)
265 die("only one folder at a time!");
266 folder = pluspath (cp);
267 } else
268 app_msgarg(&msgs, cp);
269 }
270
271 /* null terminate the list of acceptable parts/types */
272 parts[npart] = NULL;
273 types[ntype] = NULL;
274
275 /*
276 * If we had any specific parts or types specified, turn off text only
277 * content.
278 */
279
280 if (npart > 0 || ntype > 0) {
281 if (textonly == -1)
282 textonly = 0;
283 if (inlineonly == -1)
284 inlineonly = 0;
285 }
286
287 /*
288 * Check if we've specified an additional profile
289 */
290 if ((cp = getenv ("MHSHOW"))) {
291 if ((fp = fopen (cp, "r"))) {
292 readconfig(NULL, fp, cp, 0);
293 fclose (fp);
294 } else {
295 admonish ("", "unable to read $MHSHOW profile (%s)", cp);
296 }
297 }
298
299 /*
300 * Read the standard profile setup
301 */
302 if ((fp = fopen (cp = etcpath ("mhn.defaults"), "r"))) {
303 readconfig(NULL, fp, cp, 0);
304 fclose (fp);
305 }
306
307 /* Check for public cache location */
308 if ((cache_public = context_find (nmhcache)) && *cache_public != '/')
309 cache_public = NULL;
310
311 /* Check for private cache location */
312 if (!(cache_private = context_find (nmhprivcache)))
313 cache_private = ".cache";
314 cache_private = mh_xstrdup(m_maildir(cache_private));
315
316 if (!context_find ("path"))
317 free (path ("./", TFOLDER));
318
319 if (file && msgs.size)
320 die("cannot specify msg and file at same time!");
321
322 /*
323 * check if message is coming from file
324 */
325 if (file) {
326 cts = mh_xcalloc(2, sizeof *cts);
327 ctp = cts;
328
329 if ((ct = parse_mime (file)))
330 *ctp++ = ct;
331
332 headersw = 0;
333 } else {
334 /*
335 * message(s) are coming from a folder
336 */
337 if (!msgs.size)
338 app_msgarg(&msgs, "cur");
339 if (!folder)
340 folder = getfolder (1);
341 maildir = m_maildir (folder);
342
343 if (chdir (maildir) == NOTOK)
344 adios (maildir, "unable to change directory to");
345
346 /* read folder and create message structure */
347 if (!(mp = folder_read (folder, 1)))
348 die("unable to read folder %s", folder);
349
350 /* check for empty folder */
351 if (mp->nummsg == 0)
352 die("no messages in %s", folder);
353
354 /* parse all the message ranges/sequences and set SELECTED */
355 for (msgnum = 0; msgnum < msgs.size; msgnum++)
356 if (!m_convert (mp, msgs.msgs[msgnum]))
357 done (1);
358
359 /*
360 * Set the SELECT_UNSEEN bit for all the SELECTED messages,
361 * since we will use that as a tag to know which messages
362 * to remove from the "unseen" sequence.
363 */
364 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++)
365 if (is_selected(mp, msgnum))
366 set_unseen (mp, msgnum);
367
368 seq_setprev (mp); /* set the Previous-Sequence */
369 seq_setunseen (mp, 1); /* unset the Unseen-Sequence */
370
371 cts = mh_xcalloc(mp->numsel + 1, sizeof *cts);
372 ctp = cts;
373
374 /*
375 * Parse all the SELECTED messages.
376 */
377 for (msgnum = mp->lowsel; msgnum <= mp->hghsel; msgnum++) {
378 if (is_selected(mp, msgnum)) {
379 char *msgnam;
380
381 msgnam = m_name (msgnum);
382 if ((ct = parse_mime (msgnam)))
383 *ctp++ = ct;
384 }
385 }
386 }
387
388 if (!*cts)
389 done (1);
390
391 userrs = true;
392 SIGNAL (SIGQUIT, quitser);
393 SIGNAL (SIGPIPE, pipeser);
394
395 /*
396 * Get the associated umask for the relevant contents.
397 */
398 for (ctp = cts; *ctp; ctp++) {
399 struct stat st;
400
401 ct = *ctp;
402 if (type_ok (ct, 1) && !ct->c_umask) {
403 if (stat (ct->c_file, &st) != NOTOK)
404 ct->c_umask = ~(st.st_mode & 0777);
405 else
406 ct->c_umask = ~m_gmprot();
407 }
408 }
409
410 /* If reading from a folder, do some updating */
411 if (mp) {
412 context_replace (pfolder, folder);/* update current folder */
413 seq_setcur (mp, mp->hghsel); /* update current message */
414 seq_save (mp); /* synchronize sequences */
415 context_save (); /* save the context file */
416 }
417
418 if (concatsw)
419 m_popen(moreproc, 0);
420
421 /*
422 * Show the message content
423 */
424 show_all_messages (cts, concatsw, textonly, inlineonly);
425
426 /* Now free all the structures for the content */
427 for (ctp = cts; *ctp; ctp++)
428 free_content (*ctp);
429
430 free(cts);
431 cts = NULL;
432
433 if (concatsw)
434 m_pclose();
435
436 done (0);
437 return 1;
438 }
439
440
441 static void
442 pipeser (int i)
443 {
444 if (i == SIGQUIT) {
445 fflush (stdout);
446 fprintf (stderr, "\n");
447 fflush (stderr);
448 }
449
450 done (1);
451 /* NOTREACHED */
452 }