From mboxrd@z Thu Jan 1 00:00:00 1970 Return-Path: Received: from mp1 ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by ms0.migadu.com with LMTPS id Wg22Mbj/62D8ygAAgWs5BA (envelope-from ) for ; Mon, 12 Jul 2021 10:39:20 +0200 Received: from aspmx1.migadu.com ([2001:41d0:2:4a6f::]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits)) by mp1 with LMTPS id CPu7LLj/62C7IQAAbx9fmQ (envelope-from ) for ; Mon, 12 Jul 2021 08:39:20 +0000 Received: from mail.notmuchmail.org (nmbug.tethera.net [144.217.243.247]) (using TLSv1.3 with cipher TLS_AES_256_GCM_SHA384 (256/256 bits) key-exchange X25519 server-signature RSA-PSS (2048 bits) server-digest SHA256) (No client certificate requested) by aspmx1.migadu.com (Postfix) with ESMTPS id 2D46C7177 for ; Mon, 12 Jul 2021 10:39:19 +0200 (CEST) Received: from nmbug.tethera.net (localhost [127.0.0.1]) by mail.notmuchmail.org (Postfix) with ESMTP id B760B290AD; Mon, 12 Jul 2021 04:39:16 -0400 (EDT) Received: from mail-lj1-f171.google.com (mail-lj1-f171.google.com [209.85.208.171]) by mail.notmuchmail.org (Postfix) with ESMTPS id 91580290A8 for ; Mon, 12 Jul 2021 04:39:14 -0400 (EDT) Received: by mail-lj1-f171.google.com with SMTP id b40so22869526ljf.12 for ; Mon, 12 Jul 2021 01:39:14 -0700 (PDT) X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references:mime-version:content-transfer-encoding; bh=+ah42RrtOxkinaQcrTglA/cK+g3WJ/kAzBhgVop8C4A=; b=mBWHYE3xWpZcS9CJE/r0p7UapC3+zt8qdZgVdtbkHQt+FeM45Jdb7KBdbrGqRfLiNz Zi2EX5W7fV86y7IiGDRUiWuOqApofhniigMqkz4KRJgSTDitDNtqW3G8+9rzX7AngXnj kkHzImTRZhj+/wOII5PpEAZ6NTF1hqOAXcohCnkmX9bf5UfX1PZCKJq9r9f4kvD/CzQm AyLqog+H3R0Ua4um2li1/FuY//qy6Q0IYXLivEvIetHv2eBNkZbhgIe/Mb9lzOTGWR5X s88WMEmlSeGs3Ph8gVUNfDrbthedz1edtJa/WtB3E2g9aTSsDhc+1yEOxsfZAPQ7PFDe 0S2Q== X-Gm-Message-State: AOAM532l6ueeGGiOxIxbDYmOdHNHW2ctnMKScMTnQMWg9PPhl3umEaq5 ut2XEzAZeuLxjRqcAC8sKp89G29TwV+N9Q== X-Google-Smtp-Source: ABdhPJzZbDD9rZTynWc00r4I3loWIywU5Zw+q0alJ63pm3FZJ9WFrpQmesxcHzUGOpYXt7BWSAFepA== X-Received: by 2002:a05:651c:329:: with SMTP id b9mr7863524ljp.116.1626079152887; Mon, 12 Jul 2021 01:39:12 -0700 (PDT) Received: from localhost (88-112-11-80.elisa-laajakaista.fi. [88.112.11.80]) by smtp.gmail.com with ESMTPSA id g36sm124419lfv.90.2021.07.12.01.39.11 (version=TLS1_3 cipher=TLS_AES_256_GCM_SHA384 bits=256/256); Mon, 12 Jul 2021 01:39:12 -0700 (PDT) From: Hannu Hartikainen To: notmuch@notmuchmail.org Subject: [PATCH v3 1/2] cli/show: add --format=pretty Date: Mon, 12 Jul 2021 11:38:20 +0300 Message-Id: <20210712083821.442587-2-hannu@hrtk.in> X-Mailer: git-send-email 2.32.0 In-Reply-To: <20210712083821.442587-1-hannu@hrtk.in> References: <20210702203153.47289-1-hannu@hrtk.in>, <20210622123410.323216-1-hannu@hrtk.in> <20210712083821.442587-1-hannu@hrtk.in> MIME-Version: 1.0 Message-ID-Hash: SSNNFYZ5YVNWGHLILPLXK2XZMRYPYEJK X-Message-ID-Hash: SSNNFYZ5YVNWGHLILPLXK2XZMRYPYEJK X-MailFrom: hannu.hartikainen@gmail.com X-Mailman-Rule-Misses: dmarc-mitigation; no-senders; approved; emergency; loop; banned-address; member-moderation; header-match-notmuch.notmuchmail.org-0; nonmember-moderation; administrivia; implicit-dest; max-recipients; max-size; news-moderation; no-subject; suspicious-header CC: Hannu Hartikainen X-Mailman-Version: 3.2.1 Precedence: list List-Id: "Use and development of the notmuch mail system." List-Help: List-Post: List-Subscribe: List-Unsubscribe: Content-Type: text/plain; charset="us-ascii" Content-Transfer-Encoding: 7bit X-Migadu-Flow: FLOW_IN ARC-Message-Signature: i=1; a=rsa-sha256; c=relaxed/relaxed; d=yhetil.org; s=key1; t=1626079160; h=from:from:reply-to:subject:subject:date:date:message-id:message-id: to:to:cc:cc:mime-version:mime-version:content-type:content-type: content-transfer-encoding:content-transfer-encoding: in-reply-to:in-reply-to:references:references:list-id:list-help: list-unsubscribe:list-subscribe:list-post; bh=8A9XkWyRpnX6DbsBEOy4d9eGvUnlQRDNH1RasmJWquQ=; b=PomJ1JEUXx9LWNtrNQ9AAV1CXyzhJu1ajUEVHEc7raHhWJWdYmG7RgsCK4r1EEMyBsDaSZ fWjxl2wi4gRPccTCNuvArVHvUyyBBQ2wyf5hpW/OqpL9rPsenZPUGulOfY5q21/MChWfA/ mx4RT+i64xRHzPIAVr3cH7Q2yydHLL0At67El3t2YKPv1plpamXHuGPagLvi6T6U83+q3E Dqn0hl2HhICXcPvf6uhAw6QkqCrUH8DJj2Wt09xSRYSZw8DVZcnsuW7G8vV17Ms5OBq8h5 lD7JrJBhVGsBQXl/aPsPb3VhevRvbfjLU/Z5WtXXw6/m6RTXBtC20UL/C98hHg== ARC-Seal: i=1; s=key1; d=yhetil.org; t=1626079160; a=rsa-sha256; cv=none; b=Fm6IOZwVTM1eAinA92UFsihxpNwNB5TmlOSQNzvkRzSVsenr8iwL0GxxbjNsgypTcDcQxi utBvzBozLXltyjqJsEZqi5nCU1Une+SsfGWkH/Pdrb0cjibpNc7Tk44rrVMJOINxXoXbR2 dChK6bnfHiGcktOBh+W8G6FPZGTI+sx0cNlGzIVxicIH8IxRXypdiYmYJ1xKP0MM4/XSbc EkrZw8ZNKbIiG+pYqYDJD718S3uPKbMK8/QB1ErqrnR9uXyelwWeHtkLSH9cO6MlrK2R9k Q9RziA40amNu0AqAlvtmpJInrRgBw6qyoEebe+rrzkMR7Sr6A/fIjDjSckHoug== ARC-Authentication-Results: i=1; aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of notmuch-bounces@notmuchmail.org designates 144.217.243.247 as permitted sender) smtp.mailfrom=notmuch-bounces@notmuchmail.org X-Migadu-Spam-Score: -1.07 Authentication-Results: aspmx1.migadu.com; dkim=none; dmarc=none; spf=pass (aspmx1.migadu.com: domain of notmuch-bounces@notmuchmail.org designates 144.217.243.247 as permitted sender) smtp.mailfrom=notmuch-bounces@notmuchmail.org X-Migadu-Queue-Id: 2D46C7177 X-Spam-Score: -1.07 X-Migadu-Scanner: scn0.migadu.com X-TUID: pzZMz/eVmwtB This commit adds the display format `pretty`. It only shows the most important headers, similarly to `--format=text`, and only displays plaintext parts (ie. text/* but not text/html). However, compared to `--format=text` the output is kept as close to raw as possible. The rationale for this feature is twofold: 1. It is useful to be able to view messages in as human-friendly format as possible. 2. The same format should still be machine-readable, too. The email format is mostly human-readable as is. The things difficult for a human eye are the huge amount of headers that are common these days and telling different messages apart when there are many. While human readability is the main goal, another design goal was that piping the output to `git am` works, at least for individual messages sent with `git send-email`. This way the format is suitable to be used as the default, both for reading and piping output to other commands. --- NEWS | 7 ++++ completion/notmuch-completion.bash | 2 +- completion/zsh/_notmuch | 2 +- doc/man1/notmuch-show.rst | 7 +++- notmuch-show.c | 54 ++++++++++++++++++++++++++++++ test/T520-show.sh | 32 ++++++++++++++++++ 6 files changed, 101 insertions(+), 3 deletions(-) diff --git a/NEWS b/NEWS index 3e776009..57826734 100644 --- a/NEWS +++ b/NEWS @@ -1,6 +1,13 @@ Notmuch 0.33 (UNRELEASED) ========================= +CLI +--- + +`notmuch show` now has `--format=pretty`, optimized for reading plain +text emails on the command line. It only shows the most important +headers and plain text parts. + Emacs ----- diff --git a/completion/notmuch-completion.bash b/completion/notmuch-completion.bash index 15425697..86cbbcdc 100644 --- a/completion/notmuch-completion.bash +++ b/completion/notmuch-completion.bash @@ -514,7 +514,7 @@ _notmuch_show() return ;; --format) - COMPREPLY=( $( compgen -W "text json sexp mbox raw" -- "${cur}" ) ) + COMPREPLY=( $( compgen -W "text pretty json sexp mbox raw" -- "${cur}" ) ) return ;; --exclude|--body) diff --git a/completion/zsh/_notmuch b/completion/zsh/_notmuch index e920f10b..5cc386e2 100644 --- a/completion/zsh/_notmuch +++ b/completion/zsh/_notmuch @@ -237,7 +237,7 @@ _notmuch_search() { _notmuch_show() { _arguments -S \ '--entire-thread=[output entire threads]:show thread:(true false)' \ - '--format=[set output format]:output format:(text json sexp mbox raw)' \ + '--format=[set output format]:output format:(text pretty json sexp mbox raw)' \ '--format-version=[set output format version]:format version: ' \ '--part=[output a single decoded mime part]:part number: ' \ '--verify[verify signed MIME parts]' \ diff --git a/doc/man1/notmuch-show.rst b/doc/man1/notmuch-show.rst index 64639174..c68f0b2b 100644 --- a/doc/man1/notmuch-show.rst +++ b/doc/man1/notmuch-show.rst @@ -34,7 +34,7 @@ Supported options for **show** include the matching messages. For ``--format=json`` and ``--format=sexp`` this defaults to true. For other formats, this defaults to false. -.. option:: --format=(text|json|sexp|mbox|raw) +.. option:: --format=(text|pretty|json|sexp|mbox|raw) **text** (default for messages) The default plain-text format has all text-content MIME parts @@ -46,6 +46,11 @@ Supported options for **show** include '}'), to either open or close the component. For a multipart MIME message, these parts will be nested. + **pretty** + The plain-text parts of all matching messages are printed in a + format optimized for readability. Only the most important + headers are displayed. + **json** The output is formatted with Javascript Object Notation (JSON). This format is more robust than the text format for diff --git a/notmuch-show.c b/notmuch-show.c index c8f1a40f..69b1e697 100644 --- a/notmuch-show.c +++ b/notmuch-show.c @@ -606,6 +606,52 @@ format_part_text (const void *ctx, sprinter_t *sp, mime_node_t *node, return NOTMUCH_STATUS_SUCCESS; } +static notmuch_status_t +format_part_pretty (const void *ctx, sprinter_t *sp, mime_node_t *node, + int indent, const notmuch_show_params_t *params) +{ + /* The disposition and content-type metadata are associated with + * the envelope for message parts */ + GMimeObject *meta = node->envelope_part ? ( + GMIME_OBJECT (node->envelope_part) ) : node->part; + GMimeContentType *content_type = g_mime_object_get_content_type (meta); + GMimeStream *stream = params->out_stream; + int i; + + if (GMIME_IS_MESSAGE (node->part)) { + GMimeMessage *message = GMIME_MESSAGE (node->part); + char *recipients_string; + char *date_string; + + g_mime_stream_printf (stream, "Subject: %s\n", g_mime_message_get_subject (message)); + g_mime_stream_printf (stream, "From: %s\n", g_mime_message_get_from_string (message)); + recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_TO); + if (recipients_string) + g_mime_stream_printf (stream, "To: %s\n", recipients_string); + g_free (recipients_string); + recipients_string = g_mime_message_get_address_string (message, GMIME_ADDRESS_TYPE_CC); + if (recipients_string) + g_mime_stream_printf (stream, "Cc: %s\n", recipients_string); + g_free (recipients_string); + date_string = g_mime_message_get_date_string (node, message); + g_mime_stream_printf (stream, "Date: %s\n", date_string); + g_mime_stream_printf (stream, "Message-ID: <%s>\n\n", g_mime_message_get_message_id ( + message)); + } + + if (GMIME_IS_PART (node->part) && + g_mime_content_type_is_type (content_type, "text", "*") && + ! g_mime_content_type_is_type (content_type, "text", "html")) { + show_text_part_content (node->part, stream, 0); + g_mime_stream_printf (stream, "\n"); + } + + for (i = 0; i < node->nchildren; i++) + format_part_pretty (ctx, sp, mime_node_child (node, i), indent, params); + + return NOTMUCH_STATUS_SUCCESS; +} + static void format_omitted_part_meta_sprinter (sprinter_t *sp, GMimeObject *meta, GMimePart *part) { @@ -1192,6 +1238,7 @@ enum { NOTMUCH_FORMAT_JSON, NOTMUCH_FORMAT_SEXP, NOTMUCH_FORMAT_TEXT, + NOTMUCH_FORMAT_PRETTY, NOTMUCH_FORMAT_MBOX, NOTMUCH_FORMAT_RAW }; @@ -1211,6 +1258,11 @@ static const notmuch_show_format_t format_text = { .part = format_part_text, }; +static const notmuch_show_format_t format_pretty = { + .new_sprinter = sprinter_text_create, + .part = format_part_pretty, +}; + static const notmuch_show_format_t format_mbox = { .new_sprinter = sprinter_text_create, .part = format_part_mbox, @@ -1225,6 +1277,7 @@ static const notmuch_show_format_t *formatters[] = { [NOTMUCH_FORMAT_JSON] = &format_json, [NOTMUCH_FORMAT_SEXP] = &format_sexp, [NOTMUCH_FORMAT_TEXT] = &format_text, + [NOTMUCH_FORMAT_PRETTY] = &format_pretty, [NOTMUCH_FORMAT_MBOX] = &format_mbox, [NOTMUCH_FORMAT_RAW] = &format_raw, }; @@ -1259,6 +1312,7 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[]) { .opt_keyword = &format, .name = "format", .keywords = (notmuch_keyword_t []){ { "json", NOTMUCH_FORMAT_JSON }, { "text", NOTMUCH_FORMAT_TEXT }, + { "pretty", NOTMUCH_FORMAT_PRETTY }, { "sexp", NOTMUCH_FORMAT_SEXP }, { "mbox", NOTMUCH_FORMAT_MBOX }, { "raw", NOTMUCH_FORMAT_RAW }, diff --git a/test/T520-show.sh b/test/T520-show.sh index 6f42ca12..c9bcf81d 100755 --- a/test/T520-show.sh +++ b/test/T520-show.sh @@ -27,4 +27,36 @@ notmuch show --entire-thread=true --sort=newest-first $QUERY > EXPECTED notmuch show --entire-thread=true --sort=oldest-first $QUERY > OUTPUT test_expect_equal_file EXPECTED OUTPUT +test_begin_subtest "--format=pretty" +output=$(notmuch show --format=pretty id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com 2>&1 && echo OK) +test_expect_equal "$output" "Subject: [notmuch] preliminary FreeBSD support +From: Alex Botero-Lowry +To: notmuch@notmuchmail.org +Date: Tue, 17 Nov 2009 11:36:14 -0800 +Message-ID: + +I saw the announcement this morning, and was very excited, as I had been +hoping sup would be turned into a library, +since I like the concept more than the UI (I'd rather an emacs interface). + +I did a preliminary compile which worked out fine, but +sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on +FreeBSD, so notmuch_config_open segfaulted. + +Attached is a patch that supplies a default buffer size of 64 in cases where +-1 is returned. + +http://www.opengroup.org/austin/docs/austin_328.txt - seems to indicate this +is acceptable behavior, +and http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically +uses 64 as the +buffer size. + +_______________________________________________ +notmuch mailing list +notmuch@notmuchmail.org +http://notmuchmail.org/mailman/listinfo/notmuch + +OK" + test_done -- 2.32.0