+ sp->end (sp);
+
+ return res != NOTMUCH_STATUS_SUCCESS;
+}
+
+enum {
+ NOTMUCH_FORMAT_NOT_SPECIFIED,
+ NOTMUCH_FORMAT_JSON,
+ NOTMUCH_FORMAT_SEXP,
+ NOTMUCH_FORMAT_TEXT,
+ NOTMUCH_FORMAT_MBOX,
+ NOTMUCH_FORMAT_RAW
+};
+
+static const notmuch_show_format_t format_json = {
+ .new_sprinter = sprinter_json_create,
+ .part = format_part_sprinter_entry,
+};
+
+static const notmuch_show_format_t format_sexp = {
+ .new_sprinter = sprinter_sexp_create,
+ .part = format_part_sprinter_entry,
+};
+
+static const notmuch_show_format_t format_text = {
+ .new_sprinter = sprinter_text_create,
+ .part = format_part_text,
+};
+
+static const notmuch_show_format_t format_mbox = {
+ .new_sprinter = sprinter_text_create,
+ .part = format_part_mbox,
+};
+
+static const notmuch_show_format_t format_raw = {
+ .new_sprinter = sprinter_text_create,
+ .part = format_part_raw,
+};
+
+static const notmuch_show_format_t *formatters[] = {
+ [NOTMUCH_FORMAT_JSON] = &format_json,
+ [NOTMUCH_FORMAT_SEXP] = &format_sexp,
+ [NOTMUCH_FORMAT_TEXT] = &format_text,
+ [NOTMUCH_FORMAT_MBOX] = &format_mbox,
+ [NOTMUCH_FORMAT_RAW] = &format_raw,
+};
+
+int
+notmuch_show_command (notmuch_config_t *config, int argc, char *argv[])
+{
+ notmuch_database_t *notmuch;
+ notmuch_query_t *query;
+ char *query_string;
+ int opt_index, ret;
+ const notmuch_show_format_t *formatter;
+ sprinter_t *sprinter;
+ notmuch_show_params_t params = {
+ .part = -1,
+ .omit_excluded = TRUE,
+ .output_body = TRUE,
+ };
+ int format = NOTMUCH_FORMAT_NOT_SPECIFIED;
+ int exclude = TRUE;
+
+ /* This value corresponds to neither true nor false being passed
+ * on the command line */
+ int entire_thread = -1;
+ notmuch_bool_t single_message;
+
+ notmuch_opt_desc_t options[] = {
+ { NOTMUCH_OPT_KEYWORD, &format, "format", 'f',
+ (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON },
+ { "text", NOTMUCH_FORMAT_TEXT },
+ { "sexp", NOTMUCH_FORMAT_SEXP },
+ { "mbox", NOTMUCH_FORMAT_MBOX },
+ { "raw", NOTMUCH_FORMAT_RAW },
+ { 0, 0 } } },
+ { NOTMUCH_OPT_INT, ¬much_format_version, "format-version", 0, 0 },
+ { NOTMUCH_OPT_BOOLEAN, &exclude, "exclude", 'x', 0 },
+ { NOTMUCH_OPT_BOOLEAN, &entire_thread, "entire-thread", 't', 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 },
+ { NOTMUCH_OPT_BOOLEAN, ¶ms.output_body, "body", 'b', 0 },
+ { NOTMUCH_OPT_BOOLEAN, ¶ms.include_html, "include-html", 0, 0 },
+ { NOTMUCH_OPT_INHERIT, (void *) ¬much_shared_options, NULL, 0, 0 },
+ { 0, 0, 0, 0, 0 }
+ };
+
+ opt_index = parse_arguments (argc, argv, options, 1);
+ if (opt_index < 0)
+ return EXIT_FAILURE;
+
+ notmuch_process_shared_options (argv[0]);
+
+ /* decryption implies verification */
+ if (params.crypto.decrypt)
+ params.crypto.verify = TRUE;
+
+ /* specifying a part implies single message display */
+ single_message = params.part >= 0;
+
+ if (format == NOTMUCH_FORMAT_NOT_SPECIFIED) {
+ /* if part was requested and format was not specified, use format=raw */
+ if (params.part >= 0)
+ format = NOTMUCH_FORMAT_RAW;
+ else
+ format = NOTMUCH_FORMAT_TEXT;
+ }
+
+ if (format == NOTMUCH_FORMAT_MBOX) {
+ if (params.part > 0) {
+ fprintf (stderr, "Error: specifying parts is incompatible with mbox output format.\n");
+ return EXIT_FAILURE;
+ }
+ } else if (format == NOTMUCH_FORMAT_RAW) {
+ /* raw format only supports single message display */
+ single_message = TRUE;
+ }
+
+ notmuch_exit_if_unsupported_format ();
+
+ /* Default is entire-thread = FALSE except for format=json and
+ * format=sexp. */
+ if (entire_thread != FALSE && entire_thread != TRUE) {
+ if (format == NOTMUCH_FORMAT_JSON || format == NOTMUCH_FORMAT_SEXP)
+ params.entire_thread = TRUE;
+ else
+ params.entire_thread = FALSE;
+ } else {
+ params.entire_thread = entire_thread;
+ }
+
+ if (!params.output_body) {
+ if (params.part > 0) {
+ fprintf (stderr, "Warning: --body=false is incompatible with --part > 0. Disabling.\n");
+ params.output_body = TRUE;
+ } else {
+ if (format != NOTMUCH_FORMAT_JSON && format != NOTMUCH_FORMAT_SEXP)
+ fprintf (stderr,
+ "Warning: --body=false only implemented for format=json and format=sexp\n");
+ }
+ }
+
+ if (params.include_html &&
+ (format != NOTMUCH_FORMAT_JSON && format != NOTMUCH_FORMAT_SEXP)) {
+ fprintf (stderr, "Warning: --include-html only implemented for format=json and format=sexp\n");
+ }
+
+ query_string = query_string_from_args (config, argc-opt_index, argv+opt_index);
+ if (query_string == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+
+ if (*query_string == '\0') {
+ fprintf (stderr, "Error: notmuch show requires at least one search term.\n");
+ return EXIT_FAILURE;
+ }
+
+ params.crypto.gpgpath = notmuch_config_get_crypto_gpg_path (config);
+
+ if (notmuch_database_open (notmuch_config_get_database_path (config),
+ NOTMUCH_DATABASE_MODE_READ_ONLY, ¬much))
+ return EXIT_FAILURE;
+
+ notmuch_exit_if_unmatched_db_uuid (notmuch);
+
+ query = notmuch_query_create (notmuch, query_string);
+ if (query == NULL) {
+ fprintf (stderr, "Out of memory\n");
+ return EXIT_FAILURE;
+ }
+
+ /* Create structure printer. */
+ formatter = formatters[format];
+ sprinter = formatter->new_sprinter(config, stdout);
+
+ params.out_stream = g_mime_stream_stdout_new ();
+
+ /* If a single message is requested we do not use search_excludes. */
+ if (single_message) {
+ ret = do_show_single (config, query, formatter, sprinter, ¶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;
+ notmuch_status_t status;
+
+ search_exclude_tags = notmuch_config_get_search_exclude_tags
+ (config, &search_exclude_tags_length);
+
+ for (i = 0; i < search_exclude_tags_length; i++) {
+ status = notmuch_query_add_tag_exclude (query, search_exclude_tags[i]);
+ if (status && status != NOTMUCH_STATUS_IGNORED) {
+ print_status_query ("notmuch show", query, status);
+ ret = -1;
+ goto DONE;
+ }
+ }
+
+ if (exclude == FALSE) {
+ notmuch_query_set_omit_excluded (query, FALSE);
+ params.omit_excluded = FALSE;
+ }
+
+ ret = do_show (config, query, formatter, sprinter, ¶ms);
+ }
+
+ DONE:
+ g_mime_stream_flush (params.out_stream);
+ g_object_unref (params.out_stream);