Commit f8fc2081 f8fc2081dffd950ab1c6082e4a32f0096bd7415d by Sergey Poznyakoff

mail: implement struct command.

* NEWS, doc/texinfo/programs.texi: Document struct command.
* mail/struct.c: New file.
* mail/Makefile.am (mail_SOURCES): Add struct.c
* mail/decode.c (fprint_msgset, mime_descend): New functions.
Rewrite the rest using mime_descend.
* mail/mail.h (mail_struct): New function.
(MDHINT_SELECTED_HEADERS): New define.
(struct mime_descend_closure, mime_descend_fn): New types.
(mime_descend): New proto.
(util_get_content_type): Get two arguments.
* mail/table.c (mail_command_table): Add st[ruct].
* mail/util.c (util_get_content_type): Get two arguments.
All callers updated.
(util_get_hdr_value): use mu_header_aget_value_unfold and
mu_rtrim_class.
1 parent 0517ca03
GNU mailutils NEWS -- history of user-visible changes. 2009-07-12
GNU mailutils NEWS -- history of user-visible changes. 2009-07-13
Copyright (C) 2002, 2003, 2004, 2005, 2006, 2007,
2008, 2009 Free Software Foundation, Inc.
See the end of file for copying conditions.
......@@ -53,6 +53,15 @@ is also allowed.
The -F option (record outgoing messages in a file named after the
first recipient) is implemented.
** struct command
The st[ruct] command allows to list message MIME structures, e.g.:
& struct 2
2[1] text/plain 513
2[2] application/octal-stream 247K
2[3] text/x-diff 31K
** error locations
Diagnostic messages issued while processing `source' command
......
......@@ -2878,6 +2878,7 @@ Displays current mailbox summary. E.g.:
@kyindex Print, mail command
@kyindex Type, mail command
@kyindex decode, mail command
@kyindex struct, mail command
@kyindex top, mail command
@kyindex pipe, mail command
@kyindex |, mail command
......@@ -2926,6 +2927,21 @@ defaults to five.
Pipe the contents of specified messages through @var{shell-command}. If
@var{shell-command} is empty but the string variable @code{cmd} is set,
the value of this variable is used as a command name.
@item struct [@var{msglist}]
Prints the @acronym{MIME} structure of each message from
@var{msglist}. Empty @var{msglist} means current message.
Example:
@smallexample
@cartouche
& struct 2
2 multipart/mixed 14k
2[1] text/plain 296
2[2] application/octet-stream 5k
2[3] text/x-diff 31k
@end cartouche
@end smallexample
@end table
@node Marking Messages
......
......@@ -84,6 +84,7 @@ mail_SOURCES = \
shell.c\
size.c\
source.c\
struct.c\
summary.c\
table.c\
tag.c\
......
......@@ -33,7 +33,8 @@ struct decode_closure
static int print_stream (mu_stream_t, FILE *);
static int display_message (mu_message_t, msgset_t *msgset, void *closure);
static int display_message0 (mu_message_t, const msgset_t *, int);
static int display_submessage (struct mime_descend_closure *closure,
void *data);
static int get_content_encoding (mu_header_t hdr, char **value);
static void run_metamail (const char *mailcap, mu_message_t mesg);
......@@ -59,12 +60,20 @@ display_message (mu_message_t mesg, msgset_t *msgset, void *arg)
{
struct decode_closure *closure = arg;
mu_attribute_t attr = NULL;
struct mime_descend_closure mclos;
mu_message_get_attribute (mesg, &attr);
if (mu_attribute_is_deleted (attr))
return 1;
display_message0 (mesg, msgset, closure->select_hdr);
mclos.hints = closure->select_hdr ? MDHINT_SELECTED_HEADERS : 0;
mclos.msgset = msgset;
mclos.message = mesg;
mclos.type = NULL;
mclos.encoding = NULL;
mclos.parent = NULL;
mime_descend (&mclos, display_submessage, NULL);
/* Mark enclosing message as read */
if (mu_mailbox_get_message (mbox, msgset->msg_part[0], &mesg) == 0)
......@@ -112,9 +121,23 @@ display_headers (FILE *out, mu_message_t mesg,
}
}
size_t
fprint_msgset (FILE *fp, const msgset_t *msgset)
{
int i;
size_t n = 0;
n = fprintf (fp, "%d", msgset->msg_part[0]);
for (i = 1; i < msgset->npart; i++)
n += fprintf (fp, "[%d", msgset->msg_part[i]);
for (i = 1; i < msgset->npart; i++)
n += fprintf (fp, "]");
return n;
}
static void
display_part_header (FILE *out, const msgset_t *msgset,
char *type, char *encoding)
const char *type, const char *encoding)
{
int size = util_screen_columns () - 3;
unsigned int i;
......@@ -124,11 +147,8 @@ display_part_header (FILE *out, const msgset_t *msgset,
fputc ('-', out);
fputc ('+', out);
fputc ('\n', out);
fprintf (out, _("| Message=%d"), msgset->msg_part[0]);
for (i = 1; i < msgset->npart; i++)
fprintf (out, "[%d", msgset->msg_part[i]);
for (i = 1; i < msgset->npart; i++)
fprintf (out, "]");
fprintf (out, "%s", _("| Message="));
fprint_msgset (out, msgset);
fprintf (out, "\n");
fprintf (out, _("| Type=%s\n"), type);
......@@ -140,38 +160,49 @@ display_part_header (FILE *out, const msgset_t *msgset,
fputc ('\n', out);
}
static int
display_message0 (mu_message_t mesg, const msgset_t *msgset,
int select_hdr)
int
mime_descend (struct mime_descend_closure *closure,
mime_descend_fn fun, void *data)
{
int status = 0;
size_t nparts = 0;
mu_header_t hdr = NULL;
char *type;
char *encoding;
int ismime = 0;
char *tmp;
struct mime_descend_closure subclosure;
mu_message_get_header (mesg, &hdr);
util_get_content_type (hdr, &type);
mu_message_get_header (closure->message, &hdr);
util_get_content_type (hdr, &type, NULL);
get_content_encoding (hdr, &encoding);
mu_message_is_multipart (mesg, &ismime);
closure->type = type;
closure->encoding = encoding;
subclosure.hints = 0;
subclosure.parent = closure;
mu_message_is_multipart (closure->message, &ismime);
if (ismime)
{
unsigned int j;
mu_message_get_num_parts (mesg, &nparts);
mu_message_get_num_parts (closure->message, &nparts);
for (j = 1; j <= nparts; j++)
{
mu_message_t message = NULL;
if (mu_message_get_part (mesg, j, &message) == 0)
if (mu_message_get_part (closure->message, j, &message) == 0)
{
msgset_t *set = msgset_expand (msgset_dup (msgset),
msgset_t *set = msgset_expand (msgset_dup (closure->msgset),
msgset_make_1 (j));
display_message0 (message, set, 0);
subclosure.msgset = set;
subclosure.message = message;
status = mime_descend (&subclosure, fun, data);
msgset_free (set);
if (status)
break;
}
}
}
......@@ -179,14 +210,36 @@ display_message0 (mu_message_t mesg, const msgset_t *msgset,
{
mu_message_t submsg = NULL;
if (mu_message_unencapsulate (mesg, &submsg, NULL) == 0)
display_message0 (submsg, msgset, select_hdr);
if (mu_message_unencapsulate (closure->message, &submsg, NULL) == 0)
{
subclosure.hints = MDHINT_SELECTED_HEADERS;
subclosure.msgset = closure->msgset;
subclosure.message = submsg;
status = mime_descend (&subclosure, fun, data);
}
}
else if (mailvar_get (&tmp, "metamail", mailvar_type_string, 0) == 0)
else
status = fun (closure, data);
closure->type = NULL;
closure->encoding = NULL;
free (type);
free (encoding);
return status;
}
static int
display_submessage (struct mime_descend_closure *closure, void *data)
{
char *tmp;
if (mailvar_get (&tmp, "metamail", mailvar_type_string, 0) == 0)
{
/* If `metamail' is set to a string, treat it as command line
of external metamail program. */
run_metamail (tmp, mesg);
run_metamail (tmp, closure->message);
}
else
{
......@@ -197,19 +250,20 @@ display_message0 (mu_message_t mesg, const msgset_t *msgset,
mu_stream_t stream = NULL;
mu_header_t hdr = NULL;
mu_message_get_body (mesg, &body);
mu_message_get_header (mesg, &hdr);
mu_message_get_body (closure->message, &body);
mu_message_get_header (closure->message, &hdr);
mu_body_get_stream (body, &b_stream);
/* Can we decode. */
if (mu_filter_create(&d_stream, b_stream, encoding,
MU_FILTER_DECODE, MU_STREAM_READ) == 0)
if (mu_filter_create(&d_stream, b_stream, closure->encoding,
MU_FILTER_DECODE, MU_STREAM_READ) == 0)
stream = d_stream;
else
stream = b_stream;
display_part_header (ofile, msgset, type, encoding);
display_part_header (ofile, closure->msgset,
closure->type, closure->encoding);
/* If `metamail' is set to true, enable internal mailcap
support */
if (mailvar_get (NULL, "metamail", mailvar_type_boolean, 0) == 0)
......@@ -225,33 +279,31 @@ display_message0 (mu_message_t mesg, const msgset_t *msgset,
builtin_display = display_stream_mailcap (NULL, stream, hdr, no_ask,
interactive, 0, debug);
}
if (builtin_display)
{
size_t lines = 0;
int pagelines = util_get_crt ();
FILE *out;
mu_message_lines (mesg, &lines);
mu_message_lines (closure->message, &lines);
if (pagelines && lines > pagelines)
out = popen (getenv ("PAGER"), "w");
else
out = ofile;
display_headers (out, mesg, msgset, select_hdr);
display_headers (out, closure->message, closure->msgset,
closure->hints & MDHINT_SELECTED_HEADERS);
print_stream (stream, out);
if (out != ofile)
pclose (out);
}
if (d_stream)
mu_stream_destroy (&d_stream, NULL);
}
free (type);
free (encoding);
return 0;
}
......
......@@ -242,6 +242,7 @@ extern int mail_eq (int argc, char **argv); /* command = */
extern int mail_setenv (int argc, char **argv);
extern int mail_envelope (int argc, char **argv);
extern int print_envelope (msgset_t *mspec, mu_message_t msg, void *data);
extern int mail_struct (int argc, char **argv);
extern int if_cond (void);
......@@ -307,6 +308,27 @@ extern int msgset_member (msgset_t *set, size_t n);
extern msgset_t *msgset_negate (msgset_t *set);
extern size_t msgset_count (msgset_t *set);
#define MDHINT_SELECTED_HEADERS 0x1
struct mime_descend_closure
{
int hints;
const msgset_t *msgset;
mu_message_t message;
const char *type;
const char *encoding;
const struct mime_descend_closure *parent;
};
typedef int (*mime_descend_fn) (struct mime_descend_closure *closure,
void *data);
extern int mime_descend (struct mime_descend_closure *closure,
mime_descend_fn fun, void *data);
extern int util_do_command (const char *cmd, ...) MU_PRINTFLIKE(1,2);
extern int util_foreach_msg (int argc, char **argv, int flags,
......@@ -369,7 +391,7 @@ extern int util_tempfile (char **namep);
extern void util_msgset_iterate (msgset_t *msgset,
int (*fun) (mu_message_t, msgset_t *, void *),
void *closure);
extern int util_get_content_type (mu_header_t hdr, char **value);
extern int util_get_content_type (mu_header_t hdr, char **value, char **args);
extern int util_get_hdr_value (mu_header_t hdr, const char *name, char **value);
extern int util_merge_addresses (char **addr_str, const char *value);
extern int util_header_expand (mu_header_t *hdr);
......@@ -383,6 +405,8 @@ void util_mark_read (mu_message_t msg);
const char *util_url_to_string (mu_url_t url);
size_t fprint_msgset (FILE *fp, const msgset_t *msgset);
int is_address_field (const char *name);
extern int ml_got_interrupt (void);
......
/* GNU Mailutils -- a suite of utilities for electronic mail
Copyright (C) 2009 Free Software Foundation, Inc.
GNU Mailutils is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation; either version 3, or (at your option)
any later version.
GNU Mailutils is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with GNU Mailutils. If not, see <http://www.gnu.org/licenses/>. */
#include "mail.h"
#define PART_WIDTH 8
static int
show_part (struct mime_descend_closure *closure, void *data)
{
size_t width;
size_t size = 0;
width = fprint_msgset (ofile, closure->msgset);
for (; width < 5; width++)
fputc (' ', ofile);
fprintf (ofile, " %-25s", closure->type);
mu_message_size (closure->message, &size);
if (size < 1024)
fprintf (ofile, " %4lu", (unsigned long) size);
else if (size < 1024*1024)
fprintf (ofile, "%4luK", (unsigned long) size / 1024);
else
fprintf (ofile, "%4luM", (unsigned long) size / 1024 / 1024);
fprintf (ofile, "\n");
return 0;
}
static int
show_struct (msgset_t *msgset, mu_message_t msg, void *data)
{
struct mime_descend_closure mclos;
mclos.hints = 0;
mclos.msgset = msgset;
mclos.message = msg;
mclos.type = NULL;
mclos.encoding = NULL;
mclos.parent = NULL;
mime_descend (&mclos, show_part, NULL);
/* Mark enclosing message as read */
if (mu_mailbox_get_message (mbox, msgset->msg_part[0], &msg) == 0)
util_mark_read (msg);
return 0;
}
int
mail_struct (int argc, char **argv)
{
return util_foreach_msg (argc, argv, MSG_NODELETED|MSG_SILENT,
show_struct, NULL);
}
......@@ -136,6 +136,8 @@ static const struct mail_command_entry mail_command_table[] = {
mail_size, msglist_compl },
{ "so", "source", "so[urce] file", 0,
mail_source, NULL },
{ "st", "struct", "st[ruct] [msglist]", 0,
mail_struct, NULL },
{ "su", "summary", "su[mmary]", 0,
mail_summary, no_compl },
{ "T", "Type", "T[ype] [msglist]", 0,
......
......@@ -793,7 +793,7 @@ util_descend_subparts (mu_message_t mesg, msgset_t *msgset, mu_message_t *part)
mu_header_t hdr = NULL;
mu_message_get_header (mesg, &hdr);
util_get_content_type (hdr, &type);
util_get_content_type (hdr, &type, NULL);
if (mu_c_strncasecmp (type, "message/rfc822", strlen (type)) == 0)
{
if (mu_message_unencapsulate (mesg, &submsg, NULL))
......@@ -839,12 +839,12 @@ util_msgset_iterate (msgset_t *msgset,
return;
if (util_descend_subparts (mesg, msgset, &mesg) == 0)
(*fun)(mesg, msgset, closure);
(*fun) (mesg, msgset, closure);
}
}
int
util_get_content_type (mu_header_t hdr, char **value)
util_get_content_type (mu_header_t hdr, char **value, char **args)
{
char *type = NULL;
util_get_hdr_value (hdr, MU_HEADER_CONTENT_TYPE, &type);
......@@ -854,6 +854,17 @@ util_get_content_type (mu_header_t hdr, char **value)
free (type);
type = strdup ("text/plain"); /* Default. */
}
else
{
char *p;
p = strchr (type, ';');
if (p)
{
*p++ = 0;
if (args)
*args = p;
}
}
*value = type;
return 0;
}
......@@ -861,16 +872,9 @@ util_get_content_type (mu_header_t hdr, char **value)
int
util_get_hdr_value (mu_header_t hdr, const char *name, char **value)
{
int status = mu_header_aget_value (hdr, name, value);
int status = mu_header_aget_value_unfold (hdr, name, value);
if (status == 0)
{
/* Remove the newlines. */
char *nl;
while ((nl = strchr (*value, '\n')) != NULL)
{
*nl = ' ';
}
}
mu_rtrim_class (*value, MU_CTYPE_SPACE);
return status;
}
......