aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--notmuch-client.h7
-rw-r--r--notmuch-reply.c4
-rw-r--r--notmuch-show.c152
-rw-r--r--notmuch.115
-rw-r--r--notmuch.c20
-rw-r--r--show-message.c150
6 files changed, 132 insertions, 216 deletions
diff --git a/notmuch-client.h b/notmuch-client.h
index 47a4e570..7221c681 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -78,6 +78,7 @@ typedef struct notmuch_show_format {
typedef struct notmuch_show_params {
int entire_thread;
int raw;
+ int part;
} notmuch_show_params_t;
/* There's no point in continuing when we've detected that we've done
@@ -140,9 +141,6 @@ int
notmuch_cat_command (void *ctx, int argc, char *argv[]);
int
-notmuch_part_command (void *ctx, int argc, char *argv[]);
-
-int
notmuch_config_command (void *ctx, int argc, char *argv[]);
const char *
@@ -159,7 +157,8 @@ query_string_from_args (void *ctx, int argc, char *argv[]);
notmuch_status_t
show_message_body (const char *filename,
- const notmuch_show_format_t *format);
+ const notmuch_show_format_t *format,
+ notmuch_show_params_t *params);
notmuch_status_t
show_one_part (const char *filename, int part);
diff --git a/notmuch-reply.c b/notmuch-reply.c
index b5ca19c4..ab156508 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -461,6 +461,8 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
const char *subject, *from_addr = NULL;
const char *in_reply_to, *orig_references, *references;
const notmuch_show_format_t *format = &format_reply;
+ notmuch_show_params_t params;
+ params.part = -1;
for (messages = notmuch_query_search_messages (query);
notmuch_messages_valid (messages);
@@ -520,7 +522,7 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
notmuch_message_get_header (message, "from"));
show_message_body (notmuch_message_get_filename (message),
- format);
+ format, &params);
notmuch_message_destroy (message);
}
diff --git a/notmuch-show.c b/notmuch-show.c
index 27587f87..e128be54 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -82,6 +82,19 @@ static const notmuch_show_format_t format_mbox = {
""
};
+static void
+format_part_raw (GMimeObject *part,
+ unused (int *part_count));
+
+static const notmuch_show_format_t format_raw = {
+ "",
+ "", NULL,
+ "", NULL, "",
+ "", format_part_raw, NULL, "", "",
+ "", "",
+ ""
+};
+
static const char *
_get_tags_as_string (const void *ctx, notmuch_message_t *message)
{
@@ -329,6 +342,10 @@ show_part_content (GMimeObject *part, GMimeStream *stream_out)
GMimeDataWrapper *wrapper;
const char *charset;
+ /* do nothing if this is a multipart */
+ if (GMIME_IS_MULTIPART (part) || GMIME_IS_MESSAGE_PART (part))
+ return;
+
charset = g_mime_object_get_content_type_parameter (part, "charset");
if (stream_out) {
@@ -490,29 +507,44 @@ format_part_end_json (GMimeObject *part)
}
static void
+format_part_raw (GMimeObject *part, unused (int *part_count))
+{
+ GMimeStream *stream_stdout = g_mime_stream_file_new (stdout);
+ g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream_stdout), FALSE);
+ show_part_content (part, stream_stdout);
+ g_object_unref(stream_stdout);
+}
+
+static void
show_message (void *ctx,
const notmuch_show_format_t *format,
notmuch_message_t *message,
- int indent)
+ int indent,
+ notmuch_show_params_t *params)
{
- fputs (format->message_start, stdout);
- if (format->message)
- format->message(ctx, message, indent);
+ if (params->part <= 0) {
+ fputs (format->message_start, stdout);
+ if (format->message)
+ format->message(ctx, message, indent);
+
+ fputs (format->header_start, stdout);
+ if (format->header)
+ format->header(ctx, message);
+ fputs (format->header_end, stdout);
- fputs (format->header_start, stdout);
- if (format->header)
- format->header(ctx, message);
- fputs (format->header_end, stdout);
+ fputs (format->body_start, stdout);
+ }
- fputs (format->body_start, stdout);
if (format->part)
show_message_body (notmuch_message_get_filename (message),
- format);
- fputs (format->body_end, stdout);
+ format, params);
- fputs (format->message_end, stdout);
-}
+ if (params->part <= 0) {
+ fputs (format->body_end, stdout);
+ fputs (format->message_end, stdout);
+ }
+}
static void
show_messages (void *ctx,
@@ -545,7 +577,7 @@ show_messages (void *ctx,
next_indent = indent;
if (match || params->entire_thread) {
- show_message (ctx, format, message, indent);
+ show_message (ctx, format, message, indent, params);
next_indent = indent + 1;
fputs (format->message_set_sep, stdout);
@@ -588,8 +620,10 @@ do_show_single (void *ctx,
return 1;
}
+ notmuch_message_set_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH, 1);
+
/* Special case for --format=raw of full single message, just cat out file */
- if (params->raw) {
+ if (params->raw && 0 == params->part) {
const char *filename;
FILE *file;
@@ -615,6 +649,10 @@ do_show_single (void *ctx,
fclose (file);
+ } else {
+
+ show_message (ctx, format, message, 0, params);
+
}
return 0;
@@ -675,6 +713,7 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
params.entire_thread = 0;
params.raw = 0;
+ params.part = -1;
for (i = 0; i < argc && argv[i][0] == '-'; i++) {
if (strcmp (argv[i], "--") == 0) {
@@ -691,11 +730,14 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
} else if (strcmp (opt, "mbox") == 0) {
format = &format_mbox;
} else if (strcmp (opt, "raw") == 0) {
+ format = &format_raw;
params.raw = 1;
} else {
fprintf (stderr, "Invalid value for --format: %s\n", opt);
return 1;
}
+ } else if (STRNCMP_LITERAL (argv[i], "--part=") == 0) {
+ params.part = atoi(argv[i] + sizeof ("--part=") - 1);
} else if (STRNCMP_LITERAL (argv[i], "--entire-thread") == 0) {
params.entire_thread = 1;
} else {
@@ -735,7 +777,10 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
/* If --format=raw specified without specifying part, we can only
* output single message, so set part=0 */
- if (params.raw)
+ if (params.raw && params.part < 0)
+ params.part = 0;
+
+ if (params.part >= 0)
return do_show_single (ctx, query, format, &params);
else
return do_show (ctx, query, format, &params);
@@ -745,78 +790,3 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
return 0;
}
-
-int
-notmuch_part_command (void *ctx, unused (int argc), unused (char *argv[]))
-{
- notmuch_config_t *config;
- notmuch_database_t *notmuch;
- notmuch_query_t *query;
- notmuch_messages_t *messages;
- notmuch_message_t *message;
- char *query_string;
- int i;
- int part = 0;
-
- for (i = 0; i < argc && argv[i][0] == '-'; i++) {
- if (strcmp (argv[i], "--") == 0) {
- i++;
- break;
- }
- if (STRNCMP_LITERAL (argv[i], "--part=") == 0) {
- part = atoi(argv[i] + sizeof ("--part=") - 1);
- } else {
- fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
- return 1;
- }
- }
-
- argc -= i;
- argv += i;
-
- config = notmuch_config_open (ctx, NULL, NULL);
- if (config == NULL)
- return 1;
-
- query_string = query_string_from_args (ctx, argc, argv);
- if (query_string == NULL) {
- fprintf (stderr, "Out of memory\n");
- return 1;
- }
-
- if (*query_string == '\0') {
- fprintf (stderr, "Error: notmuch part requires at least one search term.\n");
- return 1;
- }
-
- notmuch = notmuch_database_open (notmuch_config_get_database_path (config),
- NOTMUCH_DATABASE_MODE_READ_ONLY);
- if (notmuch == NULL)
- return 1;
-
- query = notmuch_query_create (notmuch, query_string);
- if (query == NULL) {
- fprintf (stderr, "Out of memory\n");
- return 1;
- }
-
- if (notmuch_query_count_messages (query) != 1) {
- fprintf (stderr, "Error: search term did not match precisely one message.\n");
- return 1;
- }
-
- messages = notmuch_query_search_messages (query);
- message = notmuch_messages_get (messages);
-
- if (message == NULL) {
- fprintf (stderr, "Error: cannot find matching message.\n");
- return 1;
- }
-
- show_one_part (notmuch_message_get_filename (message), part);
-
- notmuch_query_destroy (query);
- notmuch_database_close (notmuch);
-
- return 0;
-}
diff --git a/notmuch.1 b/notmuch.1
index 2912fcfd..76222981 100644
--- a/notmuch.1
+++ b/notmuch.1
@@ -297,8 +297,20 @@ The original, raw content of the email message is displayed.
Consumers of this format should expect to implement MIME decoding and
similar functions. This format must only be used with search terms
matching a single message.
+.RE
+.RE
+
+.RS 4
+.TP 4
+.B \-\-part=N
+Output the single decoded MIME part N of a single message. The search
+terms must match only a single message. Message parts are numbered in
+a depth-first walk of the message MIME structure, and are identified
+in the 'json' or
+'text' output formats.
.RE
+
A common use of
.B notmuch show
is to display a single thread of email messages. For this, use a
@@ -311,7 +323,8 @@ See the
.B "SEARCH SYNTAX"
section below for details of the supported syntax for <search-terms>.
.RE
-.TP
+.RS 4
+.TP 4
.BR count " <search-term>..."
Count messages matching the search terms.
diff --git a/notmuch.c b/notmuch.c
index 098f7335..2e98f25d 100644
--- a/notmuch.c
+++ b/notmuch.c
@@ -269,6 +269,15 @@ command_t commands[] = {
"\t\tdecoding and similar functions. This format must only\n"
"\t\tbe used with search terms matching a single message.\n"
"\n"
+ "\t--part=N\n"
+ "\n"
+ "\t\tOutput the single decoded MIME part N of a single message.\n"
+ "\t\tThe search terms must match only a single message.\n"
+ "\t\tMessage parts are numbered in a depth-first walk of the\n"
+ "\t\tmessage MIME structure, and are identified in the 'json' or\n"
+ "\t\t'text' output formats.\n"
+ "\n"
+ "\n"
"\tA common use of \"notmuch show\" is to display a single\n"
"\tthread of email messages. For this, use a search term of\n"
"\t\"thread:<thread-id>\" as can be seen in the first column\n"
@@ -358,17 +367,6 @@ command_t commands[] = {
"\tcontain tags only from messages that match the search-term(s).\n"
"\n"
"\tIn both cases the list will be alphabetically sorted." },
- { "part", notmuch_part_command,
- "--part=<num> <search-terms>",
- "Output a single MIME part of a message.",
- "\tA single decoded MIME part, with no encoding or framing,\n"
- "\tis output to stdout. The search terms must match only a single\n"
- "\tmessage, otherwise this command will fail.\n"
- "\n"
- "\tThe part number should match the part \"id\" field output\n"
- "\tby the \"--format=json\" option of \"notmuch show\". If the\n"
- "\tmessage specified by the search terms does not include a\n"
- "\tpart with the specified \"id\" there will be no output." },
{ "config", notmuch_config_command,
"[get|set] <section>.<item> [value ...]",
"Get or set settings in the notmuch configuration file.",
diff --git a/show-message.c b/show-message.c
index 4ccd4490..32bb860f 100644
--- a/show-message.c
+++ b/show-message.c
@@ -22,13 +22,20 @@
#include "notmuch-client.h"
+typedef struct show_message_state {
+ int part_count;
+ int in_zone;
+} show_message_state_t;
+
static void
show_message_part (GMimeObject *part,
- int *part_count,
+ show_message_state_t *state,
const notmuch_show_format_t *format,
+ notmuch_show_params_t *params,
int first)
{
- *part_count += 1;
+ int selected;
+ state->part_count += 1;
if (! (GMIME_IS_PART (part) || GMIME_IS_MULTIPART (part) || GMIME_IS_MESSAGE_PART (part))) {
fprintf (stderr, "Warning: Not displaying unknown mime part: %s.\n",
@@ -36,43 +43,63 @@ show_message_part (GMimeObject *part,
return;
}
- if (!first)
- fputs (format->part_sep, stdout);
+ selected = (params->part <= 0 || state->part_count == params->part);
+
+ if (selected || state->in_zone) {
+ if (!first && (params->part <= 0 || state->in_zone))
+ fputs (format->part_sep, stdout);
- format->part (part, part_count);
+ format->part (part, &(state->part_count));
+ }
if (GMIME_IS_MULTIPART (part)) {
GMimeMultipart *multipart = GMIME_MULTIPART (part);
int i;
+ if (selected)
+ state->in_zone = 1;
+
for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
show_message_part (g_mime_multipart_get_part (multipart, i),
- part_count, format, i == 0);
+ state, format, params, i == 0);
}
+ if (selected)
+ state->in_zone = 0;
+
} else if (GMIME_IS_MESSAGE_PART (part)) {
- GMimeMessage *mime_message;
+ GMimeMessage *mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
- mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
+ if (selected)
+ state->in_zone = 1;
show_message_part (g_mime_message_get_mime_part (mime_message),
- part_count, format, first);
+ state, format, params, TRUE);
+
+ if (selected)
+ state->in_zone = 0;
}
- if (format->part_end)
- format->part_end (part);
+ if (selected || state->in_zone) {
+ if (format->part_end)
+ format->part_end (part);
+ }
}
notmuch_status_t
show_message_body (const char *filename,
- const notmuch_show_format_t *format)
+ const notmuch_show_format_t *format,
+ notmuch_show_params_t *params)
{
GMimeStream *stream = NULL;
GMimeParser *parser = NULL;
GMimeMessage *mime_message = NULL;
notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
FILE *file = NULL;
- int part_count = 0;
+ show_message_state_t state;
+
+ state.part_count = 0;
+ state.in_zone = 0;
file = fopen (filename, "r");
if (! file) {
@@ -89,8 +116,9 @@ show_message_body (const char *filename,
mime_message = g_mime_parser_construct_message (parser);
show_message_part (g_mime_message_get_mime_part (mime_message),
- &part_count,
+ &state,
format,
+ params,
TRUE);
DONE:
@@ -108,97 +136,3 @@ show_message_body (const char *filename,
return ret;
}
-
-static void
-show_one_part_output (GMimeObject *part)
-{
- GMimeStream *stream_filter = NULL;
- GMimeDataWrapper *wrapper;
- GMimeStream *stream_stdout = g_mime_stream_file_new (stdout);
-
- stream_filter = g_mime_stream_filter_new(stream_stdout);
- wrapper = g_mime_part_get_content_object (GMIME_PART (part));
- if (wrapper && stream_filter)
- g_mime_data_wrapper_write_to_stream (wrapper, stream_filter);
- if (stream_filter)
- g_object_unref(stream_filter);
-}
-
-static void
-show_one_part_worker (GMimeObject *part, int *part_count, int desired_part)
-{
- if (GMIME_IS_MULTIPART (part)) {
- GMimeMultipart *multipart = GMIME_MULTIPART (part);
- int i;
-
- *part_count = *part_count + 1;
-
- for (i = 0; i < g_mime_multipart_get_count (multipart); i++) {
- show_one_part_worker (g_mime_multipart_get_part (multipart, i),
- part_count, desired_part);
- }
- return;
- }
-
- if (GMIME_IS_MESSAGE_PART (part)) {
- GMimeMessage *mime_message;
-
- mime_message = g_mime_message_part_get_message (GMIME_MESSAGE_PART (part));
-
- show_one_part_worker (g_mime_message_get_mime_part (mime_message),
- part_count, desired_part);
-
- return;
- }
-
- if (! (GMIME_IS_PART (part)))
- return;
-
- *part_count = *part_count + 1;
-
- if (*part_count == desired_part)
- show_one_part_output (part);
-}
-
-notmuch_status_t
-show_one_part (const char *filename, int part)
-{
- GMimeStream *stream = NULL;
- GMimeParser *parser = NULL;
- GMimeMessage *mime_message = NULL;
- notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
- FILE *file = NULL;
- int part_count = 0;
-
- file = fopen (filename, "r");
- if (! file) {
- fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
- ret = NOTMUCH_STATUS_FILE_ERROR;
- goto DONE;
- }
-
- stream = g_mime_stream_file_new (file);
- g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE);
-
- parser = g_mime_parser_new_with_stream (stream);
-
- mime_message = g_mime_parser_construct_message (parser);
-
- show_one_part_worker (g_mime_message_get_mime_part (mime_message),
- &part_count, part);
-
- DONE:
- if (mime_message)
- g_object_unref (mime_message);
-
- if (parser)
- g_object_unref (parser);
-
- if (stream)
- g_object_unref (stream);
-
- if (file)
- fclose (file);
-
- return ret;
-}