unofficial mirror of notmuch@notmuchmail.org
 help / color / mirror / code / Atom feed
From: David Bremner <david@tethera.net>
To: notmuch@notmuchmail.org
Subject: [RFC PATCH v2 5/7] WIP: support show --duplicate for structured output
Date: Sun, 19 Jun 2022 20:21:51 -0300	[thread overview]
Message-ID: <20220619232152.846823-6-david@tethera.net> (raw)
In-Reply-To: <20220619232152.846823-1-david@tethera.net>

---
 devel/schemata    |  1 +
 mime-node.c       | 32 +++++++++++++++++++++++---------
 notmuch-client.h  |  5 ++++-
 notmuch-reply.c   |  4 ++--
 notmuch-show.c    | 12 ++++++++----
 test/T160-json.sh |  4 +++-
 test/T170-sexp.sh |  2 +-
 test/T520-show.sh | 36 ++++++++++++++++++++++++++++++++++++
 test/test-lib.sh  |  2 ++
 9 files changed, 80 insertions(+), 18 deletions(-)

diff --git a/devel/schemata b/devel/schemata
index 01810888..66bcdbed 100644
--- a/devel/schemata
+++ b/devel/schemata
@@ -83,6 +83,7 @@ message = {
 
     headers:        headers,
     crypto:         crypto,
+    duplicate:      integer,
     body?:          [part]    # omitted if --body=false
 }
 
