+Notmuch 0.29 (UNRELEASED)
+=========================
+
+Command Line Interface
+----------------------
+
+`notmuch show` now supports --body=false and --include-html with
+--format=text
+
Notmuch 0.28.1 (2019-02-01)
===========================
EMACSETCDIR="\$(prefix)/share/emacs/site-lisp"
fi
-printf "Checking if emacs is available... "
-if emacs --quick --batch > /dev/null 2>&1; then
+printf "Checking if emacs (>= 24) is available... "
+if emacs --quick --batch --eval '(if (< emacs-major-version 24) (kill-emacs 1))' > /dev/null 2>&1; then
printf "Yes.\n"
have_emacs=1
else
debugger_is_active (void)
{
char buf[1024];
+ char buf2[1024];
if (RUNNING_ON_VALGRIND)
return true;
sprintf (buf, "/proc/%d/exe", getppid ());
- if (readlink (buf, buf, sizeof (buf)) != -1 &&
- strncmp (basename (buf), "gdb", 3) == 0)
+ if (readlink (buf, buf2, sizeof (buf2)) != -1 &&
+ strncmp (basename (buf2), "gdb", 3) == 0)
{
return true;
}
%.gz: %
rm -f $@ && gzip --stdout $^ > $@
+ifeq ($(WITH_EMACS),1)
+$(DOCBUILDDIR)/.roff.stamp sphinx-html sphinx-texinfo: docstring.stamp
+endif
+
sphinx-html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DOCBUILDDIR)/html
``--exclude=(true|false)``
A message is called "excluded" if it matches at least one tag in
- search.tag\_exclude that does not appear explicitly in the search
+ search.exclude\_tags that does not appear explicitly in the search
terms. This option specifies whether to omit excluded messages in
the search process.
same message-id).
``--exclude=(true|false)``
- Specify whether to omit messages matching search.tag\_exclude from
+ Specify whether to omit messages matching search.exclude\_tags from
the count (the default) or not.
``--batch``
``--exclude=(true|false|all|flag)``
A message is called "excluded" if it matches at least one tag in
- search.tag\_exclude that does not appear explicitly in the search
+ search.exclude\_tags that does not appear explicitly in the search
terms. This option specifies whether to omit excluded messages in
the search process.
Default: ``auto``
``--exclude=(true|false)``
- Specify whether to omit threads only matching search.tag\_exclude
+ Specify whether to omit threads only matching search.exclude\_tags
from the search results (the default) or not. In either case the
excluded message will be marked with the exclude flag (except when
output=mbox when there is nowhere to put the flag).
``--body=(true|false)``
If true (the default) **notmuch show** includes the bodies of the
messages in the output; if false, bodies are omitted.
- ``--body=false`` is only implemented for the json and sexp formats
- and it is incompatible with ``--part > 0.``
+ ``--body=false`` is only implemented for the text, json and sexp
+ formats and it is incompatible with ``--part > 0.``
This is useful if the caller only needs the headers as body-less
output is much faster and substantially smaller.
``--include-html``
- Include "text/html" parts as part of the output (currently only
- supported with ``--format=json`` and ``--format=sexp``). By default,
- unless ``--part=N`` is used to select a specific part or
- ``--include-html`` is used to include all "text/html" parts, no
- part with content type "text/html" is included in the output.
+ Include "text/html" parts as part of the output (currently
+ only supported with ``--format=text``, ``--format=json`` and
+ ``--format=sexp``). By default, unless ``--part=N`` is used to
+ select a specific part or ``--include-html`` is used to include all
+ "text/html" parts, no part with content type "text/html" is included
+ in the output.
A common use of **notmuch show** is to display a single thread of email
messages. For this, use a search term of "thread:<thread-id>" as can be
advance to the next message, or advance to the next thread (if
already on the last message of a thread).
+``c``
+ :ref:`show-copy`
+
``N``
Move to next message
``?``
Display full set of key bindings
+Display of messages can be controlled by the following variables
+
+:index:`notmuch-message-headers`
+ |docstring::notmuch-message-headers|
+
+:index:`notmuch-message-headers-visible`
+ |docstring::notmuch-message-headers-visible|
+
+.. _show-copy:
+
+Copy to kill-ring
+-----------------
+
+You can use the usually Emacs ways of copying text to the kill-ring,
+but notmuch also provides some shortcuts. These keys are available in
+:ref:`notmuch-show`, and :ref:`notmuch-tree`. A subset are available
+in :ref:`notmuch-search`.
+
+``c F`` ``notmuch-show-stash-filename``
+ |docstring::notmuch-show-stash-filename|
+
+``c G`` ``notmuch-show-stash-git-send-email``
+ |docstring::notmuch-show-stash-git-send-email|
+
+``c I`` ``notmuch-show-stash-message-id-stripped``
+ |docstring::notmuch-show-stash-message-id-stripped|
+
+``c L`` ``notmuch-show-stash-mlarchive-link-and-go``
+ |docstring::notmuch-show-stash-mlarchive-link-and-go|
+
+``c T`` ``notmuch-show-stash-tags``
+ |docstring::notmuch-show-stash-tags|
+
+``c c`` ``notmuch-show-stash-cc``
+ |docstring::notmuch-show-stash-cc|
+
+``c d`` ``notmuch-show-stash-date``
+ |docstring::notmuch-show-stash-date|
+
+``c f`` ``notmuch-show-stash-from``
+ |docstring::notmuch-show-stash-from|
+
+``c i`` ``notmuch-show-stash-message-id``
+ |docstring::notmuch-show-stash-message-id|
+
+``c l`` ``notmuch-show-stash-mlarchive-link``
+ |docstring::notmuch-show-stash-mlarchive-link|
+
+``c s`` ``notmuch-show-stash-subject``
+ |docstring::notmuch-show-stash-subject|
+
+``c t`` ``notmuch-show-stash-to``
+ |docstring::notmuch-show-stash-to|
+
+``c ?``
+ Show all available copying commands
+
.. _notmuch-tree:
notmuch-tree
message giving the relative date, the author, subject, and any
tags.
+``c``
+ :ref:`show-copy`
+
``<return>``
Displays that message.
:index:`notmuch-tagging-keys`
- A list of keys and corresponding tagging operations.
+ |docstring::notmuch-tagging-keys|
+
+Buffer navigation
+=================
+
+:index:`notmuch-cycle-notmuch-buffers`
+ |docstring::notmuch-cycle-notmuch-buffers|
Configuration
=============
--------------
:index:`notmuch-poll`
+ |docstring::notmuch-poll|
:index:`notmuch-poll-script`
+ |docstring::notmuch-poll-script|
Init File
---------
order). Most often users create ``~/.emacs.d/notmuch-config.el`` and just
work with it. If Emacs was invoked with the ``-q`` or ``--no-init-file``
options, ``notmuch-init-file`` is not read.
+
+.. include:: ../emacs/rstdoc.rsti
+
+.. include:: ../emacs/notmuch.rsti
+
+.. include:: ../emacs/notmuch-lib.rsti
+
+.. include:: ../emacs/notmuch-show.rsti
+
+.. include:: ../emacs/notmuch-tag.rsti
/.eldeps*
/*.elc
+/*.rsti
/notmuch-version.el
/notmuch-pkg.el
$(srcdir)/$(dir)/notmuch-logo.png
emacs_bytecode = $(emacs_sources:.el=.elc)
+emacs_docstrings = $(emacs_sources:.el=.rsti)
+
+docstring.stamp: ${emacs_docstrings}
+ touch $@
# Because of defmacro's and defsubst's, we have to account for load
# dependencies between Elisp files when byte compiling. Otherwise,
ifeq ($(HAVE_EMACS),1)
%.elc: %.el $(global_deps)
$(call quiet,EMACS) --directory emacs -batch -f batch-byte-compile $<
+%.rsti: %.el
+ $(call quiet,EMACS) -batch -L emacs -l rstdoc -f rstdoc-batch-extract $< $@
endif
elpa: $(ELPA_FILE)
ifeq ($(WITH_EMACS),1)
ifeq ($(HAVE_EMACS),1)
-all: $(emacs_bytecode)
+all: $(emacs_bytecode) $(emacs_docstrings)
install-emacs: $(emacs_bytecode)
endif
-update-desktop-database "$(DESTDIR)$(desktop_dir)"
endif
-CLEAN := $(CLEAN) $(emacs_bytecode) $(dir)/notmuch-version.el $(dir)/notmuch-pkg.el
+CLEAN := $(CLEAN) $(emacs_bytecode) $(dir)/notmuch-version.el $(dir)/notmuch-pkg.el \
+ $(emacs_docstrings) docstring.stamp
(if window
;; Raise the frame already displaying the message buffer.
(progn
- (gnus-select-frame-set-input-focus (window-frame window))
+ (select-frame-set-input-focus (window-frame window))
(select-window window))
(funcall switch-function buffer)
(set-buffer buffer))
(unless (assq 'notmuch-show-mode w3m-cid-retrieve-function-alist)
(push (cons 'notmuch-show-mode #'notmuch-show--cid-w3m-retrieve)
w3m-cid-retrieve-function-alist)))
- (setq mm-inline-text-html-with-images t))
+ (setq mm-html-inhibit-images nil))
(defvar w3m-current-buffer) ;; From `w3m.el'.
(defun notmuch-show--cid-w3m-retrieve (url &rest args)
;;; Code:
(require 'coolj)
-
+(require 'notmuch-lib)
(declare-function notmuch-show-insert-bodypart "notmuch-show" (msg part depth &optional hide))
(defvar notmuch-show-indent-messages-width)
Here is an example of how to color search results based on tags.
(the following text would be placed in your ~/.emacs file):
- (setq notmuch-search-line-faces '((\"unread\" . (:foreground \"green\"))
+ (setq notmuch-search-line-faces \\='((\"unread\" . (:foreground \"green\"))
(\"deleted\" . (:foreground \"red\"
:background \"blue\"))))
Runs a new search matching only messages that match both the
current search results AND that are tagged with the given tag."
(interactive
- (list (notmuch-select-tag-with-completion "Filter by tag: ")))
+ (list (notmuch-select-tag-with-completion "Filter by tag: " notmuch-search-query-string)))
(notmuch-search (concat notmuch-search-query-string " and tag:" tag) notmuch-search-oldest-first))
;;;###autoload
--- /dev/null
+;;; rstdoc.el --- help generate documentation from docstrings -*-lexical-binding: t-*-
+
+;; Copyright (C) 2018 David Bremner
+
+;; Author: David Bremner <david@tethera.net>
+;; Created: 26 May 2018
+;; Keywords: emacs lisp, documentation
+;; Homepage: https://notmuchmail.org
+
+;; This file is not part of GNU Emacs.
+
+;; rstdoc.el is free software: you can redistribute it and/or modify it
+;; under the terms of the GNU General Public License as published by
+;; the Free Software Foundation, either version 3 of the License, or
+;; (at your option) any later version.
+;;
+;; rstdoc.el is distributed in the hope that it will be useful, but
+;; WITHOUT ANY WARRANTY; without even the implied warranty of
+;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+;; General Public License for more details.
+;;
+;; You should have received a copy of the GNU General Public License
+;; along with rstdoc.el. If not, see <https://www.gnu.org/licenses/>.
+;;
+
+;;; Commentary:
+;;
+
+;; Rstdoc provides a facility to extract all of the docstrings defined in
+;; an elisp source file. Usage:
+;;
+;; emacs -Q --batch -L . -l rstdoc -f rstdoc-batch-extract foo.el foo.rsti
+
+;;; Code:
+
+(provide 'rstdoc)
+
+(defun rstdoc-batch-extract ()
+ "Extract docstrings to and from the files on the command line"
+ (apply #'rstdoc-extract command-line-args-left))
+
+(defun rstdoc-extract (in-file out-file)
+ "Write docstrings from IN-FILE to OUT-FILE"
+ (load-file in-file)
+ (let* ((definitions (cdr (assoc (expand-file-name in-file) load-history)))
+ (doc-hash (make-hash-table :test 'eq)))
+ (mapc
+ (lambda (elt)
+ (let ((pair
+ (pcase elt
+ (`(defun . ,name) (cons name (documentation name)))
+ (`(,_ . ,_) nil)
+ (sym (cons sym (get sym 'variable-documentation))))))
+ (when (and pair (cdr pair))
+ (puthash (car pair) (cdr pair) doc-hash))))
+ definitions)
+ (with-temp-buffer
+ (maphash
+ (lambda (key val)
+ (rstdoc--insert-docstring key val))
+ doc-hash)
+ (write-region (point-min) (point-max) out-file))))
+
+(defun rstdoc--insert-docstring (symbol docstring)
+ (insert (format "\n.. |docstring::%s| replace::\n" symbol))
+ (insert (replace-regexp-in-string "^" " " (rstdoc--rst-quote-string docstring)))
+ (insert "\n"))
+
+(defvar rst--escape-alist
+ '( ("\\\\='" . "\\\\'")
+ ("\\([^\\]\\)'" . "\\1`")
+ ("^[[:space:]\t]*$" . "|br|")
+ ("^[[:space:]\t]" . "|indent| "))
+ "list of (regex . replacement) pairs")
+
+(defun rstdoc--rst-quote-string (str)
+ (with-temp-buffer
+ (insert str)
+ (dolist (pair rst--escape-alist)
+ (goto-char (point-min))
+ (while (re-search-forward (car pair) nil t)
+ (replace-match (cdr pair))))
+ (buffer-substring (point-min) (point-max))))
+
+;;; rstdoc.el ends here
--- /dev/null
+.. -*- rst -*-
+
+.. |br| replace:: |br-texinfo| |br-html|
+
+.. |br-texinfo| raw:: texinfo
+
+ @* @*
+
+.. |br-html| raw:: html
+
+ <br /><br />
+
+.. |indent| replace:: |indent-texinfo| |indent-html|
+
+.. |indent-texinfo| raw:: texinfo
+
+ @* @ @ @ @
+
+.. |indent-html| raw:: html
+
+ <br />
#include <stdbool.h>
#include "gmime-filter-reply.h"
+#include "notmuch-client.h"
/**
* SECTION: gmime-filter-reply
**/
-static void g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass);
+static void g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass, void *class_data);
static void g_mime_filter_reply_init (GMimeFilterReply *filter, GMimeFilterReplyClass *klass);
static void g_mime_filter_reply_finalize (GObject *object);
if (!type) {
static const GTypeInfo info = {
- sizeof (GMimeFilterReplyClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) g_mime_filter_reply_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (GMimeFilterReply),
- 0, /* n_preallocs */
- (GInstanceInitFunc) g_mime_filter_reply_init,
- NULL /* value_table */
+ .class_size = sizeof (GMimeFilterReplyClass),
+ .base_init = NULL,
+ .base_finalize = NULL,
+ .class_init = (GClassInitFunc) g_mime_filter_reply_class_init,
+ .class_finalize = NULL,
+ .class_data = NULL,
+ .instance_size = sizeof (GMimeFilterReply),
+ .n_preallocs = 0,
+ .instance_init = (GInstanceInitFunc) g_mime_filter_reply_init,
+ .value_table = NULL,
};
type = g_type_register_static (GMIME_TYPE_FILTER, "GMimeFilterReply", &info, (GTypeFlags) 0);
static void
-g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass)
+g_mime_filter_reply_class_init (GMimeFilterReplyClass *klass, unused (void *class_data))
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
static GMimeFilterClass *parent_class = NULL;
static void
-notmuch_filter_discard_non_term_class_init (NotmuchFilterDiscardNonTermClass *klass)
+notmuch_filter_discard_non_term_class_init (NotmuchFilterDiscardNonTermClass *klass,
+ unused (void *class_data))
{
GObjectClass *object_class = G_OBJECT_CLASS (klass);
GMimeFilterClass *filter_class = GMIME_FILTER_CLASS (klass);
if (!type) {
static const GTypeInfo info = {
- sizeof (NotmuchFilterDiscardNonTermClass),
- NULL, /* base_class_init */
- NULL, /* base_class_finalize */
- (GClassInitFunc) notmuch_filter_discard_non_term_class_init,
- NULL, /* class_finalize */
- NULL, /* class_data */
- sizeof (NotmuchFilterDiscardNonTerm),
- 0, /* n_preallocs */
- NULL, /* instance_init */
- NULL /* value_table */
+ .class_size = sizeof (NotmuchFilterDiscardNonTermClass),
+ .base_init = NULL,
+ .base_finalize = NULL,
+ .class_init = (GClassInitFunc) notmuch_filter_discard_non_term_class_init,
+ .class_finalize = NULL,
+ .class_data = NULL,
+ .instance_size = sizeof (NotmuchFilterDiscardNonTerm),
+ .n_preallocs = 0,
+ .instance_init = NULL,
+ .value_table = NULL,
};
type = g_type_register_static (GMIME_TYPE_FILTER, "NotmuchFilterDiscardNonTerm", &info, (GTypeFlags) 0);
* notmuch_query_t *query;
* notmuch_threads_t *threads;
* notmuch_thread_t *thread;
+ * notmuch_status_t stat;
*
* query = notmuch_query_create (database, query_string);
*
- * for (threads = notmuch_query_search_threads (query);
+ * for (stat = notmuch_query_search_threads (query, &threads);
+ * stat == NOTMUCH_STATUS_SUCCESS &&
* notmuch_threads_valid (threads);
* notmuch_threads_move_to_next (threads))
* {
* If there are no replies to 'message', this function will return
* NULL. (Note that notmuch_messages_valid will accept that NULL
* value as legitimate, and simply return FALSE for it.)
+ *
+ * The returned list will be destroyed when the thread is destroyed.
*/
notmuch_messages_t *
notmuch_message_get_replies (notmuch_message_t *message);
}
}
+ /* If no recipients were added but we found one of the user's
+ * addresses to use as a from address then the message is from the
+ * user to the user - add the discovered from address to the list
+ * of recipients so that the reply goes back to the user.
+ */
+ if (n == 0 && from_addr)
+ g_mime_message_add_recipient (reply, GMIME_ADDRESS_TYPE_TO, NULL, from_addr);
+
return from_addr;
}
g_mime_stream_printf (stream, "Date: %s\n", date_string);
g_mime_stream_printf (stream, "\fheader}\n");
+ if (!params->output_body)
+ {
+ g_mime_stream_printf (stream, "\f%s}\n", part_type);
+ return NOTMUCH_STATUS_SUCCESS;
+ }
g_mime_stream_printf (stream, "\fbody{\n");
}
if (leaf) {
if (g_mime_content_type_is_type (content_type, "text", "*") &&
- !g_mime_content_type_is_type (content_type, "text", "html"))
+ (params->include_html ||
+ ! g_mime_content_type_is_type (content_type, "text", "html")))
{
show_text_part_content (node->part, stream, 0);
} else {
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)
+ if (format != NOTMUCH_FORMAT_TEXT &&
+ format != NOTMUCH_FORMAT_JSON &&
+ format != NOTMUCH_FORMAT_SEXP)
fprintf (stderr,
- "Warning: --body=false only implemented for format=json and format=sexp\n");
+ "Warning: --body=false only implemented for format=text, 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");
+ (format != NOTMUCH_FORMAT_TEXT &&
+ format != NOTMUCH_FORMAT_JSON &&
+ format != NOTMUCH_FORMAT_SEXP)) {
+ fprintf (stderr, "Warning: --include-html only implemented for format=text, format=json and format=sexp\n");
}
query_string = query_string_from_args (config, argc-opt_index, argv+opt_index);
/test-results
/ghost-report
/tmp.*
+/message-id-parse
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "--format=text --part=0 --body=false, message header"
+notmuch show --format=text --part=0 --body=false 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fmessage{ id:87liy5ap00.fsf@yoom.home.cworth.org depth:0 match:1 excluded:0 filename:${MAIL_DIR}/multipart
+\fheader{
+Carl Worth <cworth@cworth.org> (2001-01-05) (attachment inbox signed unread)
+Subject: Multipart message
+From: Carl Worth <cworth@cworth.org>
+To: cworth@cworth.org
+Date: Fri, 05 Jan 2001 15:43:57 +0000
+\fheader}
+\fmessage}
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "--format=text --part=1, message body"
notmuch show --format=text --part=1 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
cat <<EOF >EXPECTED
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "--format=text --include-html --part=5, rfc822's html part"
+notmuch show --format=text --include-html --part=5 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+cat <<EOF >EXPECTED
+\fpart{ ID: 5, Content-type: text/html
+<p>This is an embedded message, with a multipart/alternative part.</p>
+\fpart}
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "--format=text --part=6, rfc822's text part"
notmuch show --format=text --part=6 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
cat <<EOF >EXPECTED
Subject: Re: This subject is exactly 200 bytes in length. Other than its
length there is not much of note here. Note that the length of 200 bytes
includes the Subject: and Re: prefixes with two spaces
+To: test_suite@notmuchmail.org
In-Reply-To: <${gen_msg_id}>
References: <${gen_msg_id}>
Subject: Re: This subject is exactly 200 bytes in length. Other than its
length there is not much of note here. Note that the length of 200 bytes
includes the Subject: and Re: prefixes with two spaces
+To: test_suite@notmuchmail.org
In-Reply-To: <${gen_msg_id}>
References: <${gen_msg_id}>
(test-output))"
cat <<EOF >EXPECTED
From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To:
+To: test_suite@notmuchmail.org
Subject: Re: Reply within emacs to an html-only message
In-Reply-To: <${gen_msg_id}>
Fcc: ${MAIL_DIR}/sent
test_expect_equal_file EXPECTED OUTPUT
test_begin_subtest "Reply within emacs to message from self"
-test_subtest_known_broken
add_message '[from]="test_suite@notmuchmail.org"' \
'[to]="test_suite@notmuchmail.org"'
test_emacs "(let ((message-hidden-headers '()))
(test-output))"
cat <<EOF >EXPECTED
From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To:
+To: test_suite@notmuchmail.org
Subject: Re: Quote MML tags in reply
In-Reply-To: <test-emacs-mml-quoting@message.id>
Fcc: ${MAIL_DIR}/sent
| notmuch_drop_mail_headers In-Reply-To References)
expected='From: Notmuch Test Suite <test_suite@notmuchmail.org>
Subject: Re: test encrypted message 002
+To: test_suite@notmuchmail.org
On 01 Jan 2000 12:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
> This is another test encrypted message.'
(notmuch-show \"subject:test.encrypted.message.002\")
(notmuch-show-reply)
(test-output))"
-# the empty To: is probably a bug, but it's not to do with encryption
-grep -v -e '^In-Reply-To:' -e '^References:' -e '^Fcc:' -e 'To:' < OUTPUT > OUTPUT.clean
+grep -v -e '^In-Reply-To:' -e '^References:' -e '^Fcc:' < OUTPUT > OUTPUT.clean
cat <<EOF >EXPECTED
From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: test_suite@notmuchmail.org
Subject: Re: test encrypted message 002
--text follows this line--
<#secure method=pgpmime mode=signencrypt>
--- /dev/null
+#!/usr/bin/env bash
+#
+# Copyright (c) 2018 rhn
+#
+
+
+test_description="Lifetime constraints for library"
+
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus threading
+
+test_begin_subtest "building database"
+test_expect_success "NOTMUCH_NEW"
+
+test_begin_subtest "Message outlives parent Messages from replies"
+
+test_C ${MAIL_DIR} <<'EOF'
+#include <stdio.h>
+#include <stdlib.h>
+#include <notmuch.h>
+int main (int argc, char** argv)
+{
+ notmuch_database_t *db;
+ notmuch_status_t stat;
+ stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, &db);
+ if (stat != NOTMUCH_STATUS_SUCCESS) {
+ fprintf (stderr, "error opening database: %d\n", stat);
+ exit (1);
+ }
+
+ notmuch_query_t *query = notmuch_query_create (db, "id:B00-root@example.org");
+ notmuch_threads_t *threads;
+
+ stat = notmuch_query_search_threads (query, &threads);
+ if (stat != NOTMUCH_STATUS_SUCCESS) {
+ fprintf (stderr, "error querying threads: %d\n", stat);
+ exit (1);
+ }
+
+ if (!notmuch_threads_valid (threads)) {
+ fprintf (stderr, "invalid threads");
+ exit (1);
+ }
+
+ notmuch_thread_t *thread = notmuch_threads_get (threads);
+ notmuch_messages_t *messages = notmuch_thread_get_messages (thread);
+
+ if (!notmuch_messages_valid (messages)) {
+ fprintf (stderr, "invalid messages");
+ exit (1);
+ }
+
+ notmuch_message_t *message = notmuch_messages_get (messages);
+ notmuch_messages_t *replies = notmuch_message_get_replies (message);
+ if (!notmuch_messages_valid (replies)) {
+ fprintf (stderr, "invalid replies");
+ exit (1);
+ }
+
+ notmuch_message_t *reply = notmuch_messages_get (replies);
+
+ notmuch_messages_destroy (replies); // the reply should not get destroyed here
+ notmuch_message_destroy (message);
+ notmuch_messages_destroy (messages); // nor here
+
+ const char *mid = notmuch_message_get_message_id (reply); // should not crash when accessing
+ fprintf (stdout, "Reply id: %s\n", mid);
+ notmuch_message_destroy (reply);
+ notmuch_thread_destroy (thread); // this destroys the reply
+ notmuch_threads_destroy (threads);
+ notmuch_query_destroy (query);
+ notmuch_database_destroy (db);
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+Reply id: B01-child@example.org
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done