X("nocheck", 0, NCHECKSW) \
X("verbose", 0, VERBSW) \
X("noverbose", 0, NVERBSW) \
+ X("concat", 0, CONCATSW) \
+ X("noconcat", 0, NCONCATSW) \
+ X("textonly", 0, TEXTONLYSW) \
+ X("notextonly", 0, NTEXTONLYSW) \
+ X("inlineonly", 0, INLINESW) \
+ X("noinlineonly", 0, NINLINESW) \
X("file file", 0, FILESW) \
X("form formfile", 0, FORMSW) \
+ X("markform formfile", 0, MARKFORMSW) \
X("part number", 0, PARTSW) \
X("type content", 0, TYPESW) \
X("rcache policy", 0, RCACHESW) \
int type_ok (CT, int);
void flush_errors (void);
-/* mhshowsbr.c */
-void show_all_messages (CT *);
-
/* mhfree.c */
extern CT *cts;
void freects_done (int) NORETURN;
int
main (int argc, char **argv)
{
- int msgnum, *icachesw;
- char *cp, *file = NULL, *folder = NULL;
+ int msgnum, *icachesw, concatsw = -1, textonly = -1, inlineonly = -1;
+ char *cp, *file = NULL, *folder = NULL, *markform = NULL;
char *maildir, buf[100], **argp;
char **arguments;
struct msgs_array msgs = { 0, 0, NULL };
checksw = 0;
continue;
+ case CONCATSW:
+ concatsw = 1;
+ continue;
+ case NCONCATSW:
+ concatsw = 0;
+ continue;
+ case TEXTONLYSW:
+ textonly = 1;
+ continue;
+ case NTEXTONLYSW:
+ textonly = 0;
+ continue;
+ case INLINESW:
+ inlineonly = 1;
+ continue;
+ case NINLINESW:
+ inlineonly = 1;
+ continue;
+
case PARTSW:
if (!(cp = *argp++) || *cp == '-')
adios (NULL, "missing argument to %s", argp[-2]);
formsw = getcpy (etcpath (cp));
continue;
+ case MARKFORMSW:
+ if (!(markform = *argp++) || *markform == '-')
+ adios (NULL, "missing argument to %s", argp[-2]);
+ continue;
+
/*
* Switches for moreproc/mhlproc
*/
parts[npart] = NULL;
types[ntype] = NULL;
+ /*
+ * If we had any specific parts or types specified, turn off text only
+ * content.
+ */
+
+ if (npart > 0 || ntype > 0) {
+ if (textonly == -1)
+ textonly = 0;
+ if (inlineonly == -1)
+ inlineonly = 0;
+ }
+
/*
* Check if we've specified an additional profile
*/
context_save (); /* save the context file */
}
+ if (concatsw)
+ m_popen(moreproc, 0);
+
/*
* Show the message content
*/
- show_all_messages (cts);
+ show_all_messages (cts, concatsw, textonly, inlineonly, markform);
/* Now free all the structures for the content */
for (ctp = cts; *ctp; ctp++)
free ((char *) cts);
cts = NULL;
+ if (concatsw)
+ m_pclose();
+
done (0);
return 1;
}
#include <h/tws.h>
#include <h/mime.h>
#include <h/mhparse.h>
+#include <h/fmt_scan.h>
#include <h/utils.h>
#ifdef HAVE_ICONV
# include <iconv.h>
/*
* prototypes
*/
-void show_all_messages (CT *);
int show_content_aux (CT, int, char *, char *);
/*
* static prototypes
*/
-static void show_single_message (CT, char *);
-static void DisplayMsgHeader (CT, char *);
-static int show_switch (CT, int);
-static int show_content (CT, int);
+static void show_single_message (CT, char *, int, int, int, struct format *);
+static void DisplayMsgHeader (CT, char *, int);
+static int show_switch (CT, int, int, int, int, struct format *);
+static int show_content (CT, int, int, int, struct format *fmt);
static int show_content_aux2 (CT, int, char *, char *, int, int, int);
-static int show_text (CT, int);
-static int show_multi (CT, int);
-static int show_multi_internal (CT, int);
+static int show_text (CT, int, int);
+static int show_multi (CT, int, int, int, int, struct format *);
+static int show_multi_internal (CT, int, int, int, int, struct format *fmt);
static int show_multi_aux (CT, int, char *);
static int show_message_rfc822 (CT, int);
static int show_partial (CT, int);
-static int show_external (CT, int);
+static int show_external (CT, int, int, int, int, struct format *);
static int parse_display_string (CT, char *, int *, int *, char *, char *,
size_t, int multipart);
static int convert_content_charset (CT, char **);
+static struct format *compile_marker(char *);
+static void output_marker (CT, struct format *);
+static void free_markercomps (void);
static int pidcheck(int);
+/*
+ * Components (and list of parameters/components) we care about for the
+ * content marker display.
+ */
+
+static struct comp *part_comp = NULL;
+static struct comp *ctype_comp = NULL;
+static struct comp *description_comp = NULL;
+static struct comp *dispo_comp = NULL;
+
+struct param_comp_list {
+ char *param;
+ struct comp *comp;
+ struct param_comp_list *next;
+};
+
+static struct param_comp_list *ctype_pc_list = NULL;
+static struct param_comp_list *dispo_pc_list = NULL;
+
/*
* Top level entry point to show/display a group of messages
*/
void
-show_all_messages (CT *cts)
+show_all_messages (CT *cts, int concatsw, int textonly, int inlineonly,
+ char *markerform)
{
CT ct, *ctp;
+ struct format *fmt;
/*
* If form is not specified, then get default form
if (!formsw)
formsw = getcpy (etcpath ("mhl.headers"));
+ /*
+ * Compile the content marker format line
+ */
+ fmt = compile_marker(markerform);
+
/*
* If form is "mhl.null", suppress display of header.
*/
/* if top-level type is ok, then display message */
if (type_ok (ct, 1))
- show_single_message (ct, formsw);
+ show_single_message (ct, formsw, concatsw, textonly, inlineonly,
+ fmt);
}
+
+ free_markercomps();
+ fmt_free(fmt, 1);
}
*/
static void
-show_single_message (CT ct, char *form)
+show_single_message (CT ct, char *form, int concatsw, int textonly,
+ int inlineonly, struct format *fmt)
{
sigset_t set, oset;
* the message headers.
*/
if (form)
- DisplayMsgHeader(ct, form);
+ DisplayMsgHeader(ct, form, concatsw);
/* Show the body of the message */
- show_switch (ct, 0);
+ show_switch (ct, 0, concatsw, textonly, inlineonly, fmt);
if (ct->c_fp) {
fclose (ct->c_fp);
sigaddset (&set, SIGTERM);
sigprocmask (SIG_BLOCK, &set, &oset);
- while (wait (&status) != NOTOK) {
+ while (!concatsw && wait (&status) != NOTOK) {
pidcheck (status);
continue;
}
*/
static void
-DisplayMsgHeader (CT ct, char *form)
+DisplayMsgHeader (CT ct, char *form, int concatsw)
{
pid_t child_id;
int i, vecp;
* If we've specified -(no)moreproc,
* then just pass that along.
*/
- if (nomore) {
+ if (nomore || concatsw) {
vec[vecp++] = getcpy("-nomoreproc");
} else if (progsw) {
vec[vecp++] = getcpy("-moreproc");
*/
static int
-show_switch (CT ct, int alternate)
+show_switch (CT ct, int alternate, int concatsw, int textonly, int inlineonly,
+ struct format *fmt)
{
switch (ct->c_type) {
case CT_MULTIPART:
- return show_multi (ct, alternate);
+ return show_multi (ct, alternate, concatsw, textonly,
+ inlineonly, fmt);
case CT_MESSAGE:
switch (ct->c_subtype) {
return show_partial (ct, alternate);
case MESSAGE_EXTERNAL:
- return show_external (ct, alternate);
+ return show_external (ct, alternate, concatsw, textonly,
+ inlineonly, fmt);
case MESSAGE_RFC822:
default:
}
case CT_TEXT:
- return show_text (ct, alternate);
+ return show_text (ct, alternate, concatsw);
case CT_AUDIO:
case CT_IMAGE:
case CT_VIDEO:
case CT_APPLICATION:
- return show_content (ct, alternate);
+ return show_content (ct, alternate, textonly, inlineonly, fmt);
default:
adios (NULL, "unknown content type %d", ct->c_type);
*/
static int
-show_content (CT ct, int alternate)
+show_content (CT ct, int alternate, int textonly, int inlineonly,
+ struct format *fmt)
{
char *cp, buffer[BUFSIZ];
CI ci = &ct->c_ctinfo;
+ /*
+ * If we're here, we are not a text type. So we don't need to check
+ * the content-type.
+ */
+
+ if (textonly || (inlineonly && is_inline(ct))) {
+ output_marker(ct, fmt);
+ return OK;
+ }
+
/* Check for invo_name-show-type/subtype */
snprintf (buffer, sizeof(buffer), "%s-show-%s/%s",
invo_name, ci->ci_type, ci->ci_subtype);
unfortunately the type checks are necessary without
some code rearrangement. And to make this really ugly,
only do it in mhshow, not mhfixmsg, mhn, or mhstore. */
- if (convert_content_charset (ct, &file) != OK) {
+ if (convert_content_charset (ct, &file) == OK) {
+ (*ct->c_ceclosefnx) (ct);
+ if ((fd = (*ct->c_ceopenfnx) (ct, &file)) == NOTOK)
+ return NOTOK;
+ } else {
admonish (NULL, "unable to convert character set%s to %s",
ct->c_partno ? "of part " : "",
ct->c_partno ? ct->c_partno : "",
list_switch (ct, -1, 1, 0, 0, 0);
}
+ /*
+ * If the command is a zero-length string, just write the output on
+ * stdout.
+ */
+
+ if (buffer[0] == '\0') {
+ char readbuf[BUFSIZ];
+ ssize_t cc;
+
+ if (fd == NOTOK) {
+ advise(NULL, "Cannot use NULL command to display content-type "
+ "%s/%s", ct->c_ctinfo.ci_type, ct->c_ctinfo.ci_subtype);
+ return NOTOK;
+ }
+
+ while ((cc = read(fd, readbuf, sizeof(readbuf))) > 0) {
+ fwrite(readbuf, sizeof(char), cc, stdout);
+ }
+
+ if (cc < 0) {
+ advise("read", "while reading text content");
+ return NOTOK;
+ }
+
+ return OK;
+ }
+
vec = argsplit(buffer, &file, &vecp);
vec[vecp++] = NULL;
*/
static int
-show_text (CT ct, int alternate)
+show_text (CT ct, int alternate, int concatsw)
{
char *cp, buffer[BUFSIZ];
CI ci = &ct->c_ctinfo;
* if it is not a text part of a multipart/alternative
*/
if (!alternate || ct->c_subtype == TEXT_PLAIN) {
- snprintf (buffer, sizeof(buffer), "%%l%s %%F", progsw ? progsw :
- moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
+ if (concatsw)
+ snprintf(buffer, sizeof(buffer), "%%l");
+ else
+ snprintf (buffer, sizeof(buffer), "%%l%s %%F", progsw ? progsw :
+ moreproc && *moreproc ? moreproc : DEFAULT_PAGER);
cp = (ct->c_showproc = add (buffer, NULL));
return show_content_aux (ct, alternate, cp, NULL);
}
*/
static int
-show_multi (CT ct, int alternate)
+show_multi (CT ct, int alternate, int concatsw, int textonly, int inlineonly,
+ struct format *fmt)
{
char *cp, buffer[BUFSIZ];
CI ci = &ct->c_ctinfo;
* unknown types are displayable, since they're treated as mixed
* per RFC 2046.
*/
- return show_multi_internal (ct, alternate);
+ return show_multi_internal (ct, alternate, concatsw, textonly,
+ inlineonly, fmt);
}
*/
static int
-show_multi_internal (CT ct, int alternate)
+show_multi_internal (CT ct, int alternate, int concatsw, int textonly,
+ int inlineonly, struct format *fmt)
{
int alternating, nowalternate, result;
struct multipart *m = (struct multipart *) ct->c_ctparams;
if (part_ok (p, 1) && type_ok (p, 1)) {
int inneresult;
- inneresult = show_switch (p, nowalternate);
+ inneresult = show_switch (p, nowalternate, concatsw, textonly,
+ inlineonly, fmt);
switch (inneresult) {
case NOTOK:
if (alternate && !alternating) {
*/
static int
-show_external (CT ct, int alternate)
+show_external (CT ct, int alternate, int concatsw, int textonly, int inlineonly,
+ struct format *fmt)
{
struct exbody *e = (struct exbody *) ct->c_ctparams;
CT p = e->eb_content;
if (!type_ok (p, 0))
return OK;
- return show_switch (p, alternate);
+ return show_switch (p, alternate, concatsw, textonly, inlineonly, fmt);
}
return OK;
}
+/*
+ * Compile our format string and save any parameters we care about.
+ */
+
+#define DEFAULT_MARKER "[ part %{part} - %{content-type} - %<{description}" \
+ "%{description}%?{cdispo-filename}%{cdispo-filename}" \
+ "%|%{ctype-name}%> ]"
+
+static struct format *
+compile_marker(char *markerform)
+{
+ struct format *fmt;
+ char *fmtstring;
+ struct comp *comp = NULL;
+ unsigned int bucket;
+ struct param_comp_list *pc_entry;
+
+ fmtstring = new_fs(markerform, NULL, DEFAULT_MARKER);
+
+ (void) fmt_compile(fmtstring, &fmt, 1);
+ free(fmtstring);
+
+ /*
+ * Things we care about:
+ *
+ * part - Part name (e.g., 1.1)
+ * content-type - Content-Type
+ * description - Content-Description
+ * disposition - Content-Disposition (inline, attachment)
+ * ctype-<param> - Content-Type parameter
+ * cdispo-<param> - Content-Disposition parameter
+ */
+
+ while ((comp = fmt_nextcomp(comp, &bucket)) != NULL) {
+ if (strcasecmp(comp->c_name, "part") == 0) {
+ part_comp = comp;
+ } else if (strcasecmp(comp->c_name, "content-type") == 0) {
+ ctype_comp = comp;
+ } else if (strcasecmp(comp->c_name, "description") == 0) {
+ description_comp = comp;
+ } else if (strcasecmp(comp->c_name, "disposition") == 0) {
+ dispo_comp = comp;
+ } else if (strncasecmp(comp->c_name, "ctype-", 6) == 0 &&
+ strlen(comp->c_name) > 6) {
+ pc_entry = mh_xmalloc(sizeof(*pc_entry));
+ pc_entry->param = getcpy(comp->c_name + 6);
+ pc_entry->comp = comp;
+ pc_entry->next = ctype_pc_list;
+ ctype_pc_list = pc_entry;
+ } else if (strncasecmp(comp->c_name, "cdispo-", 7) == 0 &&
+ strlen(comp->c_name) > 7) {
+ pc_entry = mh_xmalloc(sizeof(*pc_entry));
+ pc_entry->param = getcpy(comp->c_name + 7);
+ pc_entry->comp = comp;
+ pc_entry->next = dispo_pc_list;
+ dispo_pc_list = pc_entry;
+ }
+ }
+
+ return fmt;
+}
+
+/*
+ * Output on stdout an appropriate marker for this content, using mh-format
+ */
+
+static void
+output_marker(CT ct, struct format *fmt)
+{
+ char outbuf[BUFSIZ];
+ struct param_comp_list *pcentry;
+ int dat[5];
+
+ /*
+ * Grab any items we care about.
+ */
+
+ if (ctype_comp && ct->c_ctinfo.ci_type) {
+ ctype_comp->c_text = concat(ct->c_ctinfo.ci_type, "/",
+ ct->c_ctinfo.ci_subtype, NULL);
+ }
+
+ if (part_comp && ct->c_partno) {
+ part_comp->c_text = getcpy(ct->c_partno);
+ }
+
+ if (description_comp && ct->c_descr) {
+ description_comp->c_text = getcpy(ct->c_descr);
+ }
+
+ if (dispo_comp && ct->c_dispo_type) {
+ dispo_comp->c_text = getcpy(ct->c_dispo_type);
+ }
+
+ for (pcentry = ctype_pc_list; pcentry != NULL; pcentry = pcentry->next) {
+ pcentry->comp->c_text = get_param(ct->c_ctinfo.ci_first_pm,
+ pcentry->param, '?', 0);
+ }
+
+ for (pcentry = dispo_pc_list; pcentry != NULL; pcentry = pcentry->next) {
+ pcentry->comp->c_text = get_param(ct->c_dispo_first,
+ pcentry->param, '?', 0);
+ }
+
+ fmt_scan(fmt, outbuf, sizeof(outbuf), sizeof(outbuf), dat, NULL);
+
+ fputs(outbuf, stdout);
+
+ fmt_freecomptext();
+}
+
+/*
+ * Reset (and free) any of the saved marker text
+ */
+
+static void
+free_markercomps(void)
+{
+ struct param_comp_list *pc_entry, *pc2;
+
+ part_comp = NULL;
+ ctype_comp = NULL;
+ description_comp = NULL;
+ dispo_comp = NULL;
+
+ for (pc_entry = ctype_pc_list; pc_entry != NULL; ) {
+ free(pc_entry->param);
+ pc2 = pc_entry->next;
+ free(pc_entry);
+ pc_entry = pc2;
+ }
+
+ for (pc_entry = dispo_pc_list; pc_entry != NULL; ) {
+ free(pc_entry->param);
+ pc2 = pc_entry->next;
+ free(pc_entry);
+ pc_entry = pc2;
+ }
+}
+
/*
* Exit if the display process returned with a nonzero exit code, or terminated
* with a SIGQUIT signal.