diff --git a/mime-node.c b/mime-node.c
index d29c4e48..890c8a0d 100644
--- a/mime-node.c
+++ b/mime-node.c
@@ -78,13 +78,14 @@ mime_node_get_message_crypto_status (mime_node_t *node)
 
 notmuch_status_t
 mime_node_open (const void *ctx, notmuch_message_t *message,
+		int duplicate,
 		_notmuch_crypto_t *crypto, mime_node_t **root_out)
 {
     const char *filename = notmuch_message_get_filename (message);
     mime_node_context_t *mctx;
     mime_node_t *root;
     notmuch_status_t status;
-    int fd;
+    int fd = -1;
 
     root = talloc_zero (ctx, mime_node_t);
     if (root == NULL) {
@@ -103,20 +104,33 @@ mime_node_open (const void *ctx, notmuch_message_t *message,
     talloc_set_destructor (mctx, _mime_node_context_free);
 
     /* Fast path */
-    fd = open (filename, O_RDONLY);
+    if (duplicate <= 0)
+	fd = open (filename, O_RDONLY);
     if (fd == -1) {
-	/* Slow path - for some reason the first file in the list is
-	 * not available anymore. This is clearly a problem in the
+	/* Slow path - Either we are trying to open a specific file, or
+	 * for some reason the first file in the list is
+	 * not available anymore. The latter is clearly a problem in the
 	 * database, but we are not going to let this problem be a
 	 * show stopper */
 	notmuch_filenames_t *filenames;
+	int i=1;
+
 	for (filenames = notmuch_message_get_filenames (message);
 	     notmuch_filenames_valid (filenames);
-	     notmuch_filenames_move_to_next (filenames)) {
-	    filename = notmuch_filenames_get (filenames);
-	    fd = open (filename, O_RDONLY);
-	    if (fd != -1)
-		break;
+	     notmuch_filenames_move_to_next (filenames), i++) {
+	    if (i>= duplicate) {
+		filename = notmuch_filenames_get (filenames);
+		fd = open (filename, O_RDONLY);
+		if (fd != -1) {
+		    break;
+		} else {
+		    if (duplicate > 0) {
+			fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
+			status = NOTMUCH_STATUS_FILE_ERROR;
+			goto DONE;
+		    }
+		}
+	    }
 	}
 
 	talloc_free (filenames);
diff --git a/notmuch-client.h b/notmuch-client.h
index f8f987e7..21b49908 100644
--- a/notmuch-client.h
+++ b/notmuch-client.h
@@ -230,6 +230,7 @@ show_one_part (const char *filename, int part);
 
 void
 format_part_sprinter (const void *ctx, struct sprinter *sp, mime_node_t *node,
+		      int duplicate,
 		      bool output_body,
 		      bool include_html);
 
@@ -389,7 +390,8 @@ struct mime_node {
 };
 
 /* Construct a new MIME node pointing to the root message part of
- * message. If crypto->verify is true, signed child parts will be
+ * message. Use the duplicate-th filename if that parameter is
+ * positive. If crypto->verify is true, signed child parts will be
  * verified. If crypto->decrypt is NOTMUCH_DECRYPT_TRUE, encrypted
  * child parts will be decrypted using either stored session keys or
  * asymmetric crypto.  If crypto->decrypt is NOTMUCH_DECRYPT_AUTO,
@@ -407,6 +409,7 @@ struct mime_node {
  */
 notmuch_status_t
 mime_node_open (const void *ctx, notmuch_message_t *message,
+		int duplicate,
 		_notmuch_crypto_t *crypto, mime_node_t **node_out);
 
 /* Return a new MIME node for the requested child part of parent.
diff --git a/notmuch-reply.c b/notmuch-reply.c
index 9fca22db..96cff692 100644
--- a/notmuch-reply.c
+++ b/notmuch-reply.c
@@ -663,7 +663,7 @@ do_reply (notmuch_database_t *notmuch,
 	 notmuch_messages_move_to_next (messages)) {
 	message = notmuch_messages_get (messages);
 
-	if (mime_node_open (notmuch, message, &params->crypto, &node))
+	if (mime_node_open (notmuch, message, 0, &params->crypto, &node))
 	    return 1;
 
 	reply = create_reply_message (notmuch, message,
@@ -683,7 +683,7 @@ do_reply (notmuch_database_t *notmuch,
 
 	    /* Start the original */
 	    sp->map_key (sp, "original");
-	    format_part_sprinter (notmuch, sp, node, true, false);
+	    format_part_sprinter (notmuch, sp, node, -1, true, false);
 
 	    /* End */
 	    sp->end (sp);
diff --git a/notmuch-show.c b/notmuch-show.c
index 7b57aab3..c162cc57 100644
--- a/notmuch-show.c
+++ b/notmuch-show.c
@@ -672,6 +672,7 @@ format_omitted_part_meta_sprinter (sprinter_t *sp, GMimeObject *meta, GMimePart
 
 void
 format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
+		      int duplicate,
 		      bool output_body,
 		      bool include_html)
 {
@@ -683,10 +684,13 @@ format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
 	sp->begin_map (sp);
 	format_message_sprinter (sp, node->envelope_file);
 
+	sp->map_key (sp, "duplicate");
+	sp->integer (sp, duplicate > 0 ? duplicate : 1);
+
 	if (output_body) {
 	    sp->map_key (sp, "body");
 	    sp->begin_list (sp);
-	    format_part_sprinter (ctx, sp, mime_node_child (node, 0), true, include_html);
+	    format_part_sprinter (ctx, sp, mime_node_child (node, 0), -1, true, include_html);
 	    sp->end (sp);
 	}
 
@@ -850,7 +854,7 @@ format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
     }
 
     for (i = 0; i < node->nchildren; i++)
-	format_part_sprinter (ctx, sp, mime_node_child (node, i), true, include_html);
+	format_part_sprinter (ctx, sp, mime_node_child (node, i), -1, true, include_html);
 
     /* Close content structures */
     for (i = 0; i < nclose; i++)
@@ -864,7 +868,7 @@ format_part_sprinter_entry (const void *ctx, sprinter_t *sp,
 			    mime_node_t *node, unused (int indent),
 			    const notmuch_show_params_t *params)
 {
-    format_part_sprinter (ctx, sp, node, params->output_body, params->include_html);
+    format_part_sprinter (ctx, sp, node, params->duplicate, params->output_body, params->include_html);
 
     return NOTMUCH_STATUS_SUCCESS;
 }
@@ -1018,7 +1022,7 @@ show_message (void *ctx,
 	session_key_count_error = notmuch_message_count_properties (message, "session-key",
 								    &session_keys);
 
-    status = mime_node_open (local, message, &(params->crypto), &root);
+    status = mime_node_open (local, message, params->duplicate, &(params->crypto), &root);
     if (status)
 	goto DONE;
     part = mime_node_seek_dfs (root, (params->part < 0 ? 0 : params->part));
diff --git a/test/T160-json.sh b/test/T160-json.sh
index e1252353..4a797f6a 100755
--- a/test/T160-json.sh
+++ b/test/T160-json.sh
@@ -49,7 +49,7 @@ output=$(notmuch show --format=json "id:$id")
 filename=$(notmuch search --output=files "id:$id")
 # Get length of README after base64-encoding, minus additional newline.
 attachment_length=$(( $(base64 $NOTMUCH_SRCDIR/test/README | wc -c) - 1 ))
-test_expect_equal_json "$output" "[[[{\"id\": \"$id\",  \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"$filename\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"content-length\": $attachment_length, \"content-transfer-encoding\": \"base64\", \"content-disposition\": \"inline\", \"filename\": \"README\"}]}]}, []]]]"
+test_expect_equal_json "$output" "[[[{\"id\": \"$id\", \"duplicate\": 1, \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"$filename\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"content-length\": $attachment_length, \"content-transfer-encoding\": \"base64\", \"content-disposition\": \"inline\", \"filename\": \"README\"}]}]}, []]]]"
 
 test_begin_subtest "Search message: json, utf-8"
 add_message "[subject]=\"json-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
@@ -97,6 +97,7 @@ cat <<EOF > EXPECTED
         [
             {
                 "date_relative": "2001-01-05",
+		"duplicate": 1,
                 "excluded": false,
                 "filename": [
                     "${MAIL_DIR}/copy1",
@@ -132,6 +133,7 @@ cat <<EOF > EXPECTED
         [
             {
                 "date_relative": "2001-01-05",
+		"duplicate": 1,
                 "excluded": false,
                 "filename": "${MAIL_DIR}/copy1",
                 "headers": {
diff --git a/test/T170-sexp.sh b/test/T170-sexp.sh
index 76e07481..0be94bd2 100755
--- a/test/T170-sexp.sh
+++ b/test/T170-sexp.sh
@@ -45,7 +45,7 @@ output=$(notmuch show --format=sexp "id:$id")
 filename=$(notmuch search --output=files "id:$id")
 # Get length of README after base64-encoding, minus additional newline.
 attachment_length=$(( $(base64 $NOTMUCH_SRCDIR/test/README | wc -c) - 1 ))
-test_expect_equal "$output" "((((:id \"$id\" :match t :excluded nil :filename (\"$filename\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\") :body ((:id 1 :content-type \"multipart/mixed\" :content ((:id 2 :content-type \"text/plain\" :content \"This is a test message with inline attachment with a filename\") (:id 3 :content-type \"application/octet-stream\" :content-disposition \"inline\" :filename \"README\" :content-transfer-encoding \"base64\" :content-length $attachment_length)))) :crypto () :headers (:Subject \"sexp-show-inline-attachment-filename\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"test_suite@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+test_expect_equal "$output" "((((:id \"$id\" :match t :excluded nil :filename (\"$filename\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\") :duplicate 1 :body ((:id 1 :content-type \"multipart/mixed\" :content ((:id 2 :content-type \"text/plain\" :content \"This is a test message with inline attachment with a filename\") (:id 3 :content-type \"application/octet-stream\" :content-disposition \"inline\" :filename \"README\" :content-transfer-encoding \"base64\" :content-length $attachment_length)))) :crypto () :headers (:Subject \"sexp-show-inline-attachment-filename\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"test_suite@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
 
 test_begin_subtest "show extra headers"
 add_message "[subject]=\"extra-headers\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[in-reply-to]=\"<parent@notmuch-test-suite>\"" "[body]=\"extra-headers test\""\
diff --git a/test/T520-show.sh b/test/T520-show.sh
index 12bde6c7..c40a1b64 100755
--- a/test/T520-show.sh
+++ b/test/T520-show.sh
@@ -45,4 +45,40 @@ if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
 
 fi
 
+add_email_corpus duplicate
+
+ID1=debian/2.6.1.dfsg-4-1-g87ea161@87ea161e851dfb1ea324af00e4ecfccc18875e15
+
+test_begin_subtest "format json, --duplicate=2, duplicate key"
+output=$(notmuch show --format=json --duplicate=2 id:${ID1})
+test_json_nodes <<<"$output" "dup:['duplicate']=2"
+
+test_begin_subtest "format json, subject, --duplicate=1"
+output=$(notmuch show --format=json --duplicate=1 id:${ID1})
+file=$(notmuch search --output=files id:${ID1} | head -n 1)
+subject=$(sed -n 's/^Subject: \(.*\)$/\1/p' < $file)
+test_json_nodes <<<"$output" "subject:['headers']['Subject']=\"$subject\""
+
+test_begin_subtest "format json, subject, --duplicate=2"
+output=$(notmuch show --format=json --duplicate=2 id:${ID1})
+file=$(notmuch search --output=files id:${ID1} | tail -n 1)
+subject=$(sed -n 's/^Subject: \(.*\)$/\1/p' < $file)
+test_json_nodes <<<"$output" "subject:['headers']['Subject']=\"$subject\""
+
+ID2=87r2geywh9.fsf@tethera.net
+for dup in {1..2}; do
+    test_begin_subtest "format json, body, --duplicate=${dup}"
+    output=$(notmuch show --format=json --duplicate=${dup} id:${ID2} | \
+	     python $NOTMUCH_SRCDIR/test/json_check_nodes.py "body:['body'][0]['content']" | \
+	     grep '^# body')
+    test_expect_equal "$output" "# body ${dup}"
+done
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+for dup in {1..5}; do
+    test_begin_subtest "format json, --duplicate=${dup}, 'duplicate' key"
+    output=$(notmuch show --format=json --duplicate=${dup} id:${ID3})
+    test_json_nodes <<<"$output" "dup:['duplicate']=${dup}"
+done
+
 test_done
diff --git a/test/test-lib.sh b/test/test-lib.sh
index 4da7825f..acddf026 100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -522,6 +522,7 @@ notmuch_json_show_sanitize () {
 	-e 's|"id": "[^"]*",|"id": "XXXXX",|g' \
 	-e 's|"Date": "Fri, 05 Jan 2001 [^"]*0000"|"Date": "GENERATED_DATE"|g' \
 	-e 's|"filename": "signature.asc",||g' \
+	-e 's|"duplicate": 1,||g' \
 	-e 's|"filename": \["/[^"]*"\],|"filename": \["YYYYY"\],|g' \
 	-e 's|"timestamp": 97.......|"timestamp": 42|g' \
 	-e 's|"content-length": [1-9][0-9]*|"content-length": "NONZERO"|g'
@@ -532,6 +533,7 @@ notmuch_sexp_show_sanitize () {
 	-e 's|:id "[^"]*"|:id "XXXXX"|g' \
 	-e 's|:Date "Sat, 01 Jan 2000 [^"]*0000"|:Date "GENERATED_DATE"|g' \
 	-e 's|:filename "signature.asc"||g' \
+	-e 's|:duplicate 1 ||g' \
 	-e 's|:filename ("/[^"]*")|:filename ("YYYYY")|g' \
 	-e 's|:timestamp 9........|:timestamp 42|g' \
 	-e 's|:content-length [1-9][0-9]*|:content-length "NONZERO"|g'
-- 
2.35.2
\r

  parent reply	other threads:[~2022-06-19 23:22 UTC|newest]

Thread overview: 9+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2022-06-19 23:21 v2 WIP commands to choose duplicates in emacs interface David Bremner
2022-06-19 23:21 ` [RFC PATCH v2 1/7] test: use notmuch_json_show_sanitize more places David Bremner
2022-06-19 23:21 ` [RFC PATCH v2 2/7] test: define and use notmuch_sexp_*_sanitize functions David Bremner
2022-06-19 23:21 ` [RFC PATCH v2 3/7] test: add new corpus of duplicate messages David Bremner
2022-06-19 23:21 ` [RFC PATCH v2 4/7] CLI/show: WIP support --duplicate for raw output David Bremner
2022-06-19 23:21 ` David Bremner [this message]
2022-06-19 23:21 ` [RFC PATCH v2 6/7] WIP/emacs: replace message with different duplicate David Bremner
2022-06-19 23:21 ` [RFC PATCH v2 7/7] WIP/emacs: display count of duplicates in headerline David Bremner
2022-06-29 12:15 ` v2 WIP commands to choose duplicates in emacs interface David Bremner

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

  List information: https://notmuchmail.org/

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20220619232152.846823-6-david@tethera.net \
    --to=david@tethera.net \
    --cc=notmuch@notmuchmail.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
Code repositories for project(s) associated with this public inbox

	https://yhetil.org/notmuch.git/

This is a public inbox, see mirroring instructions
for how to clone and mirror all data and code used for this inbox;
as well as URLs for read-only IMAP folder(s) and NNTP newsgroup(s).