+ if (format->message_set_end)
+ fputs (format->message_set_end, stdout);
+ }
+
+ if (format->message_set_end)
+ fputs (format->message_set_end, stdout);
+
+ return res;
+}
+
+/* Formatted output of single message */
+static int
+do_show_single (void *ctx,
+ notmuch_query_t *query,
+ const notmuch_show_format_t *format,
+ notmuch_show_params_t *params)
+{
+ notmuch_messages_t *messages;
+ notmuch_message_t *message;
+
+ 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;
+ }
+
+ notmuch_message_set_flag (message, NOTMUCH_MESSAGE_FLAG_MATCH, 1);
+
+ return show_message (ctx, format, message, 0, params) != NOTMUCH_STATUS_SUCCESS;
+}
+
+/* Formatted output of threads */
+static int
+do_show (void *ctx,
+ notmuch_query_t *query,
+ const notmuch_show_format_t *format,
+ notmuch_show_params_t *params)
+{
+ notmuch_threads_t *threads;
+ notmuch_thread_t *thread;
+ notmuch_messages_t *messages;
+ int first_toplevel = 1;
+ notmuch_status_t status, res = NOTMUCH_STATUS_SUCCESS;
+
+ if (format->message_set_start)
+ fputs (format->message_set_start, stdout);
+
+ for (threads = notmuch_query_search_threads (query);
+ notmuch_threads_valid (threads);
+ notmuch_threads_move_to_next (threads))
+ {
+ thread = notmuch_threads_get (threads);
+
+ messages = notmuch_thread_get_toplevel_messages (thread);
+
+ if (messages == NULL)
+ INTERNAL_ERROR ("Thread %s has no toplevel messages.\n",
+ notmuch_thread_get_thread_id (thread));
+
+ if (!first_toplevel && format->message_set_sep)
+ fputs (format->message_set_sep, stdout);
+ first_toplevel = 0;
+
+ status = show_messages (ctx, format, messages, 0, params);
+ if (status && !res)
+ res = status;
+
+ notmuch_thread_destroy (thread);
+
+ }
+
+ if (format->message_set_end)
+ fputs (format->message_set_end, stdout);
+
+ return res != NOTMUCH_STATUS_SUCCESS;
+}
+
+enum {
+ NOTMUCH_FORMAT_NOT_SPECIFIED,
+ NOTMUCH_FORMAT_JSON,
+ NOTMUCH_FORMAT_TEXT,
+ NOTMUCH_FORMAT_MBOX,
+ NOTMUCH_FORMAT_RAW
+};
+
+enum {
+ ENTIRE_THREAD_DEFAULT,
+ ENTIRE_THREAD_TRUE,
+ ENTIRE_THREAD_FALSE,
+};
+
+/* The following is to allow future options to be added more easily */
+enum {
+ EXCLUDE_TRUE,
+ EXCLUDE_FALSE,
+};
+
+int
+notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
+{
+ notmuch_config_t *config;
+ notmuch_database_t *notmuch;
+ notmuch_query_t *query;
+ char *query_string;
+ int opt_index, ret;
+ const notmuch_show_format_t *format = &format_text;
+ notmuch_show_params_t params = {
+ .part = -1,
+ .omit_excluded = TRUE,
+ .crypto = {
+ .verify = FALSE,
+ .decrypt = FALSE
+ }
+ };
+ int format_sel = NOTMUCH_FORMAT_NOT_SPECIFIED;
+ int exclude = EXCLUDE_TRUE;
+ int entire_thread = ENTIRE_THREAD_DEFAULT;
+
+ notmuch_opt_desc_t options[] = {
+ { NOTMUCH_OPT_KEYWORD, &format_sel, "format", 'f',
+ (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
+ { "text", NOTMUCH_FORMAT_TEXT },
+ { "mbox", NOTMUCH_FORMAT_MBOX },
+ { "raw", NOTMUCH_FORMAT_RAW },
+ { 0, 0 } } },
+ { NOTMUCH_OPT_KEYWORD, &exclude, "exclude", 'x',
+ (notmuch_keyword_t []){ { "true", EXCLUDE_TRUE },
+ { "false", EXCLUDE_FALSE },
+ { 0, 0 } } },
+ { NOTMUCH_OPT_KEYWORD, &entire_thread, "entire-thread", 't',
+ (notmuch_keyword_t []){ { "true", ENTIRE_THREAD_TRUE },
+ { "false", ENTIRE_THREAD_FALSE },
+ { "", ENTIRE_THREAD_TRUE },
+ { 0, 0 } } },
+ { NOTMUCH_OPT_INT, ¶ms.part, "part", 'p', 0 },
+ { NOTMUCH_OPT_BOOLEAN, ¶ms.crypto.decrypt, "decrypt", 'd', 0 },
+ { NOTMUCH_OPT_BOOLEAN, ¶ms.crypto.verify, "verify", 'v', 0 },
+ { 0, 0, 0, 0, 0 }
+ };
+
+ opt_index = parse_arguments (argc, argv, options, 1);
+ if (opt_index < 0) {
+ /* diagnostics already printed */
+ return 1;
+ }
+
+ /* decryption implies verification */
+ if (params.crypto.decrypt)
+ params.crypto.verify = TRUE;
+
+ if (format_sel == NOTMUCH_FORMAT_NOT_SPECIFIED) {
+ /* if part was requested and format was not specified, use format=raw */
+ if (params.part >= 0)
+ format_sel = NOTMUCH_FORMAT_RAW;
+ else
+ format_sel = NOTMUCH_FORMAT_TEXT;
+ }
+
+ switch (format_sel) {
+ case NOTMUCH_FORMAT_JSON:
+ format = &format_json;
+ break;
+ case NOTMUCH_FORMAT_TEXT:
+ format = &format_text;
+ break;
+ case NOTMUCH_FORMAT_MBOX:
+ if (params.part > 0) {
+ fprintf (stderr, "Error: specifying parts is incompatible with mbox output format.\n");
+ return 1;
+ }
+
+ format = &format_mbox;
+ break;
+ case NOTMUCH_FORMAT_RAW:
+ format = &format_raw;
+ /* If --format=raw specified without specifying part, we can only
+ * output single message, so set part=0 */
+ if (params.part < 0)
+ params.part = 0;
+ params.raw = TRUE;
+ break;
+ }
+
+ /* Default is entire-thread = FALSE except for format=json. */
+ if (entire_thread == ENTIRE_THREAD_DEFAULT) {
+ if (format == &format_json)
+ entire_thread = ENTIRE_THREAD_TRUE;
+ else
+ entire_thread = ENTIRE_THREAD_FALSE;
+ }
+
+ if (entire_thread == ENTIRE_THREAD_TRUE)
+ params.entire_thread = TRUE;
+ else
+ params.entire_thread = FALSE;
+
+ config = notmuch_config_open (ctx, NULL, NULL);
+ if (config == NULL)
+ return 1;
+
+ query_string = query_string_from_args (ctx, argc-opt_index, argv+opt_index);
+ if (query_string == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ return 1;
+ }
+
+ if (*query_string == '\0') {
+ fprintf (stderr, "Error: notmuch show requires at least one search term.\n");
+ return 1;
+ }
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much))
+ return 1;
+
+ query = notmuch_query_create (notmuch, query_string);
+ if (query == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ return 1;
+ }
+
+ /* If a single message is requested we do not use search_excludes. */
+ if (params.part >= 0)
+ ret = do_show_single (ctx, query, format, ¶ms);
+ else {
+ /* We always apply set the exclude flag. The
+ * exclude=true|false option controls whether or not we return
+ * threads that only match in an excluded message */
+ const char **search_exclude_tags;
+ size_t search_exclude_tags_length;
+ unsigned int i;
+
+ search_exclude_tags = notmuch_config_get_search_exclude_tags
+ (config, &search_exclude_tags_length);
+ for (i = 0; i < search_exclude_tags_length; i++)
+ notmuch_query_add_tag_exclude (query, search_exclude_tags[i]);
+
+ if (exclude == EXCLUDE_FALSE) {
+ notmuch_query_set_omit_excluded (query, FALSE);
+ params.omit_excluded = FALSE;
+ }
+
+ ret = do_show (ctx, query, format, ¶ms);
+ }