]> git.notmuchmail.org Git - notmuch/commitdiff
Merge tag '0.35' into debian/bullseye-backports archive/debian/0.35-1_bpo11+1 debian/0.35-1_bpo11+1
authorDavid Bremner <david@tethera.net>
Wed, 16 Feb 2022 00:53:12 +0000 (20:53 -0400)
committerDavid Bremner <david@tethera.net>
Wed, 16 Feb 2022 00:54:56 +0000 (20:54 -0400)
notmuch 0.35 release

110 files changed:
.gitignore
Makefile.global
Makefile.local
NEWS
bindings/Makefile.local
bindings/python-cffi/notmuch2/_build.py
bindings/python-cffi/notmuch2/_database.py
bindings/python-cffi/notmuch2/_message.py
bindings/python-cffi/setup.py
bindings/python-cffi/tests/test_config.py
bindings/python-cffi/tests/test_database.py
bindings/python-cffi/tests/test_message.py
bindings/python-cffi/tests/test_tags.py
bindings/python-cffi/tests/test_thread.py
bindings/python-cffi/version.txt [deleted file]
bindings/python/notmuch/version.py
completion/notmuch-completion.bash
completion/zsh/_notmuch
configure
debian/changelog
debian/control
debian/elpa-notmuch.elpa
debian/notmuch-doc.install [new file with mode: 0644]
debian/rules
devel/schemata
doc/Makefile.local
doc/conf.py
doc/man1/notmuch-address.rst
doc/man1/notmuch-config.rst
doc/man1/notmuch-count.rst
doc/man1/notmuch-dump.rst
doc/man1/notmuch-insert.rst
doc/man1/notmuch-reply.rst
doc/man1/notmuch-restore.rst
doc/man1/notmuch-search.rst
doc/man1/notmuch-show.rst
doc/man5/notmuch-hooks.rst
doc/man7/notmuch-properties.rst
doc/man7/notmuch-sexp-queries.rst
doc/notmuch-emacs.rst
emacs/Makefile.local
emacs/coolj.el
emacs/notmuch-hello.el
emacs/notmuch-logo.svg [new file with mode: 0644]
emacs/notmuch-maildir-fcc.el
emacs/notmuch-show.el
emacs/notmuch-tag.el
emacs/notmuch-tree.el
emacs/notmuch.el
lib/built-with.c
lib/config.cc
lib/database-private.h
lib/database.cc
lib/indexopts.c
lib/notmuch-private.h
lib/notmuch.h
lib/open.cc
lib/parse-sexp.cc
lib/parse-time-vrp.cc
lib/prefix.cc
lib/regexp-fields.cc
notmuch-client.h
notmuch-config.c
notmuch-insert.c
notmuch-new.c
notmuch-reindex.c
notmuch-show.c
notmuch.c
sprinter-json.c
sprinter-sexp.c
sprinter-text.c
sprinter.h
test/T030-config.sh
test/T040-setup.sh
test/T050-new.sh
test/T051-new-renames.sh [new file with mode: 0755]
test/T055-path-config.sh
test/T060-count.sh
test/T070-insert.sh
test/T081-sexpr-search.sh
test/T100-search-by-folder.sh
test/T160-json.sh
test/T170-sexp.sh
test/T310-emacs.sh
test/T350-crypto.sh
test/T370-search-folder-coherence.sh
test/T392-python-cffi-notmuch.sh [new file with mode: 0755]
test/T440-emacs-hello.sh
test/T450-emacs-show.sh
test/T562-lib-database.sh
test/T563-lib-directory.sh
test/T564-lib-query.sh
test/T566-lib-message.sh
test/T568-lib-thread.sh
test/T590-libconfig.sh
test/T595-reopen.sh
test/T610-message-property.sh
test/T620-lock.sh
test/T640-database-modified.sh
test/T750-gzip.sh
test/T800-asan.sh [new file with mode: 0755]
test/emacs.expected-output/notmuch-hello-all-tags [new file with mode: 0644]
test/notmuch-test.h
test/setup.expected-output/config-with-comments
test/symbol-test.cc
test/test-lib-emacs.sh
test/test-lib.sh
util/hex-escape.h
util/string-util.c
version.txt

index 3edd1768ed85aac3dc6bfa94a91107a3df75c1cb..f846ebecf1e5ece7c8b309c4f1ea9e5e2706d3d8 100644 (file)
@@ -16,5 +16,6 @@
 /sh.config
 /sphinx.config
 /version.stamp
+/bindings/python-cffi/_notmuch_config.py
 TAGS
 tags
index fe79121d018e0026716bf327e23536583c3e06a9..7a7a3c6d09ab687e6899cadd8055f83e6dbba07a 100644 (file)
@@ -50,9 +50,9 @@ DETACHED_SIG_FILE=$(TAR_FILE).asc
 PV_FILE=bindings/python/notmuch/version.py
 
 # Smash together user's values with our extra values
-FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CPPFLAGS) $(CFLAGS) $(WARN_CFLAGS) $(extra_cflags) $(CONFIGURE_CFLAGS)
-FINAL_CXXFLAGS = $(CPPFLAGS) $(CXXFLAGS) $(WARN_CXXFLAGS) $(extra_cflags) $(extra_cxxflags) $(CONFIGURE_CXXFLAGS)
-FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lnotmuch_util -Llib -lnotmuch
+FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(WARN_CFLAGS) $(extra_cflags) $(CPPFLAGS) $(CONFIGURE_CFLAGS) $(CFLAGS)
+FINAL_CXXFLAGS = $(WARN_CXXFLAGS) $(extra_cflags) $(extra_cxxflags) $(CPPFLAGS) $(CONFIGURE_CXXFLAGS) $(CXXFLAGS)
+FINAL_NOTMUCH_LDFLAGS = -Lutil -lnotmuch_util -Llib -lnotmuch $(LDFLAGS)
 ifeq ($(LIBDIR_IN_LDCONFIG),0)
 FINAL_NOTMUCH_LDFLAGS += $(RPATH_LDFLAGS)
 endif
index e12b94cd6f668bfc830070ec24d924ff8941bebf..10fb99085f54ff9f4f6fef73ae3e41e69dbcf6a4 100644 (file)
@@ -54,7 +54,6 @@ update-versions:
        sed -i -e "s/^__VERSION__[[:blank:]]*=.*$$/__VERSION__ = \'${VERSION}\'/" \
            -e "s/^SOVERSION[[:blank:]]*=.*$$/SOVERSION = \'${LIBNOTMUCH_VERSION_MAJOR}\'/" \
            ${PV_FILE}
-       cp version.txt bindings/python-cffi
 
 # We invoke make recursively only to force ordering of our phony
 # targets in the case of parallel invocation of make (-j).
diff --git a/NEWS b/NEWS
index 27e431561717e169ea858295a56251759a5cc5de..c6ce2eea90b250adbf7872bad13976faa09c601d 100644 (file)
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,109 @@
+Notmuch 0.35 (2022-02-06)
+=========================
+
+Library
+-------
+
+Implement the `date` and `lastmod` fields in the S-expression parser.
+
+Ignore trailing `/` for pathnames in both query parsers.
+
+Rename configuration option `built_with.sexpr_query` to
+`built_with.sexp_queries`.
+
+Do not assume a default mail root in split (e.g. XDG) configurations.
+
+Fix some small memory leaks in `notmuch_database_open_with_config`.
+
+CLI
+---
+
+Improve handling of leading/trailing punctation and space for
+configuration lists.
+
+Only ignore `.notmuch` at the top level in `notmuch new`.
+
+Optionally show extra headers in `notmuch show`. See
+`show.extra_headers` in notmuch-config(1).
+
+Emacs
+-----
+
+Drop `C-TAB` binding in hello mode, document `backtab`.
+
+Fix visual glitch in search mode by running `notmuch-search-hook`
+lazily.
+
+Don't add space to completion candidates, improves compatibility with
+third party completion frameworks.
+
+Make citation formating more robust against whitespace.
+
+Use `--excludes=false` when generating the 'All tags' section.
+
+Use cached copy of message body for `Fcc`, avoiding variant bodies for
+signed and/or encrypted messages.
+
+Add notmuch-logo.svg and use it in notmuch-hello view, replacing
+the .png version.
+
+Make header line in show buffers optional.
+
+Add customizable names for search buffers.
+
+Build
+-----
+
+Fix out-of-tree build for `python-cffi` bindings.
+
+Rearrange position of {C,CXX,CPP,LD}FLAGS, prevent some clashes with
+installed version of notmuch.
+
+Ignore more configure options.
+
+Test Suite
+----------
+
+Replace some uses of `gdb` in the test suite with `LD_PRELOAD` based
+shims.
+
+Use `--with-colons` for gpgsm, fix compatibility with newer gnupg.
+
+Python bindings
+---------------
+
+Add `matched` property to message objects.
+
+Users are reminded that the old python bindings in bindings/python are
+deprecated; this will probably be the last major release that ships
+them.
+
+Completion
+----------
+
+Use `database.mail_root` for path completion in bash/zsh.
+
+Notmuch 0.34.3 (2022-01-09)
+===========================
+
+Library
+-------
+
+Do not crash when presented with a .notmuch directory without a
+xapian/ subdirectory.
+
+Python Bindings (notmuch2)
+--------------------------
+
+Database constructor now searches for configuration by default. Pass
+`config=Database.CONFIG.EMPTY` to disable.
+
+The `Message.replies()` method now returns OwnedMessage objects, to
+prevent certain memory de-allocation errors.
+
+Fix for importing `notmuch2` module when building bindings
+documentation.
+
 Notmuch 0.34.2 (2021-12-09)
 ===========================
 
index 3672e69ff76af07da05afc0a527355d2c0271941..7b10af088abbb98716ae45c4cba35cf7a50ffeb2 100644 (file)
@@ -3,21 +3,26 @@
 dir := bindings
 
 # force the shared library to be built
-ruby-bindings: lib/$(LINKER_NAME)
+ruby-bindings: $(dir)/ruby.stamp
+
+$(dir)/ruby.stamp: lib/$(LINKER_NAME)
 ifeq ($(HAVE_RUBY_DEV),1)
        cd $(dir)/ruby && \
                EXTRA_LDFLAGS="$(NO_UNDEFINED_LDFLAGS)" \
                LIBNOTMUCH="../../lib/$(LINKER_NAME)" \
                NOTMUCH_SRCDIR='$(NOTMUCH_SRCDIR)' \
                $(RUBY) extconf.rb --vendor
-       $(MAKE) -C $(dir)/ruby CFLAGS="$(CFLAGS) -pipe -fno-plt -fPIC"
+       $(MAKE) -C $(dir)/ruby CFLAGS="$(CFLAGS) -pipe -fno-plt -fPIC" && touch $@
 endif
 
-python-cffi-bindings: lib/$(LINKER_NAME)
+python-cffi-bindings: $(dir)/python-cffi.stamp
+
+$(dir)/python-cffi.stamp: lib/$(LINKER_NAME)
 ifeq ($(HAVE_PYTHON3_CFFI),1)
        cd $(dir)/python-cffi && \
                ${PYTHON} setup.py build --build-lib build/stage && \
-               mkdir -p build/stage/tests && cp tests/*.py build/stage/tests
+               mkdir -p build/stage/tests && cp tests/*.py build/stage/tests && \
+               touch ../python-cffi.stamp
 endif
 
 CLEAN += $(patsubst %,$(dir)/ruby/%, \
@@ -26,6 +31,6 @@ CLEAN += $(patsubst %,$(dir)/ruby/%, \
        init.o message.o messages.o mkmf.log notmuch.so query.o \
        status.o tags.o thread.o threads.o)
 
-CLEAN += bindings/ruby/.vendorarchdir.time
+CLEAN += bindings/ruby/.vendorarchdir.time $(dir)/ruby.stamp
 
-CLEAN += bindings/python-cffi/build
+CLEAN += bindings/python-cffi/build $(dir)/python-cffi.stamp
index f6184b97be773ff8f885b66dc312c1dfeda889f6..a55b484fa86030d60e467ffe69f464b192d3e0e2 100644 (file)
@@ -1,5 +1,5 @@
 import cffi
-
+from _notmuch_config import *
 
 ffibuilder = cffi.FFI()
 ffibuilder.set_source(
@@ -16,8 +16,8 @@ ffibuilder.set_source(
         #ERROR libnotmuch  version < 5.1 not supported
     #endif
     """,
-    include_dirs=['../../lib'],
-    library_dirs=['../../lib'],
+    include_dirs=[NOTMUCH_INCLUDE_DIR],
+    library_dirs=[NOTMUCH_LIB_DIR],
     libraries=['notmuch'],
 )
 ffibuilder.cdef(
@@ -54,6 +54,7 @@ ffibuilder.cdef(
         NOTMUCH_STATUS_NO_DATABASE,
         NOTMUCH_STATUS_DATABASE_EXISTS,
         NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
+        NOTMUCH_STATUS_NO_MAIL_ROOT,
         NOTMUCH_STATUS_LAST_STATUS
     } notmuch_status_t;
     typedef enum {
index 14a8f15c6b654e0f02da62fff93959cb906f9546..d7485b4d983d8f55efd92b8a8581dac86e415c3a 100644 (file)
@@ -139,7 +139,7 @@ class Database(base.NotmuchObject):
             path = os.fsencode(path)
         return path
 
-    def __init__(self, path=None, mode=MODE.READ_ONLY, config=CONFIG.EMPTY):
+    def __init__(self, path=None, mode=MODE.READ_ONLY, config=CONFIG.SEARCH):
         if isinstance(mode, str):
             mode = self.STR_MODE_MAP[mode]
         self.mode = mode
index 2f2320766ce9805ca88e0ea81c34c36050263a2c..aa1cb8757bfb3b0c1555a4bb8f7f9ae57b6c4b32 100644 (file)
@@ -205,6 +205,20 @@ class Message(base.NotmuchObject):
             self._msg_p, capi.lib.NOTMUCH_MESSAGE_FLAG_EXCLUDED)
         return bool(ret)
 
+    @property
+    def matched(self):
+        """Indicates whether this message was matched by the query.
+
+        When a thread is created from a search, some of the
+        messages may not match the original query.  This property
+        is set to *True* for those that do match.
+
+        :raises ObjectDestroyedError: if used after destroyed.
+        """
+        ret = capi.lib.notmuch_message_get_flag(
+            self._msg_p, capi.lib.NOTMUCH_MESSAGE_FLAG_MATCH)
+        return bool(ret)
+
     @property
     def date(self):
         """The message date as an integer.
@@ -357,14 +371,14 @@ class Message(base.NotmuchObject):
         This method will only work if the message was created from a
         thread.  Otherwise it will yield no results.
 
-        :returns: An iterator yielding :class:`Message` instances.
+        :returns: An iterator yielding :class:`OwnedMessage` instances.
         :rtype: MessageIter
         """
         # The notmuch_messages_valid call accepts NULL and this will
         # become an empty iterator, raising StopIteration immediately.
         # Hence no return value checking here.
         msgs_p = capi.lib.notmuch_message_get_replies(self._msg_p)
-        return MessageIter(self, msgs_p, db=self._db)
+        return MessageIter(self, msgs_p, db=self._db, msg_cls=OwnedMessage)
 
     def __hash__(self):
         return hash(self.messageid)
index cda5233840bc0e0183181b8464c750ed3dd75866..55fb2d24f832b49e2730db879309b441a58a0a78 100644 (file)
@@ -1,6 +1,7 @@
 import setuptools
+from _notmuch_config import *
 
-with open('version.txt') as fp:
+with open(NOTMUCH_VERSION_FILE) as fp:
     VERSION = fp.read().strip()
 
 setuptools.setup(
index 1b2695f51707a9d447c0b3160a1f269d025f0f17..67b0dea44ea99837f91a738b5236661dc7c9f7e3 100644 (file)
@@ -23,9 +23,9 @@ class TestIter:
 
     def test_set_get(self, maildir):
         # Ensure get-set works from different db objects
-        with dbmod.Database.create(maildir.path) as db0:
+        with dbmod.Database.create(maildir.path, config=dbmod.Database.CONFIG.EMPTY) as db0:
             db0.config['spam'] = 'ham'
-        with dbmod.Database(maildir.path) as db1:
+        with dbmod.Database(maildir.path, config=dbmod.Database.CONFIG.EMPTY) as db1:
             assert db1.config['spam'] == 'ham'
 
     def test_get_keyerror(self, db):
index 9b3219c00e634a9e4d91fa6d3c7c329dd8e59aa2..f1d12ea6c2f046186377a7ce48b104d3a5855873 100644 (file)
@@ -13,7 +13,7 @@ import notmuch2._message as message
 
 @pytest.fixture
 def db(maildir):
-    with dbmod.Database.create(maildir.path) as db:
+    with dbmod.Database.create(maildir.path, config=notmuch2.Database.CONFIG.EMPTY) as db:
         yield db
 
 
@@ -293,7 +293,7 @@ class TestQuery:
         maildir.deliver(body='baz',
                         headers=[('In-Reply-To', '<{}>'.format(msgid))])
         notmuch('new')
-        with dbmod.Database(maildir.path, 'rw') as db:
+        with dbmod.Database(maildir.path, 'rw', config=notmuch2.Database.CONFIG.EMPTY) as db:
             yield db
 
     def test_count_messages(self, db):
index 532bf92159dd86cb23c5cd50133fa5444c4418dd..56701d056de5ff6d1cda5e7fc0c53b1cdd8a6de5 100644 (file)
@@ -97,6 +97,9 @@ class TestMessage:
     def test_ghost_no(self, msg):
         assert not msg.ghost
 
+    def test_matched_no(self,msg):
+        assert not msg.matched
+
     def test_date(self, msg):
         # XXX Someone seems to treat things as local time instead of
         #     UTC or the other way around.
index faf3947b6090f901a63f5828c962fc64e395591f..f2c6209df437f8fcb0d0d906d781b048de9cc8e9 100644 (file)
@@ -23,7 +23,7 @@ class TestImmutable:
         """
         maildir.deliver()
         notmuch('new')
-        with database.Database(maildir.path) as db:
+        with database.Database(maildir.path, config=database.Database.CONFIG.EMPTY) as db:
             yield db.tags
 
     def test_type(self, tagset):
@@ -33,7 +33,7 @@ class TestImmutable:
     def test_hash(self, tagset, maildir, notmuch):
         h0 = hash(tagset)
         notmuch('tag', '+foo', '*')
-        with database.Database(maildir.path) as db:
+        with database.Database(maildir.path, config=database.Database.CONFIG.EMPTY) as db:
             h1 = hash(db.tags)
         assert h0 != h1
 
@@ -42,7 +42,7 @@ class TestImmutable:
 
     def test_neq(self, tagset, maildir, notmuch):
         notmuch('tag', '+foo', '*')
-        with database.Database(maildir.path) as db:
+        with database.Database(maildir.path, config=database.Database.CONFIG.EMPTY) as db:
             assert tagset != db.tags
 
     def test_contains(self, tagset):
@@ -159,7 +159,8 @@ class TestMutableTagset:
         _, pathname = maildir.deliver()
         notmuch('new')
         with database.Database(maildir.path,
-                               mode=database.Mode.READ_WRITE) as db:
+                               mode=database.Mode.READ_WRITE,
+                               config=database.Database.CONFIG.EMPTY) as db:
             msg = db.get(pathname)
             yield msg.tags
 
@@ -195,7 +196,8 @@ class TestMutableTagset:
         _, pathname = maildir.deliver(flagged=True)
         notmuch('new')
         with database.Database(maildir.path,
-                               mode=database.Mode.READ_WRITE) as db:
+                               mode=database.Mode.READ_WRITE,
+                               config=database.Database.CONFIG.EMPTY) as db:
             msg = db.get(pathname)
             msg.tags.discard('flagged')
             msg.tags.from_maildir_flags()
@@ -205,7 +207,8 @@ class TestMutableTagset:
         _, pathname = maildir.deliver(flagged=True)
         notmuch('new')
         with database.Database(maildir.path,
-                               mode=database.Mode.READ_WRITE) as db:
+                               mode=database.Mode.READ_WRITE,
+                               config=database.Database.CONFIG.EMPTY) as db:
             msg = db.get(pathname)
             flags = msg.path.name.split(',')[-1]
             assert 'F' in flags
index 1f44b35d2c9e1a798ddeefe597643fb52c4eb30a..619d2aacfaa255ebfa0a6e775d2bab2575c5c3a3 100644 (file)
@@ -13,7 +13,7 @@ def thread(maildir, notmuch):
     maildir.deliver(body='bar',
                     headers=[('In-Reply-To', '<{}>'.format(msgid))])
     notmuch('new')
-    with notmuch2.Database(maildir.path) as db:
+    with notmuch2.Database(maildir.path, config=notmuch2.Database.CONFIG.EMPTY) as db:
         yield next(db.threads('foo'))
 
 
@@ -57,6 +57,13 @@ def test_iter(thread):
 def test_matched(thread):
     assert thread.matched == 1
 
+def test_matched_iter(thread):
+    count = 0
+    msgs = list(iter(thread))
+    for msg in msgs:
+        if msg.matched:
+            count += 1
+    assert count == thread.matched
 
 def test_authors_type(thread):
     assert isinstance(thread.authors, notmuch2.BinString)
diff --git a/bindings/python-cffi/version.txt b/bindings/python-cffi/version.txt
deleted file mode 100644 (file)
index 3f8003c..0000000
+++ /dev/null
@@ -1 +0,0 @@
-0.34.2
index 7a872f5f2077a1c237bd8a351eb8b9510217f8fe..f7392174c83069d1137d65e322436e942dab80c6 100644 (file)
@@ -1,3 +1,3 @@
 # this file should be kept in sync with ../../../version
-__VERSION__ = '0.34.2'
+__VERSION__ = '0.35'
 SOVERSION = '5'
index 15425697a215290e79a4d58e7cdf4884f3a5f2bc..0022b54bff5df41765aecdd640484a6dd53b5e5f 100644 (file)
@@ -103,12 +103,12 @@ _notmuch_search_terms()
            COMPREPLY=( $(compgen -P "from:" -W "`_notmuch_email ${cur}`" -- ${cur##from:}) )
            ;;
        path:*)
-           local path=`notmuch config get database.path`
+           local path=`notmuch config get database.mail_root`
            compopt -o nospace
            COMPREPLY=( $(compgen -d "$path/${cur##path:}" | sed "s|^$path/||" ) )
            ;;
        folder:*)
-           local path=`notmuch config get database.path`
+           local path=`notmuch config get database.mail_root`
            compopt -o nospace
            COMPREPLY=( $(compgen -d "$path/${cur##folder:}" | \
                sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
@@ -281,7 +281,7 @@ _notmuch_insert()
     $split &&
     case "${prev}" in
        --folder)
-           local path=`notmuch config get database.path`
+           local path=`notmuch config get database.mail_root`
            compopt -o nospace
            COMPREPLY=( $(compgen -d "$path/${cur}" | \
                sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
index e920f10b2363dedba6e712bec26f75ebbcfce600..e207d90b72020f0a94fa46c72702a34a6a1ddc35 100644 (file)
@@ -69,8 +69,8 @@ _notmuch_term_mimetype() {
 
 _notmuch_term_path() {
   local ret=1 expl
-  local maildir="$(notmuch config get database.path)"
-  [[ -d $maildir ]] || { _message -e "database.path not found" ; return $ret }
+  local maildir="$(notmuch config get database.mail_root)"
+  [[ -d $maildir ]] || { _message -e "database.mail_root not found" ; return $ret }
 
   _description notmuch-folder expl 'maildir folder'
   _files "$expl[@]" -W $maildir -/ && ret=0
@@ -79,8 +79,8 @@ _notmuch_term_path() {
 
 _notmuch_term_folder() {
   local ret=1 expl
-  local maildir="$(notmuch config get database.path)"
-  [[ -d $maildir ]] || { _message -e "database.path not found" ; return $ret }
+  local maildir="$(notmuch config get database.mail_root)"
+  [[ -d $maildir ]] || { _message -e "database.mail_root not found" ; return $ret }
 
   _description notmuch-folder expl 'maildir folder'
   local ignoredfolders=( '*/(cur|new|tmp)' )
index 6c3a38f1098ace4d563f5962d9470013e898175a..36f3f606814417a60236e1eadb78c4c0f86c0857 100755 (executable)
--- a/configure
+++ b/configure
@@ -55,6 +55,8 @@ subdirs="${subdirs} bindings"
 # the directory structure and copy Makefiles.
 if [ "$srcdir" != "." ]; then
 
+    NOTMUCH_BUILDDIR=$PWD
+
     for dir in . ${subdirs}; do
        mkdir -p "$dir"
        cp "$srcdir"/"$dir"/Makefile.local "$dir"
@@ -78,6 +80,8 @@ if [ "$srcdir" != "." ]; then
        "$srcdir"/bindings/python-cffi/notmuch2 \
        "$srcdir"/bindings/python-cffi/setup.py \
        bindings/python-cffi/
+else
+    NOTMUCH_BUILDDIR=$NOTMUCH_SRCDIR
 fi
 
 # Set several defaults (optionally specified by the user in
@@ -308,12 +312,22 @@ for option; do
        true
     elif [ "${option%%=*}" = '--host' ] ; then
        true
+    elif [ "${option%%=*}" = '--bindir' ] ; then
+       true
+    elif [ "${option%%=*}" = '--sbindir' ] ; then
+       true
     elif [ "${option%%=*}" = '--datadir' ] ; then
        true
     elif [ "${option%%=*}" = '--localstatedir' ] ; then
        true
+    elif [ "${option%%=*}" = '--sharedstatedir' ] ; then
+       true
     elif [ "${option%%=*}" = '--libexecdir' ] ; then
        true
+    elif [ "${option%%=*}" = '--exec-prefix' ] ; then
+       true
+    elif [ "${option%%=*}" = '--program-prefix' ] ; then
+       true
     elif [ "${option}" = '--disable-maintainer-mode' ] ; then
        true
     elif [ "${option}" = '--disable-dependency-tracking' ] ; then
@@ -396,6 +410,18 @@ EOF
     exit 1
 fi
 
+printf "C compiler supports address sanitizer... "
+test_cmdline="${CC} ${CFLAGS} ${CPPFLAGS} -fsanitize=address minimal.c ${LDFLAGS} -o minimal"
+if ${test_cmdline} >/dev/null 2>&1 && ./minimal
+then
+    printf "Yes.\n"
+    have_asan=1
+else
+    printf "Nope, skipping those tests.\n"
+    have_asan=0
+fi
+unset test_cmdline
+
 printf "Reading libnotmuch version from source... "
 cat > _libversion.c <<EOF
 #include <stdio.h>
@@ -734,6 +760,7 @@ if command -v ${BASHCMD} > /dev/null; then
     printf "Yes (%s).\n" "$bash_absolute"
 else
     have_bash=0
+    bash_absolute=
     printf "No. (%s not found)\n" "${BASHCMD}"
 fi
 
@@ -744,6 +771,7 @@ if command -v ${PERL} > /dev/null; then
     printf "Yes (%s).\n" "$perl_absolute"
 else
     have_perl=0
+    perl_absolute=
     printf "No. (%s not found)\n" "${PERL}"
 fi
 
@@ -1245,6 +1273,7 @@ cat > Makefile.config <<EOF
 # directory (the current directory at the time configure was run).
 srcdir = ${srcdir}
 NOTMUCH_SRCDIR = ${NOTMUCH_SRCDIR}
+NOTMUCH_BUILDDIR = ${NOTMUCH_BUILDDIR}
 
 # subdirectories to build
 subdirs = ${subdirs}
@@ -1531,6 +1560,9 @@ NOTMUCH_GMIME_X509_CERT_VALIDITY=${gmime_x509_cert_validity}
 # Whether GMime can verify signatures when decrypting with a session key:
 NOTMUCH_GMIME_VERIFY_WITH_SESSION_KEY=${gmime_verify_with_session_key}
 
+# Does the C compiler support the address sanitizer
+NOTMUCH_HAVE_ASAN=${have_asan}
+
 # do we have man pages?
 NOTMUCH_HAVE_MAN=$((have_sphinx))
 
@@ -1579,6 +1611,14 @@ EOF
     printf "rsti_dir = '%s'\n" "$(cd emacs && pwd -P)"
 } > sphinx.config
 
+cat > bindings/python-cffi/_notmuch_config.py <<EOF
+# _notmuch_config.py was automatically generated by the configure
+# script in the root of the notmuch source tree.
+NOTMUCH_VERSION_FILE='${NOTMUCH_SRCDIR}/version.txt'
+NOTMUCH_INCLUDE_DIR='${NOTMUCH_SRCDIR}/lib'
+NOTMUCH_LIB_DIR='${NOTMUCH_SRCDIR}/lib'
+EOF
+
 # Finally, after everything configured, inform the user how to continue.
 cat <<EOF
 
index 2875593c7833bd5de74b48b1ade2d43fe66cb0a1..d149c3a279bc25e808a2f0abedaf1a41b9ae69cd 100644 (file)
@@ -1,3 +1,34 @@
+notmuch (0.35-1~bpo11+1) bullseye-backports; urgency=medium
+
+  * Rebuild for bullseye-backports.
+
+ -- David Bremner <bremner@debian.org>  Tue, 15 Feb 2022 20:54:43 -0400
+
+notmuch (0.35-1) unstable; urgency=medium
+
+  * New upstream release
+
+ -- David Bremner <bremner@debian.org>  Sun, 06 Feb 2022 12:15:19 -0400
+
+notmuch (0.35~rc0-2) experimental; urgency=medium
+
+  * Reupload with binaries
+
+ -- David Bremner <bremner@debian.org>  Sat, 29 Jan 2022 21:53:29 -0400
+
+notmuch (0.35~rc0-1) experimental; urgency=medium
+
+  * New upstream release candidate
+
+ -- David Bremner <bremner@debian.org>  Sat, 29 Jan 2022 18:14:57 -0400
+
+notmuch (0.34.3-1) unstable; urgency=medium
+
+  * New upstream bugfix release, with several fixes for the notmuch2
+    python module.
+
+ -- David Bremner <bremner@debian.org>  Sun, 09 Jan 2022 15:30:38 -0400
+
 notmuch (0.34.2-1~bpo11+1) bullseye-backports; urgency=medium
 
   * Rebuild for bullseye-backports.
index 9872d60240ed3d479b4703a7f4323c89d3f256b3..a11d41306a17f11e9552370e974ab76e1a7b737b 100644 (file)
@@ -56,7 +56,8 @@ Recommends:
  gnupg-agent,
  gpgsm,
 Suggests:
- mailscripts
+ mailscripts,
+ notmuch-doc,
 Description: thread-based email index, search and tagging
  Notmuch is a system for indexing, searching, reading, and tagging
  large collections of email messages in maildir or mh format. It uses
@@ -65,6 +66,21 @@ Description: thread-based email index, search and tagging
  .
  This package contains the notmuch command-line interface
 
+Package: notmuch-doc
+Architecture: all
+Depends:
+ ${misc:Depends},
+ ${sphinxdoc:Depends},
+Suggests:
+ notmuch
+Description: thread-based email index, search and tagging
+ Notmuch is a system for indexing, searching, reading, and tagging
+ large collections of email messages in maildir or mh format. It uses
+ the Xapian library to provide fast, full-text search with a very
+ convenient search syntax.
+ .
+ This package contains the HTML documentation
+
 Package: libnotmuch5
 Section: libs
 Architecture: any
index 4712b73ff76e8ad31fc54c51999ef862d863dd9e..7b3ce0fa483fab5e2d87d78135ad14749f10011f 100644 (file)
@@ -1,3 +1,3 @@
 debian/tmp/usr/share/emacs/site-lisp/*.el
-debian/tmp/usr/share/emacs/site-lisp/notmuch-logo.png
+debian/tmp/usr/share/emacs/site-lisp/notmuch-logo.svg
 emacs/notmuch-pkg.el
diff --git a/debian/notmuch-doc.install b/debian/notmuch-doc.install
new file mode 100644 (file)
index 0000000..fa902fe
--- /dev/null
@@ -0,0 +1 @@
+doc/_build/html usr/share/doc/notmuch
index fa0551a951906e686860d08a78c615f24af6e3b0..558671261bf0f93ae1e441ea2bb31e3267695a72 100755 (executable)
@@ -3,7 +3,7 @@
 export DEB_BUILD_MAINT_OPTIONS = hardening=+all
 
 %:
-       dh $@ --with python3,elpa
+       dh $@ --with python3,elpa,sphinxdoc
 
 override_dh_auto_configure:
        BASHCMD=/bin/bash ./configure --prefix=/usr \
@@ -19,7 +19,7 @@ override_dh_auto_test:
        dh_auto_test -- V=1
 
 override_dh_auto_build:
-       dh_auto_build -- V=1
+       dh_auto_build -- V=1 all sphinx-html
        PYBUILD_NAME=notmuch dh_auto_build --buildsystem=pybuild --sourcedirectory bindings/python
        PYBUILD_NAME=notmuch2 dh_auto_build --buildsystem=pybuild --sourcedirectory bindings/python-cffi
        $(MAKE) -C contrib/notmuch-mutt
index 01e3a3df03305244b1c206040c5d9c85ef3b8ac1..01810888e5a62d0ee28569cd5406ba19cefb7050 100644 (file)
@@ -145,9 +145,11 @@ headers = {
     Cc?:            string,
     Bcc?:           string,
     Reply-To?:      string,
-    Date:           string
+    Date:           string,
+    extra_header_pair*
 }
 
+extra_header_pair=  (header_name: string)
 # Encryption status (format_part_sprinter)
 encstatus = [{status: "good"|"bad"}]
 
index 730ad4fb5a7b433cc1bd85c1c59a8a4ba44ed32e..d43ef26923bcd9f192047ff2f5d5b1c101a62d0c 100644 (file)
@@ -35,7 +35,7 @@ endif
 
 INFO_INFO_FILES := $(INFO_TEXI_FILES:.texi=.info)
 
-.PHONY: sphinx-html sphinx-texinfo sphinx-info doc-prereqs
+.PHONY: sphinx-html sphinx-texinfo sphinx-info
 
 .PHONY: install-man build-man apidocs install-apidocs
 
@@ -47,23 +47,28 @@ $(DOCBUILDDIR)/.roff.stamp $(DOCBUILDDIR)/.html.stamp $(DOCBUILDDIR)/.texi.stamp
 endif
 
 ifeq ($(HAVE_PYTHON3_CFFI),1)
-doc-prereqs: python-cffi-bindings
+DOC_PREREQS=bindings/python-cffi.stamp
+else
+DOC_PREREQS=
 endif
 
 sphinx-html: $(DOCBUILDDIR)/.html.stamp
 
-$(DOCBUILDDIR)/.html.stamp: $(ALL_RST_FILES) doc-prereqs
+$(DOCBUILDDIR)/.html.stamp: $(ALL_RST_FILES) $(DOC_PREREQS)
        $(SPHINXBUILD) -b html -d $(DOCBUILDDIR)/html_doctrees $(ALLSPHINXOPTS) $(DOCBUILDDIR)/html
        touch $@
 
 sphinx-texinfo: $(DOCBUILDDIR)/.texi.stamp
 
-$(DOCBUILDDIR)/.texi.stamp: $(ALL_RST_FILES) doc-prereqs
+$(DOCBUILDDIR)/.texi.stamp: $(ALL_RST_FILES) $(DOC_PREREQS)
        $(SPHINXBUILD) -b texinfo -d $(DOCBUILDDIR)/texinfo_doctrees $(ALLSPHINXOPTS) $(DOCBUILDDIR)/texinfo
        touch $@
 
-sphinx-info: sphinx-texinfo
+sphinx-info: $(DOCBUILDDIR)/.info.stamp
+
+$(DOCBUILDDIR)/.info.stamp: $(DOCBUILDDIR)/.texi.stamp $(DOC_PREREQS)
        $(MAKE) -C $(DOCBUILDDIR)/texinfo info
+       touch $@
 
 # Use the man page converter that is available. We should never depend
 # on MAN_ROFF_FILES if a converter is not available.
@@ -112,6 +117,11 @@ build-man:
 install-man:
        @echo "No sphinx, will not install man pages."
 else
+
+# it should be safe to depend on the stamp file, because it is created
+# after all roff files are moved into place.
+${MAN_GZIP_FILES}: ${DOCBUILDDIR}/.roff.stamp
+
 build-man: ${MAN_GZIP_FILES}
 install-man: ${MAN_GZIP_FILES}
        mkdir -m0755 -p "$(DESTDIR)$(mandir)/man1"
@@ -127,7 +137,7 @@ ifneq ($(HAVE_SPHINX)$(HAVE_MAKEINFO),11)
 build-info:
        @echo "Missing sphinx or makeinfo, not building info pages"
 else
-build-info: sphinx-info
+build-info: $(DOCBUILDDIR)/.info.stamp
 endif
 
 ifneq ($(HAVE_SPHINX)$(HAVE_MAKEINFO)$(HAVE_INSTALL_INFO),111)
@@ -145,5 +155,5 @@ $(dir)/config.dox: version.stamp
        echo "INPUT=${srcdir}/lib/notmuch.h" >> $@
 
 CLEAN := $(CLEAN) $(DOCBUILDDIR) $(DOCBUILDDIR)/.roff.stamp $(DOCBUILDDIR)/.texi.stamp
-CLEAN := $(CLEAN) $(DOCBUILDDIR)/.html.stamp
+CLEAN := $(CLEAN) $(DOCBUILDDIR)/.html.stamp $(DOCBUILDDIR)/.info.stamp
 CLEAN := $(CLEAN) $(MAN_GZIP_FILES) $(MAN_ROFF_FILES) $(dir)/conf.pyc $(dir)/config.dox
index c7fd8f5acec84807e2738dfae00a0d3c709e8393..e46e1d4e47faa089c45553f747a6f95003dba9a4 100644 (file)
@@ -14,7 +14,7 @@ master_doc = 'index'
 
 # General information about the project.
 project = u'notmuch'
-copyright = u'2009-2021, Carl Worth and many others'
+copyright = u'2009-2022, Carl Worth and many others'
 
 location = os.path.dirname(__file__)
 
index 7423b62955087462aeefad99efdfaafe5b5e149c..c70dde35d87f4836f7df889ef99bfd7ba211e02f 100644 (file)
@@ -42,7 +42,7 @@ Supported options for **address** include
    neither ``--output=sender`` nor ``--output=recipients`` is
    given, ``--output=sender`` is implied.
 
-   **sender**
+   sender
      Output all addresses from the *From* header.
 
      Note: Searching for **sender** should be much faster than
@@ -50,17 +50,17 @@ Supported options for **address** include
      cached directly in the database whereas other addresses need
      to be fetched from message files.
 
-   **recipients**
+   recipients
      Output all addresses from the *To*, *Cc* and *Bcc* headers.
 
-   **count**
+   count
      Print the count of how many times was the address encountered
      during search.
 
      Note: With this option, addresses are printed only after the
      whole search is finished. This may take long time.
 
-   **address**
+   address
      Output only the email addresses instead of the full mailboxes
      with names and email addresses. This option has no effect on
      the JSON or S-Expression output formats.
@@ -69,17 +69,17 @@ Supported options for **address** include
 
    Control the deduplication of results.
 
-   **no**
+   no
      Output all occurrences of addresses in the matching
      messages. This is not applicable with ``--output=count``.
 
-   **mailbox**
+   mailbox
      Deduplicate addresses based on the full, case sensitive name
      and email address, or mailbox. This is effectively the same as
      piping the ``--deduplicate=no`` output to **sort | uniq**, except
      for the order of results. This is the default.
 
-   **address**
+   address
      Deduplicate addresses based on the case insensitive address
      part of the mailbox. Of all the variants (with different name
      or case), print the one occurring most frequently among the
index 36e57ea61ccf64f54c16fe76db788743520bd237..41e1338b12e8a34bc4ce87c7462f1f3f35ab5852 100644 (file)
@@ -55,14 +55,14 @@ The available configuration items are described below. Non-absolute
 paths are presumed relative to `$HOME` for items in section
 **database**.
 
-**database.path**
+database.path
     Notmuch will store its database here, (in
     sub-directory named ``.notmuch`` if **database.mail\_root**
     is unset).
 
     Default: see :ref:`database`
 
-**database.mail_root**
+database.mail_root
     The top-level directory where your mail currently exists and to
     where mail will be delivered in the future. Files should be
     individual email messages.
@@ -72,7 +72,7 @@ paths are presumed relative to `$HOME` for items in section
     Default: For compatibility with older configurations, the value of
     database.path is used if **database.mail\_root** is unset.
 
-**database.backup_dir**
+database.backup_dir
     Directory to store tag dumps when upgrading database.
 
     History: this configuration value was introduced in notmuch 0.32.
@@ -80,7 +80,7 @@ paths are presumed relative to `$HOME` for items in section
     Default: A sibling directory of the Xapian database called
     `backups`.
 
-**database.hook_dir**
+database.hook_dir
     Directory containing hooks run by notmuch commands. See
     :any:`notmuch-hooks(5)`.
 
@@ -88,7 +88,7 @@ paths are presumed relative to `$HOME` for items in section
 
     Default: See HOOKS, below.
 
-**database.autocommit**
+database.autocommit
 
     How often to commit transactions to disk. `0` means wait until
     command completes, otherwise an integer `n` specifies to commit to
@@ -96,30 +96,30 @@ paths are presumed relative to `$HOME` for items in section
 
     History: this configuration value was introduced in notmuch 0.33.
 
-**user.name**
+user.name
     Your full name.
 
     Default: ``$NAME`` variable if set, otherwise read from
     ``/etc/passwd``.
 
-**user.primary\_email**
+user.primary\_email
     Your primary email address.
 
     Default: ``$EMAIL`` variable if set, otherwise constructed from
     the username and hostname of the current machine.
 
-**user.other\_email**
+user.other\_email
     A list of other email addresses at which you receive email.
 
     Default: not set.
 
-**new.tags**
+new.tags
     A list of tags that will be added to all messages incorporated by
     **notmuch new**.
 
     Default: ``unread;inbox``.
 
-**new.ignore**
+new.ignore
     A list to specify files and directories that will not be searched
     for messages by :any:`notmuch-new(1)`. Each entry in the list is either:
 
@@ -137,7 +137,7 @@ paths are presumed relative to `$HOME` for items in section
 
     Default: empty list.
 
-**search.exclude\_tags**
+search.exclude\_tags
     A list of tags that will be excluded from search results by
     default. Using an excluded tag in a query will override that
     exclusion.
@@ -145,7 +145,21 @@ paths are presumed relative to `$HOME` for items in section
     Default: empty list. Note that :any:`notmuch-setup(1)` puts
     ``deleted;spam`` here when creating new configuration file.
 
-**maildir.synchronize\_flags**
+.. _show.extra_headers:
+
+show.extra\_headers
+
+    By default :any:`notmuch-show(1)` includes the following headers
+    in structured output if they are present in the message:
+    `Subject`, `From`, `To`, `Cc`, `Bcc`, `Reply-To`, `Date`. This
+    option allows the specification of a list of further
+    headers to output.
+
+    History: This configuration value was introduced in notmuch 0.35.
+
+    Default: empty list.
+
+maildir.synchronize\_flags
     If true, then the following maildir flags (in message filenames)
     will be synchronized with the corresponding notmuch tags:
 
@@ -178,7 +192,7 @@ paths are presumed relative to `$HOME` for items in section
 
     Default: ``true``.
 
-**index.decrypt**
+index.decrypt
     Policy for decrypting encrypted messages during indexing.  Must be
     one of: ``false``, ``auto``, ``nostash``, or ``true``.
 
@@ -231,7 +245,7 @@ paths are presumed relative to `$HOME` for items in section
 
     Default: ``auto``.
 
-**index.header.<prefix>**
+index.header.<prefix>
     Define the query prefix <prefix>, based on a mail header. For
     example ``index.header.List=List-Id`` will add a probabilistic
     prefix ``List:`` that searches the ``List-Id`` field.  User
@@ -240,18 +254,18 @@ paths are presumed relative to `$HOME` for items in section
     supported. See :any:`notmuch-search-terms(7)` for a list of existing
     prefixes, and an explanation of probabilistic prefixes.
 
-**built_with.<name>**
+built_with.<name>
     Compile time feature <name>. Current possibilities include
     "retry_lock" (configure option, included by default).
     (since notmuch 0.30, "compact" and "field_processor" are
     always included.)
 
-**query.<name>**
+query.<name>
     Expansion for named query called <name>. See
     :any:`notmuch-search-terms(7)` for more information about named
     queries.
 
-**squery.<name>**
+squery.<name>
     Expansion for named query called <name>, using s-expression syntax. See
     :any:`notmuch-sexp-queries(7)` for more information about s-expression
     queries.
index 9a7e4bacf4591fa0b2faf8b6220271c490c1214b..4c9c9a1cb4d78ef47c1c03bff273d46481e8bf45 100644 (file)
@@ -28,13 +28,13 @@ Supported options for **count** include
 
 .. option:: --output=(messages|threads|files)
 
-   **messages**
+   messages
      Output the number of matching messages. This is the default.
 
-   **threads**
+   threads
      Output the number of matching threads.
 
-   **files**
+   files
      Output the number of files associated with matching
      messages. This may be bigger than the number of matching
      messages due to duplicates (i.e. multiple files having the
index 4319c5c312682e767069112b5d007dc0330e1276..a7ca39d008215aff883a1ba6d58ead49646b642f 100644 (file)
@@ -39,7 +39,7 @@ Supported options for **dump** include
    Notmuch restore supports two plain text dump formats, both with
    one message-id per line, followed by a list of tags.
 
-   **batch-tag**
+   batch-tag
      The default **batch-tag** dump format is intended to more
      robust against malformed message-ids and tags containing
      whitespace or non-\ :manpage:`ascii(7)` characters. Each line
@@ -58,7 +58,7 @@ Supported options for **dump** include
      :any:`notmuch-tag(1)`; note that the single message-id query is
      mandatory for :any:`notmuch-restore(1)`.
 
-   **sup**
+   sup
      The **sup** dump file format is specifically chosen to be
      compatible with the format of files produced by
      :manpage:`sup-dump(1)`. So if you've previously been using sup
@@ -77,18 +77,18 @@ Supported options for **dump** include
 
    Control what kind of metadata is included in the output.
 
-   **config**
+   config
      Output configuration data stored in the database. Each line
      starts with "#@ ", followed by a space separated key-value
      pair.  Both key and value are hex encoded if needed.
 
-   **properties**
+   properties
      Output per-message (key,value) metadata.  Each line starts
      with "#= ", followed by a message id, and a space separated
      list of key=value pairs.  Ids, keys and values are hex encoded
      if needed.  See :any:`notmuch-properties(7)` for more details.
 
-   **tags**
+   tags
      Output per-message boolean metadata, namely tags. See *format* above
      for description of the output.
 
index 82c4a7a0b94ea7a328d832591f59128424741923..da9ca791aa29bf98ce632c3154c83f73baf1d6c2 100644 (file)
@@ -14,7 +14,7 @@ DESCRIPTION
 
 **notmuch insert** reads a message from standard input and delivers it
 into the maildir directory given by configuration option
-**database.path**, then incorporates the message into the notmuch
+**database.mail_root**, then incorporates the message into the notmuch
 database. It is an alternative to using a separate tool to deliver the
 message then running :any:`notmuch-new(1)` afterwards.
 
@@ -38,7 +38,7 @@ Supported options for **insert** include
 .. option:: --folder=<folder>
 
    Deliver the message to the specified folder, relative to the
-   top-level directory given by the value of **database.path**. The
+   top-level directory given by the value of **database.mail_root**. The
    default is the empty string, which means delivering to the
    top-level directory.
 
index 4a78a90b2251d51d890a44026ee25dbe03babbd7..fa0371f955e81fe49c15fefc5d7ccca5acd43261 100644 (file)
@@ -40,22 +40,22 @@ Supported options for **reply** include
 
 .. option:: --format=(default|json|sexp|headers-only)
 
-   **default**
+   default
      Includes subject and quoted message body as an RFC 2822
      message.
 
-   **json**
+   json
      Produces JSON output containing headers for a reply message
      and the contents of the original message. This output can be
      used by a client to create a reply message intelligently.
 
-   **sexp**
+   sexp
      Produces S-Expression output containing headers for a reply
      message and the contents of the original message. This output
      can be used by a client to create a reply message
      intelligently.
 
-   **headers-only**
+   headers-only
      Only produces In-Reply-To, References, To, Cc, and Bcc
      headers.
 
@@ -67,10 +67,10 @@ Supported options for **reply** include
 
 .. option:: --reply-to=(all|sender)
 
-   **all** (default)
+   all (default)
      Replies to all addresses.
 
-   **sender**
+   sender
      Replies only to the sender. If replying to user's own message
      (Reply-to: or From: header is one of the user's configured
      email addresses), try To:, Cc:, and Bcc: headers in this
index bd452475768d82db28bf13bb9468ddfbe819304a..ac6b4245e94dbfa2afae92f44ef2824b779c24bc 100644 (file)
@@ -32,14 +32,14 @@ Supported options for **restore** include
    line specifying a message-id and a set of tags. For details of the
    actual formats, see :any:`notmuch-dump(1)`.
 
-   **sup**
+   sup
      The **sup** dump file format is specifically chosen to be
      compatible with the format of files produced by sup-dump. So
      if you've previously been using sup for mail, then the
      **notmuch restore** command provides you a way to import all
      of your tags (or labels as sup calls them).
 
-   **batch-tag**
+   batch-tag
      The **batch-tag** dump format is intended to more robust
      against malformed message-ids and tags containing whitespace
      or non-\ **ascii(7)** characters. See :any:`notmuch-dump(1)` for
@@ -49,7 +49,7 @@ Supported options for **restore** include
      changes if the **maildir.synchronize\_flags** configuration
      option is enabled. See :any:`notmuch-config(1)` for details.
 
-   **auto**
+   auto
      This option (the default) tries to guess the format from the
      input. For correctly formed input in either supported format,
      this heuristic, based the fact that batch-tag format contains
@@ -59,18 +59,18 @@ Supported options for **restore** include
 
    Control what kind of metadata is restored.
 
-   **config**
+   config
      Restore configuration data to the database. Each configuration
      line starts with "#@ ", followed by a space separated
      key-value pair.  Both key and value are hex encoded if needed.
 
-   **properties**
+   properties
      Restore per-message (key,value) metadata.  Each line starts
      with "#= ", followed by a message id, and a space separated
      list of key=value pairs.  Ids, keys and values are hex encoded
      if needed.  See :any:`notmuch-properties(7)` for more details.
 
-   **tags**
+   tags
      Restore per-message metadata, namely tags. See *format* above
      for more details.
 
index 2d9ca2d5abec09b22a9e47846a08e55ee2c4fd83..ad305efdb6a07a2ff2635c618da21fcce2c9ad9d 100644 (file)
@@ -43,7 +43,7 @@ Supported options for **search** include
 
 .. option:: --output=(summary|threads|messages|files|tags)
 
-   **summary**
+   summary
      Output a summary of each thread with any message matching the
      search terms. The summary includes the thread ID, date, the
      number of messages in the thread (both the number matched and
@@ -52,19 +52,19 @@ Supported options for **search** include
      for some messages, the total number of files is printed in
      parentheses (see below for an example).
 
-   **threads**
+   threads
      Output the thread IDs of all threads with any message matching
      the search terms, either one per line (``--format=text``),
      separated by null characters (``--format=text0``), as a JSON array
      (``--format=json``), or an S-Expression list (``--format=sexp``).
 
-   **messages**
+   messages
      Output the message IDs of all messages matching the search
      terms, either one per line (``--format=text``), separated by null
      characters (``--format=text0``), as a JSON array (``--format=json``),
      or as an S-Expression list (``--format=sexp``).
 
-   **files**
+   files
      Output the filenames of all messages matching the search
      terms, either one per line (``--format=text``), separated by null
      characters (``--format=text0``), as a JSON array (``--format=json``),
@@ -78,7 +78,7 @@ Supported options for **search** include
      in other directories that are included in the output, although
      these files alone would not match the search.
 
-   **tags**
+   tags
      Output all tags that appear on any message matching the search
      terms, either one per line (``--format=text``), separated by null
      characters (``--format=text0``), as a JSON array (``--format=json``),
@@ -115,20 +115,20 @@ Supported options for **search** include
    terms. This option specifies whether to omit excluded messages in
    the search process.
 
-   **true** (default)
+   true (default)
      Prevent excluded messages from matching the search terms.
 
-   **all**
+   all
      Additionally prevent excluded messages from appearing in
      displayed results, in effect behaving as though the excluded
      messages do not exist.
 
-   **false**
+   false
      Allow excluded messages to match search terms and appear in
      displayed results. Excluded messages are still marked in the
      relevant outputs.
 
-   **flag**
+   flag
      Only has an effect when ``--output=summary``. The output is
      almost identical to **false**, but the "match count" is the
      number of matching non-excluded messages in the thread, rather
index 64639174d6c2763049d2b65d3c7d58c8c1bb00c6..1b02d4079a34d4e9b5ba471eb2f90f9eadd458b4 100644 (file)
@@ -36,7 +36,7 @@ Supported options for **show** include
 
 .. option:: --format=(text|json|sexp|mbox|raw)
 
-   **text** (default for messages)
+   text (default for messages)
      The default plain-text format has all text-content MIME parts
      decoded. Various components in the output, (**message**,
      **header**, **body**, **attachment**, and MIME **part**), will
@@ -46,7 +46,7 @@ Supported options for **show** include
      '}'), to either open or close the component. For a multipart
      MIME message, these parts will be nested.
 
-   **json**
+   json
      The output is formatted with Javascript Object Notation
      (JSON). This format is more robust than the text format for
      automated processing. The nested structure of multipart MIME
@@ -58,7 +58,7 @@ Supported options for **show** include
      as UTF-8 and any message content included in the output will
      be charset-converted to UTF-8.
 
-   **sexp**
+   sexp
      The output is formatted as the Lisp s-expression (sexp)
      equivalent of the JSON format above. Objects are formatted as
      property lists whose keys are keywords (symbols preceded by a
@@ -66,7 +66,7 @@ Supported options for **show** include
      formatted as ``nil``. As for JSON, the s-expression output is
      always encoded as UTF-8.
 
-   **mbox**
+   mbox
      All matching messages are output in the traditional, Unix mbox
      format with each message being prefixed by a line beginning
      with "From " and a blank line separating each message. Lines
@@ -77,7 +77,7 @@ Supported options for **show** include
 
        http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html
 
-   **raw** (default if ``--part`` is given)
+   raw (default if ``--part`` is given)
      Write the raw bytes of the given MIME part of a message to
      standard out. For this format, it is an error to specify a
      query that matches more than one message.
@@ -221,6 +221,13 @@ email messages. For this, use a search term of "thread:<thread-id>" as
 can be seen in the first column of output from the
 :any:`notmuch-search(1)` command.
 
+CONFIGURATION
+=============
+
+Structured output (json / sexp) is influenced by the configuration
+option :ref:`show.extra_headers <show.extra_headers>`. See
+:any:`notmuch-config(1)` for details.
+
 EXIT STATUS
 ===========
 
index 268917cdec981b78c55334a63aaf50f42acdd519..0ab5efbc9c503e64cac3659c7f6f0ccdc0947f4f 100644 (file)
@@ -19,7 +19,7 @@ must have executable permissions.
 
 The currently available hooks are described below.
 
-**pre-new**
+pre-new
     This hook is invoked by the :any:`notmuch-new(1)` command before
     scanning or importing new messages into the database. If this hook
     exits with a non-zero status, notmuch will abort further
@@ -28,7 +28,7 @@ The currently available hooks are described below.
     Typically this hook is used for fetching or delivering new mail to
     be imported into the database.
 
-**post-new**
+post-new
     This hook is invoked by the :any:`notmuch-new(1)` command after
     new messages have been imported into the database and initial tags
     have been applied. The hook will not be run if there have been any
@@ -37,7 +37,7 @@ The currently available hooks are described below.
     Typically this hook is used to perform additional query-based
     tagging on the imported messages.
 
-**post-insert**
+post-insert
     This hook is invoked by the :any:`notmuch-insert(1)` command after
     the message has been delivered, added to the database, and initial
     tags have been applied. The hook will not be run if there have
index 7f128b5c4f0f9c8a57e07b14bebee9f2f4cc261f..ff79f4c2f2c9774b885600d64682267d314ea227 100644 (file)
@@ -55,7 +55,7 @@ MESSAGE PROPERTIES
 The following properties are set by notmuch internally in the course
 of its normal activity.
 
-**index.decryption**
+index.decryption
     If a message contains encrypted content, and notmuch tries to
     decrypt that content during indexing, it will add the property
     ``index.decryption=success`` when the cleartext was successfully
@@ -75,8 +75,7 @@ of its normal activity.
     :any:`notmuch-config(1)`), then this property will not be set on that
     message.
 
-**session-key**
-
+session-key
     When :any:`notmuch-show(1)` or :any:`notmuch-reply(1)` encounters
     a message with an encrypted part, if notmuch finds a
     ``session-key`` property associated with the message, it will try
@@ -111,8 +110,7 @@ of its normal activity.
     example, an AES-128 key might be stashed in a notmuch property as:
     ``session-key=7:14B16AF65536C28AF209828DFE34C9E0``.
 
-**index.repaired**
-
+index.repaired
     Some messages arrive in forms that are confusing to view; they can
     be mangled by mail transport agents, or the sending mail user
     agent may structure them in a way that is confusing.  If notmuch
index 019d15f0b70fcfafc8cdcd2ccc7abec0a81ef7d5..bc8e5086f342be9568cfc3baafac3e700c5c623b 100644 (file)
@@ -21,7 +21,7 @@ build of notmuch supports it with
 
 ::
 
-   $ notmuch config get built_with.sexpr_query
+   $ notmuch config get built_with.sexp_queries
 
 
 S-EXPRESSIONS
@@ -31,10 +31,12 @@ An *s-expression* is either an atom, or list of whitespace delimited
 s-expressions inside parentheses. Atoms are either
 
 *basic value*
+
     A basic value is an unquoted string containing no whitespace, double quotes, or
     parentheses.
 
 *quoted string*
+
     Double quotes (") delimit strings possibly containing whitespace
     or parentheses. These can contain double quote characters by
     escaping with backslash. E.g. ``"this is a quote \""``.
@@ -48,9 +50,11 @@ a *field*, *logical operation*, or *modifier*, and 0 or more
 subqueries.
 
 ``*``
+
    "*" matches any non-empty string in the current field.
 
 ``()``
+
     The empty list matches all messages
 
 *term*
@@ -62,19 +66,23 @@ subqueries.
     phrase splitting see :any:`fields`.
 
 ``(`` *field* |q1| |q2| ... |qn| ``)``
+
     Restrict the queries |q1| to |qn| to *field*, and combine with *and*
     (for most fields) or *or*. See :any:`fields` for more information.
 
 ``(`` *operator* |q1| |q2| ... |qn| ``)``
+
     Combine queries |q1| to |qn|. Currently supported operators are
     ``and``, ``or``, and ``not``. ``(not`` |q1| ... |qn| ``)`` is equivalent
     to ``(and (not`` |q1| ``) ... (not`` |qn| ``))``.
 
 ``(`` *modifier* |q1| |q2| ... |qn| ``)``
+
     Combine queries |q1| to |qn|, and reinterpret the result (e.g. as a regular expression).
     See :any:`modifiers` for more information.
 
 ``(macro (`` |p1| ... |pn| ``) body)``
+
     Define saved query with parameter substitution. The syntax is
     recognized only in saved s-expression queries (see ``squery.*`` in
     :any:`notmuch-config(1)`). Parameter names in ``body`` must be
@@ -164,26 +172,31 @@ MODIFIERS
 that are neither operators nor fields.
 
 ``(infix`` *atom* ``)``
+
     Interpret *atom* as an infix notmuch query (see
     :any:`notmuch-search-terms(7)`). Not supported inside fields.
 
 ``(matching`` |q1| |q2| ... |qn| ``)`` ``(of`` |q1| |q2| ... |qn|  ``)``
+
     Match all messages have the same values of the current field as
     those matching all of |q1| ... |qn|. Supported in most term [#not-path]_ or
     phrase fields. Most commonly used in the ``thread`` field.
 
 ``(query`` *atom* ``)``
+
     Expand to the saved query named by *atom*. See
     :any:`notmuch-config(1)` for more. Note that the saved query must
     be in infix syntax (:any:`notmuch-search-terms(7)`). Not supported
     inside fields.
 
 ``(regex`` *atom* ``)`` ``(rx`` *atom* ``)``
+
     Interpret *atom* as a POSIX.2 regular expression (see
     :manpage:`regex(7)`). This applies in term fields and a subset [#not-phrase]_ of
     phrase fields (see :any:`field-table`).
 
 ``(starts-with`` *subword* ``)``
+
     Matches any term starting with *subword*.  This applies in either
     phrase or term :any:`fields <fields>`, or outside of fields [#not-body]_. Note that
     a ``starts-with`` query cannot be part of a phrase. The
@@ -193,63 +206,80 @@ EXAMPLES
 ========
 
 ``Wizard``
+
     Match all messages containing the word "wizard", ignoring case.
 
 ``added``
+
     Match all messages containing "added", but also those containing "add", "additional",
     "Additional", "adds", etc... via stemming.
 
 ``(and Bob Marley)``
+
     Match messages containing words "Bob" and "Marley", or their stems
     The words need not be adjacent.
 
 ``(not Bob Marley)``
+
     Match messages containing neither "Bob" nor "Marley", nor their stems,
 
 ``"quick fox"`` ``quick-fox`` ``quick@fox``
+
     Match the *phrase* "quick" followed by "fox" in phrase fields (or
     outside a field). Match the literal string in a term field.
 
 ``(folder (of (id 1234@invalid)))``
+
     Match any message in the same folder as the one with Message-Id "1234@invalid"
 
 ``(id 1234@invalid blah@test)``
+
     Matches Message-Id "1234@invalid" *or* Message-Id "blah@test"
 
 ``(and (infix "date:2009-11-18..2009-11-18") (tag unread))``
+
     Match messages in the given date range with tag unread.
 
 ``(starts-with prelim)``
+
     Match any words starting with "prelim".
 
 ``(subject quick "brown fox")``
+
     Match messages whose subject contains "quick" (anywhere, stemmed) and
     the phrase "brown fox".
 
 ``(subject (starts-with prelim))``
+
     Matches any word starting with "prelim", inside a message subject.
 
 ``(subject (starts-wih quick) "brown fox")``
+
     Match messages whose subject contains "quick brown fox", but also
     "brown fox quicksand".
 
 ``(thread (of (id 1234@invalid)))``
+
     Match any message in the same thread as the one with Message-Id "1234@invalid"
 
 ``(thread (matching (from bob@example.com) (to bob@example.com)))``
+
     Match any (messages in) a thread containing a message from
     "bob@example.com" and a (possibly distinct) message to "bob at
     example.com")
 
 ``(to (or bob@example.com mallory@example.org))`` ``(or (to bob@example.com) (to mallory@example.org))``
+
     Match in the "To" or "Cc" headers, "bob@example.com",
     "mallory@example.org", and also "bob@example.com.au" since it
     contains the adjacent triple "bob", "example", "com".
 
 ``(not (to *))``
+
     Match messages with an empty or invalid 'To' and 'Cc' field.
 
 ``(List *)``
+
     Match messages with a non-empty List-Id header, assuming
     configuration ``index.header.List=List-Id``
 
@@ -306,10 +336,10 @@ NOTES
                in notmuch, this modifier is not supported in the
                ``path`` field.
 
-.. |q1| replace:: :math:`q_1`
-.. |q2| replace:: :math:`q_2`
-.. |qn| replace:: :math:`q_n`
+.. |q1| replace:: `q`\ :sub:`1`
+.. |q2| replace:: `q`\ :sub:`2`
+.. |qn| replace:: `q`\ :sub:`n`
 
-.. |p1| replace:: :math:`p_1`
-.. |p2| replace:: :math:`p_2`
-.. |pn| replace:: :math:`p_n`
+.. |p1| replace:: `p`\ :sub:`1`
+.. |p2| replace:: `p`\ :sub:`2`
+.. |pn| replace:: `p`\ :sub:`n`
index 12ee25e5ba10795315dfb3e3373f5d11968693b5..85b2c0ea39ada56b9be4dc350fdf85625a6dd990 100644 (file)
@@ -56,7 +56,7 @@ notmuch-hello key bindings
 ``<tab>``
     Move to the next widget (button or text entry field)
 
-``<backspace>``
+``<backtab>``
     Move to the previous widget.
 
 ``<return>``
@@ -175,6 +175,16 @@ variables.
 :index:`notmuch-search-oldest-first`
     Display the oldest threads at the top of the buffer
 
+It is also possible to customize how the name of buffers containing
+search results is formatted using the following variables:
+
+:index:`notmuch-search-buffer-name-format`
+       |docstring::notmuch-search-buffer-name-format|
+
+:index:`notmuch-saved-search-buffer-name-format`
+       |docstring::notmuch-saved-search-buffer-name-format|
+
+
 .. _notmuch-show:
 
 notmuch-show
@@ -222,6 +232,9 @@ Display of messages can be controlled by the following variables
 :index:`notmuch-message-headers-visible`
        |docstring::notmuch-message-headers-visible|
 
+:index:`notmuch-show-header-line`
+       |docstring::notmuch-show-header-line|
+
 .. _show-copy:
 
 Copy to kill-ring
index d1b320c33d28a7303c4977ad692daf5da03ef5ae..0f1f0eb2ef69f50cf86a7903b25932257157edf7 100644 (file)
@@ -42,7 +42,7 @@ emacs_mua := $(dir)/notmuch-emacs-mua
 emacs_mua_desktop := $(dir)/notmuch-emacs-mua.desktop
 
 emacs_images := \
-       $(srcdir)/$(dir)/notmuch-logo.png
+       $(srcdir)/$(dir)/notmuch-logo.svg
 
 emacs_bytecode = $(emacs_sources:.el=.elc)
 emacs_docstrings = $(emacs_sources:.el=.rsti)
index d820525b3f0240a3afe0e41a5faa82b85cd16cd2..79d2a1b7f03db509b95705b1701fa601cf7987ca 100644 (file)
@@ -45,7 +45,7 @@ Otherwise respect `fill-column'."
   :group 'coolj
   :type 'boolean)
 
-(defcustom coolj-line-prefix-regexp "^\\(>+ \\)*"
+(defcustom coolj-line-prefix-regexp "^\\(>+ ?\\)*"
   "Regular expression that matches line prefixes."
   :group 'coolj
   :type 'regexp)
index 71487bd97b8795b71747594f98bcd54ed821a976..beb25382aa2fa9f5c91bc653e80b79bf6281c0f6 100644 (file)
@@ -198,7 +198,7 @@ fields of the search."
 (defvar notmuch-hello-indent 4
   "How much to indent non-headers.")
 
-(defimage notmuch-hello-logo ((:type png :file "notmuch-logo.png")))
+(defimage notmuch-hello-logo ((:type svg :file "notmuch-logo.svg")))
 
 (defcustom notmuch-show-logo t
   "Should the notmuch logo be shown?"
@@ -486,11 +486,14 @@ diagonal."
 (defun notmuch-hello-widget-search (widget &rest _ignore)
   (cl-case (widget-get widget :notmuch-search-type)
    (tree
-    (notmuch-tree (widget-get widget :notmuch-search-terms)
-                 nil nil nil nil nil nil
-                 (widget-get widget :notmuch-search-oldest-first)))
+    (let ((n (notmuch-search-format-buffer-name (widget-value widget) "tree" t)))
+      (notmuch-tree (widget-get widget :notmuch-search-terms)
+                   nil nil n nil nil nil
+                   (widget-get widget :notmuch-search-oldest-first))))
    (unthreaded
-    (notmuch-unthreaded (widget-get widget :notmuch-search-terms)))
+    (let ((n (notmuch-search-format-buffer-name (widget-value widget)
+                                               "unthreaded" t)))
+      (notmuch-unthreaded (widget-get widget :notmuch-search-terms) nil nil n)))
    (t
     (notmuch-search (widget-get widget :notmuch-search-terms)
                    (widget-get widget :notmuch-search-oldest-first)))))
@@ -557,7 +560,8 @@ with any properties in the original saved-search.
 
 The values :show-empty-searches, :filter and :filter-count from
 options will be handled as specified for
-`notmuch-hello-insert-searches'."
+`notmuch-hello-insert-searches'. :disable-includes can be used to
+turn off the default exclude processing in `notmuch-count(1)'"
   (with-temp-buffer
     (dolist (elem query-list nil)
       (let ((count-query (or (notmuch-saved-search-get elem :count-query)
@@ -570,7 +574,11 @@ options will be handled as specified for
                                            (plist-get options :filter))))
         "\n")))
     (unless (= (notmuch--call-process-region (point-min) (point-max) notmuch-command
-                                   t t nil "count" "--batch") 0)
+                                            t t nil "count"
+                                            (if (plist-get options :disable-excludes)
+                                                "--exclude=false"
+                                              "--exclude=true")
+                                            "--batch") 0)
       (notmuch-logged-error
        "notmuch count --batch failed"
        "Please check that the notmuch CLI is new enough to support `count
@@ -702,7 +710,6 @@ with `notmuch-hello-query-counts'."
   ;; that when we modify map it does not modify widget-keymap).
   (let ((map (make-composed-keymap (list (make-sparse-keymap) widget-keymap))))
     (set-keymap-parent map notmuch-common-keymap)
-    (define-key map (kbd "<C-tab>") 'widget-backward)
     map)
   "Keymap for \"notmuch hello\" buffers.")
 
@@ -786,7 +793,7 @@ Complete list of currently available key bindings:
                   :help-echo "Refresh"
                   (notmuch-hello-nice-number
                    (string-to-number
-                    (car (notmuch--process-lines notmuch-command "count")))))
+                    (car (notmuch--process-lines notmuch-command "count" "--exclude=false")))))
     (widget-insert " messages.\n")))
 
 (defun notmuch-hello-insert-saved-searches ()
@@ -918,7 +925,8 @@ following:
    nil
    :initially-hidden (not notmuch-show-all-tags-list)
    :hide-tags notmuch-hello-hide-tags
-   :filter notmuch-hello-tag-list-make-query))
+   :filter notmuch-hello-tag-list-make-query
+   :disable-excludes t))
 
 (defun notmuch-hello-insert-footer ()
   "Insert the notmuch-hello footer."
diff --git a/emacs/notmuch-logo.svg b/emacs/notmuch-logo.svg
new file mode 100644 (file)
index 0000000..2c65a73
--- /dev/null
@@ -0,0 +1,27 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<svg xmlns="http://www.w3.org/2000/svg" width="100" height="100"
+     viewbox="0 0 100 100" fill="#000" stroke-width="2">
+  <circle cx="50" cy="5" r="5" />
+  <g transform="translate(50 20) rotate(20)">
+    <circle cx="-47" cy="0" r="3" />
+    <circle cx="47"  cy="0" r="3" />
+    <path d="M-47 -1  L0 -3  L47 -1 L47 1  L0 3  L-47 1 Z" />
+  </g>
+  <path d="M49 4  L45 88
+           A5 5  0  0 1  40 93  L20 93  A5 5  0  0 0  15 100
+           L85 100
+           A5 5  0  0 0  80 93  L60 93  A5 5  0  0 1  55 88
+           L55 90  L51 4 Z" />
+  <g fill="#fff" stroke="#000">
+    <rect x="7" y="33" width="30" height="18" />
+    <line x1="7"  y1="51" x2="18" y2="41" />
+    <line x1="37" y1="51" x2="26" y2="41" />
+    <polyline points="7 33  22 44  37 33" fill="none" />
+  </g>
+  <path d="M-18 0  A24 20  0  0 0  18 0" transform="translate(22 51.0)" />
+  <path d="M-18 0  A24 20  0  0 0  18 0" transform="translate(78 71.5)" />
+  <g fill="none" stroke="#000">
+    <path d="M9  53.0  l 12 -42  l 2 0  l 12 42" />
+    <path d="M91 73.5  l-12 -42  l-2 0  l-12 42" />
+  </g>
+</svg>
index 7e177bf7fb4c25bd7d7b4ecb6bfe916292433bdc..5102078849d629a2da37bac13c9a3548153ff996 100644 (file)
@@ -149,6 +149,7 @@ Otherwise set it according to `notmuch-fcc-dirs'."
         (buf (current-buffer))
         (mml-externalize-attachments message-fcc-externalize-attachments))
      (with-current-buffer (get-buffer-create " *message temp*")
+       (message-clone-locals buf) ;; for message-encoded-mail-cache
        (erase-buffer)
        (insert-buffer-substring buf)
        ,@body)))
@@ -158,7 +159,10 @@ Otherwise set it according to `notmuch-fcc-dirs'."
 
 This should be called on a temporary copy.
 This is taken from the function message-do-fcc."
-  (message-encode-message-body)
+  (if (not message-encoded-mail-cache)
+      (message-encode-message-body)
+    (erase-buffer)
+    (insert message-encoded-mail-cache))
   (save-restriction
     (message-narrow-to-headers)
     (mail-encode-encoded-word-buffer))
@@ -179,12 +183,12 @@ This is a rearranged version of message mode's message-do-fcc."
        (setq file (message-fetch-field "fcc" t)))
       (when file
        (with-temporary-notmuch-message-buffer
+        (notmuch-maildir-setup-message-for-saving)
         (save-restriction
           (message-narrow-to-headers)
           (while (setq file (message-fetch-field "fcc" t))
             (push file files)
             (message-remove-header "fcc" nil t)))
-        (notmuch-maildir-setup-message-for-saving)
         ;; Process FCC operations.
         (mapc #'notmuch-fcc-handler files)
         (kill-buffer (current-buffer)))))))
index 4de3e423bec077ab85b622ac05e40fef2582c494..7c1f02c92f1499be58df08929863d4e1aa013118 100644 (file)
@@ -84,6 +84,11 @@ visible for any given message."
   :type 'boolean
   :group 'notmuch-show)
 
+(defcustom notmuch-show-header-line t
+  "Show a header line with the current message's subject."
+  :type 'boolean
+  :group 'notmuch-show)
+
 (defcustom notmuch-show-relative-dates t
   "Display relative dates in the message summary line."
   :type 'boolean
@@ -1345,11 +1350,12 @@ If no messages match the query return NIL."
       (notmuch-show-mapc
        (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
       ;; Set the header line to the subject of the first message.
-      (setq header-line-format
-           (replace-regexp-in-string "%" "%%"
-                                     (notmuch-sanitize
-                                      (notmuch-show-strip-re
-                                       (notmuch-show-get-subject)))))
+      (when notmuch-show-header-line
+       (setq header-line-format
+             (replace-regexp-in-string "%" "%%"
+                                       (notmuch-sanitize
+                                        (notmuch-show-strip-re
+                                         (notmuch-show-get-subject))))))
       (run-hooks 'notmuch-show-hook)
       (if state
          (notmuch-show-apply-state state)
index 536315e9a788f7ae10dc2a58750fb99e1f874584..8af09e68b8b42143b958d26a55740ffcdfcc6b89 100644 (file)
@@ -241,7 +241,7 @@ DATA is the content of an SVG picture (e.g., as returned by
   "Return SVG data representing a star icon.
 This can be used with `notmuch-tag-format-image-data'."
   "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
-<svg version=\"1.1\" width=\"16\" height=\"16\">
+<svg version=\"1.1\" width=\"16\" height=\"16\" xmlns=\"http://www.w3.org/2000/svg\">
   <g transform=\"translate(-242.81601,-315.59635)\">
     <path
        d=\"m 290.25762,334.31206 -17.64143,-11.77975 -19.70508,7.85447 5.75171,-20.41814 -13.55925,-16.31348 21.19618,-0.83936 11.325,-17.93675 7.34825,19.89939 20.55849,5.22795 -16.65471,13.13786 z\"
@@ -254,7 +254,7 @@ This can be used with `notmuch-tag-format-image-data'."
   "Return SVG data representing an empty star icon.
 This can be used with `notmuch-tag-format-image-data'."
   "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
-<svg version=\"1.1\" width=\"16\" height=\"16\">
+<svg version=\"1.1\" width=\"16\" height=\"16\" xmlns=\"http://www.w3.org/2000/svg\">
   <g transform=\"translate(-242.81601,-315.59635)\">
     <path
        d=\"m 290.25762,334.31206 -17.64143,-11.77975 -19.70508,7.85447 5.75171,-20.41814 -13.55925,-16.31348 21.19618,-0.83936 11.325,-17.93675 7.34825,19.89939 20.55849,5.22795 -16.65471,13.13786 z\"
@@ -267,7 +267,7 @@ This can be used with `notmuch-tag-format-image-data'."
   "Return SVG data representing a tag icon.
 This can be used with `notmuch-tag-format-image-data'."
   "<?xml version=\"1.0\" encoding=\"UTF-8\" standalone=\"no\"?>
-<svg version=\"1.1\" width=\"16\" height=\"16\">
+<svg version=\"1.1\" width=\"16\" height=\"16\" xmlns=\"http://www.w3.org/2000/svg\">
   <g transform=\"translate(0,-1036.3622)\">
     <path
        d=\"m 0.44642857,1040.9336 12.50000043,0 2.700893,3.6161 -2.700893,3.616 -12.50000043,0 z\"
@@ -429,17 +429,9 @@ initial input in the minibuffer."
            (set-keymap-parent map crm-local-completion-map)
            (define-key map " " 'self-insert-command)
            map)))
-    (delete "" (completing-read-multiple
-               prompt
-               ;; Append the separator to each completion so when the
-               ;; user completes a tag they can immediately begin
-               ;; entering another.  `completing-read-multiple'
-               ;; ultimately splits the input on crm-separator, so we
-               ;; don't need to strip this back off (we just need to
-               ;; delete "empty" entries caused by trailing spaces).
-               (mapcar (lambda (tag-op) (concat tag-op crm-separator)) tag-list)
-               nil nil initial-input
-               'notmuch-read-tag-changes-history))))
+    (completing-read-multiple prompt tag-list
+                             nil nil initial-input
+                             'notmuch-read-tag-changes-history)))
 
 ;;; Tagging
 
index 001a367d99f8bf6f6fa737c46b53319c6108d957..303c6fadcdb2da81cc0189bfe062d8b77b6bd753 100644 (file)
@@ -179,7 +179,7 @@ Note that the author string should not contain whitespace
      (:foreground "dark blue"))
     (t
      (:bold t)))
-  "Face used in tree mode for the date in messages matching the query."
+  "Face used in tree mode for the author in messages matching the query."
   :group 'notmuch-tree
   :group 'notmuch-faces)
 
@@ -236,7 +236,7 @@ Note that the author string should not contain whitespace
 
 (defface notmuch-tree-no-match-author-face
   nil
-  "Face used in tree mode for the date in messages matching the query."
+  "Face used in tree mode for non-matching authors."
   :group 'notmuch-tree
   :group 'notmuch-faces)
 
@@ -1191,11 +1191,11 @@ The arguments are:
     (setq query (notmuch-read-query (concat "Notmuch "
                                            (if unthreaded "unthreaded " "tree ")
                                            "view search: "))))
-  (let ((buffer (get-buffer-create (generate-new-buffer-name
-                                   (or buffer-name
-                                       (concat "*notmuch-"
-                                               (if unthreaded "unthreaded-" "tree-")
-                                               query "*")))))
+  (let* ((name
+         (or buffer-name
+             (notmuch-search-buffer-title query
+                                          (if unthreaded "unthreaded" "tree"))))
+        (buffer (get-buffer-create (generate-new-buffer-name name)))
        (inhibit-read-only t))
     (pop-to-buffer-same-window buffer))
   ;; Don't track undo information for this buffer
@@ -1206,6 +1206,9 @@ The arguments are:
 
 (defun notmuch-unthreaded (&optional query query-context target buffer-name
                                     open-target)
+  "Display threads matching QUERY in unthreaded view.
+
+See function NOTMUCH-TREE for documentation of the arguments"
   (interactive)
   (notmuch-tree query query-context target buffer-name open-target t))
 
index 2ef67c0e798dbc65c144cef77cd8642c506f98c0..6abb17ffdc6d0b1b529ac0024bb08cff5a127b45 100644 (file)
@@ -535,12 +535,12 @@ thread."
       (message "End of search results."))))
 
 (defun notmuch-tree-from-search-current-query ()
-  "Call notmuch tree with the current query."
+  "Tree view of current query."
   (interactive)
   (notmuch-tree notmuch-search-query-string))
 
 (defun notmuch-unthreaded-from-search-current-query ()
-  "Call notmuch tree with the current query."
+  "Unthreaded view of current query."
   (interactive)
   (notmuch-unthreaded notmuch-search-query-string))
 
@@ -880,6 +880,14 @@ sets the :orig-tag property."
       (setq notmuch-search-target-thread "found")
       (goto-char pos))))
 
+(defvar-local notmuch--search-hook-run nil
+  "Flag used to ensure the notmuch-search-hook is only run once per buffer")
+
+(defun notmuch--search-hook-wrapper ()
+  (unless notmuch--search-hook-run
+    (setq notmuch--search-hook-run t)
+    (run-hooks 'notmuch-search-hook)))
+
 (defun notmuch-search-process-filter (proc string)
   "Process and filter the output of \"notmuch search\"."
   (let ((results-buf (process-buffer proc))
@@ -892,7 +900,9 @@ sets the :orig-tag property."
          (goto-char (point-max))
          (insert string))
        (notmuch-sexp-parse-partial-list 'notmuch-search-append-result
-                                        results-buf)))))
+                                        results-buf))
+      (with-current-buffer results-buf
+       (notmuch--search-hook-wrapper)))))
 
 ;;; Commands (and some helper functions used by them)
 
@@ -905,7 +915,39 @@ See `notmuch-tag' for information on the format of TAG-CHANGES."
          (notmuch-search-get-tags-region (point-min) (point-max)) "Tag all")))
   (notmuch-search-tag tag-changes (point-min) (point-max) t))
 
-(defun notmuch-search-buffer-title (query)
+(defcustom notmuch-search-buffer-name-format "*notmuch-%t-%s*"
+  "Format for the name of search results buffers.
+
+In this spec, %s will be replaced by a description of the search
+query and %t by its type (search, tree or unthreaded).  The
+buffer name is formatted using `format-spec': see its docstring
+for additional parameters for the s and t format specifiers.
+
+See also `notmuch-saved-search-buffer-name-format'"
+  :type 'string
+  :group 'notmuch-search)
+
+(defcustom notmuch-saved-search-buffer-name-format "*notmuch-saved-%t-%s*"
+  "Format for the name of search results buffers for saved searches.
+
+In this spec, %s will be replaced by the saved search name and %t
+by its type (search, tree or unthreaded).  The buffer name is
+formatted using `format-spec': see its docstring for additional
+parameters for the s and t format specifiers.
+
+See also `notmuch-search-buffer-name-format'"
+  :type 'string
+  :group 'notmuch-search)
+
+(defun notmuch-search-format-buffer-name (query type saved)
+  "Compose a buffer name for the given QUERY, TYPE (search, tree,
+unthreaded) and whether it's SAVED (t or nil)."
+  (let ((fmt (if saved
+                notmuch-saved-search-buffer-name-format
+              notmuch-search-buffer-name-format)))
+    (format-spec fmt `((?t . ,(or type "search")) (?s . ,query)))))
+
+(defun notmuch-search-buffer-title (query &optional type)
   "Returns the title for a buffer with notmuch search results."
   (let* ((saved-search
          (let (longest
@@ -920,19 +962,20 @@ See `notmuch-tag' for information on the format of TAG-CHANGES."
                     do (setq longest tuple))
            longest))
         (saved-search-name (notmuch-saved-search-get saved-search :name))
+        (saved-search-type (notmuch-saved-search-get saved-search :search-type))
         (saved-search-query (notmuch-saved-search-get saved-search :query)))
     (cond ((and saved-search (equal saved-search-query query))
           ;; Query is the same as saved search (ignoring case)
-          (concat "*notmuch-saved-search-" saved-search-name "*"))
+          (notmuch-search-format-buffer-name saved-search-name
+                                             saved-search-type
+                                             t))
          (saved-search
-          (concat "*notmuch-search-"
-                  (replace-regexp-in-string
-                   (concat "^" (regexp-quote saved-search-query))
-                   (concat "[ " saved-search-name " ]")
-                   query)
-                  "*"))
-         (t
-          (concat "*notmuch-search-" query "*")))))
+          (let ((query (replace-regexp-in-string
+                        (concat "^" (regexp-quote saved-search-query))
+                        (concat "[ " saved-search-name " ]")
+                        query)))
+            (notmuch-search-format-buffer-name query saved-search-type t)))
+         (t (notmuch-search-format-buffer-name query type nil)))))
 
 (defun notmuch-read-query (prompt)
   "Read a notmuch-query from the minibuffer with completion.
@@ -1036,8 +1079,7 @@ the configured default sort order."
          (process-put proc 'parse-buf
                       (generate-new-buffer " *notmuch search parse*"))
          (set-process-filter proc 'notmuch-search-process-filter)
-         (set-process-query-on-exit-flag proc nil))))
-    (run-hooks 'notmuch-search-hook)))
+         (set-process-query-on-exit-flag proc nil))))))
 
 (defun notmuch-search-refresh-view ()
   "Refresh the current view.
index 89958e1222d3582588f69de59958f717074c7902..275e72b89e84d3de95ac73d628576d8da1211d0d 100644 (file)
@@ -32,7 +32,7 @@ notmuch_built_with (const char *name)
        return HAVE_XAPIAN_DB_RETRY_LOCK;
     } else if (STRNCMP_LITERAL (name, "session_key") == 0) {
        return true;
-    } else if (STRNCMP_LITERAL (name, "sexpr_query") == 0) {
+    } else if (STRNCMP_LITERAL (name, "sexp_queries") == 0) {
        return HAVE_SFSEXP;
     } else {
        return false;
index e502858d623db091628e4945d7736c0db2d5b2cd..503a0c8b585323dd59fad974b55ca47b7399fad6 100644 (file)
@@ -435,11 +435,6 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
        for (gchar **keys_p = keys; *keys_p; keys_p++) {
            char *absolute_key = talloc_asprintf (notmuch, "%s.%s", *grp,  *keys_p);
            char *normalized_val;
-           val = g_key_file_get_value (file, *grp, *keys_p, NULL);
-           if (! val) {
-               status = NOTMUCH_STATUS_FILE_ERROR;
-               goto DONE;
-           }
 
            /* If we opened from a given path, do not overwrite it */
            if (strcmp (absolute_key, "database.path") == 0 &&
@@ -447,6 +442,12 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
                notmuch->xapian_db)
                continue;
 
+           val = g_key_file_get_string (file, *grp, *keys_p, NULL);
+           if (! val) {
+               status = NOTMUCH_STATUS_FILE_ERROR;
+               goto DONE;
+           }
+
            normalized_val = _expand_path (notmuch, absolute_key, val);
            _notmuch_string_map_set (notmuch->config, absolute_key, normalized_val);
            g_free (val);
@@ -596,6 +597,8 @@ _notmuch_config_key_to_string (notmuch_config_key_t key)
        return "user.name";
     case NOTMUCH_CONFIG_AUTOCOMMIT:
        return "database.autocommit";
+    case NOTMUCH_CONFIG_EXTRA_HEADERS:
+       return "show.extra_headers";
     default:
        return NULL;
     }
@@ -643,6 +646,7 @@ _notmuch_config_default (notmuch_database_t *notmuch, notmuch_config_key_t key)
        return "";
     case NOTMUCH_CONFIG_AUTOCOMMIT:
        return "8000";
+    case NOTMUCH_CONFIG_EXTRA_HEADERS:
     case NOTMUCH_CONFIG_HOOK_DIR:
     case NOTMUCH_CONFIG_BACKUP_DIR:
     case NOTMUCH_CONFIG_OTHER_EMAIL:
@@ -657,6 +661,10 @@ notmuch_status_t
 _notmuch_config_load_defaults (notmuch_database_t *notmuch)
 {
     notmuch_config_key_t key;
+    notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+
+    if (notmuch->config == NULL)
+       notmuch->config = _notmuch_string_map_create (notmuch);
 
     for (key = NOTMUCH_CONFIG_FIRST;
         key < NOTMUCH_CONFIG_LAST;
@@ -666,11 +674,14 @@ _notmuch_config_load_defaults (notmuch_database_t *notmuch)
 
        val = _notmuch_string_map_get (notmuch->config, key_string);
        if (! val) {
+           if (key == NOTMUCH_CONFIG_MAIL_ROOT && (notmuch->params & NOTMUCH_PARAM_SPLIT))
+               status = NOTMUCH_STATUS_NO_MAIL_ROOT;
+
            _notmuch_string_map_set (notmuch->config, key_string, _notmuch_config_default (notmuch,
                                                                                           key));
        }
     }
-    return NOTMUCH_STATUS_SUCCESS;
+    return status;
 }
 
 const char *
index 8dd7728113335bd7c051ebf536bc8575d596b2a3..657b1aa11dc1d71b411c0fc45b6d814464d51611 100644 (file)
@@ -160,11 +160,12 @@ operator&= (_notmuch_features &a, _notmuch_features b)
 
 /*
  * Configuration options for xapian database fields */
-typedef enum notmuch_field_flags {
+typedef enum {
     NOTMUCH_FIELD_NO_FLAGS     = 0,
     NOTMUCH_FIELD_EXTERNAL     = 1 << 0,
     NOTMUCH_FIELD_PROBABILISTIC = 1 << 1,
     NOTMUCH_FIELD_PROCESSOR    = 1 << 2,
+    NOTMUCH_FIELD_STRIP_TRAILING_SLASH = 1 << 3,
 } notmuch_field_flag_t;
 
 /*
@@ -191,12 +192,17 @@ operator& (notmuch_field_flag_t a, notmuch_field_flag_t b)
                                    Xapian::QueryParser::FLAG_PURE_NOT)
 
 /*
- * Which parameters were explicit when the database was opened */
+ * explicit and implied parameters to open */
 typedef enum {
     NOTMUCH_PARAM_NONE         = 0,
+    /* database passed explicitely */
     NOTMUCH_PARAM_DATABASE     = 1 << 0,
+    /* config file passed explicitely */
     NOTMUCH_PARAM_CONFIG       = 1 << 1,
+    /* profile name passed explicitely */
     NOTMUCH_PARAM_PROFILE      = 1 << 2,
+    /* split (e.g. XDG) configuration */
+    NOTMUCH_PARAM_SPLIT                = 1 << 3,
 } notmuch_open_param_t;
 
 /*
@@ -374,5 +380,10 @@ notmuch_status_t
 _notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
                                      Xapian::Query &output);
 #endif
+
+/* parse-time-vrp.h */
+notmuch_status_t
+_notmuch_date_strings_to_query (Xapian::valueno slot, const std::string &from, const std::string &to,
+                               Xapian::Query &output, std::string &msg);
 #endif
 #endif
index 7eb0de79ee288ce704e302d6dde73d66a9722535..df83e2048673b235e6c1055ccb47d39923f480a7 100644 (file)
@@ -311,6 +311,8 @@ notmuch_status_to_string (notmuch_status_t status)
        return "Database exists, not recreated";
     case NOTMUCH_STATUS_BAD_QUERY_SYNTAX:
        return "Syntax error in query";
+    case NOTMUCH_STATUS_NO_MAIL_ROOT:
+       return "No mail root found";
     default:
     case NOTMUCH_STATUS_LAST_STATUS:
        return "Unknown error status value";
@@ -590,10 +592,12 @@ notmuch_database_compact (const char *path,
     notmuch_database_t *notmuch = NULL;
     char *message = NULL;
 
-    ret = notmuch_database_open_verbose (path,
-                                        NOTMUCH_DATABASE_MODE_READ_WRITE,
-                                        &notmuch,
-                                        &message);
+    ret = notmuch_database_open_with_config (path,
+                                            NOTMUCH_DATABASE_MODE_READ_WRITE,
+                                            "",
+                                            NULL,
+                                            &notmuch,
+                                            &message);
     if (ret) {
        if (status_cb) status_cb (message, closure);
        return ret;
@@ -751,6 +755,8 @@ notmuch_database_destroy (notmuch_database_t *notmuch)
     notmuch->date_range_processor = NULL;
     delete notmuch->last_mod_range_processor;
     notmuch->last_mod_range_processor = NULL;
+    delete notmuch->stemmer;
+    notmuch->stemmer = NULL;
 
     talloc_free (notmuch);
 
index 4a8608586a356c083ed8ad35e70fe8882fa21a25..2ffd19424ea9752d2976a2438c6214c63dba5e41 100644 (file)
 
 #include "notmuch-private.h"
 
+struct _notmuch_indexopts {
+    _notmuch_crypto_t crypto;
+};
+
 notmuch_indexopts_t *
 notmuch_database_get_default_indexopts (notmuch_database_t *db)
 {
index 093c29b1bbc5384198b90c351046a6a3b19200e1..3cc79bc4c86148445aaaf7f831421abdbb45801d 100644 (file)
@@ -121,7 +121,7 @@ typedef enum {
  */
 #define NOTMUCH_MESSAGE_ID_MAX (200 - sizeof (NOTMUCH_METADATA_THREAD_ID_PREFIX))
 
-typedef enum _notmuch_private_status {
+typedef enum {
     /* First, copy all the public status values. */
     NOTMUCH_PRIVATE_STATUS_SUCCESS                     = NOTMUCH_STATUS_SUCCESS,
     NOTMUCH_PRIVATE_STATUS_OUT_OF_MEMORY               = NOTMUCH_STATUS_OUT_OF_MEMORY,
@@ -173,7 +173,7 @@ typedef enum _notmuch_private_status {
      (notmuch_status_t) private_status)
 
 /* Flags shared by various lookup functions. */
-typedef enum _notmuch_find_flags {
+typedef enum {
     /* Lookup without creating any documents.  This is the default
      * behavior. */
     NOTMUCH_FIND_LOOKUP = 0,
@@ -711,9 +711,7 @@ _notmuch_thread_create (void *ctx,
 
 /* indexopts.c */
 
-struct _notmuch_indexopts {
-    _notmuch_crypto_t crypto;
-};
+struct _notmuch_indexopts;
 
 #define CONFIG_HEADER_PREFIX "index.header."
 
index 5c5a024e41defb04968357f1a0a999c6ae7faf45..2e6ec2af1ae2a5054cd1516088764cc44557faa4 100644 (file)
@@ -58,7 +58,7 @@ NOTMUCH_BEGIN_DECLS
  * version in Makefile.local.
  */
 #define LIBNOTMUCH_MAJOR_VERSION        5
-#define LIBNOTMUCH_MINOR_VERSION        5
+#define LIBNOTMUCH_MINOR_VERSION        6
 #define LIBNOTMUCH_MICRO_VERSION        0
 
 
@@ -112,7 +112,7 @@ typedef int notmuch_bool_t;
  * A zero value (NOTMUCH_STATUS_SUCCESS) indicates that the function
  * completed without error. Any other value indicates an error.
  */
-typedef enum _notmuch_status {
+typedef enum {
     /**
      * No error occurred.
      */
@@ -224,6 +224,10 @@ typedef enum _notmuch_status {
      * Syntax error in query
      */
     NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
+    /**
+     * No mail root could be deduced from parameters and environment
+     */
+    NOTMUCH_STATUS_NO_MAIL_ROOT,
     /**
      * Not an actual status value. Just a way to find out how many
      * valid status values there are.
@@ -323,7 +327,7 @@ typedef enum {
  * config_path="" and error_message=NULL
  * @deprecated Deprecated as of libnotmuch 5.4 (notmuch 0.32)
  */
-/* NOTMUCH_DEPRECATED(5, 4) */
+NOTMUCH_DEPRECATED(5, 4)
 notmuch_status_t
 notmuch_database_open (const char *path,
                       notmuch_database_mode_t mode,
@@ -335,7 +339,7 @@ notmuch_database_open (const char *path,
  * @deprecated Deprecated as of libnotmuch 5.4 (notmuch 0.32)
  *
  */
-/* NOTMUCH_DEPRECATED(5, 4) */
+NOTMUCH_DEPRECATED(5, 4)
 notmuch_status_t
 notmuch_database_open_verbose (const char *path,
                               notmuch_database_mode_t mode,
@@ -1686,7 +1690,7 @@ notmuch_message_reindex (notmuch_message_t *message,
 /**
  * Message flags.
  */
-typedef enum _notmuch_message_flag {
+typedef enum {
     NOTMUCH_MESSAGE_FLAG_MATCH,
     NOTMUCH_MESSAGE_FLAG_EXCLUDED,
 
@@ -2532,7 +2536,7 @@ notmuch_config_list_destroy (notmuch_config_list_t *config_list);
 /**
  * Configuration keys known to libnotmuch
  */
-typedef enum _notmuch_config_key {
+typedef enum {
     NOTMUCH_CONFIG_FIRST,
     NOTMUCH_CONFIG_DATABASE_PATH = NOTMUCH_CONFIG_FIRST,
     NOTMUCH_CONFIG_MAIL_ROOT,
@@ -2546,6 +2550,7 @@ typedef enum _notmuch_config_key {
     NOTMUCH_CONFIG_OTHER_EMAIL,
     NOTMUCH_CONFIG_USER_NAME,
     NOTMUCH_CONFIG_AUTOCOMMIT,
+    NOTMUCH_CONFIG_EXTRA_HEADERS,
     NOTMUCH_CONFIG_LAST
 } notmuch_config_key_t;
 
index a942383b84b8cda800ae8006034555e267d11119..30cfcf9e6a985728cc4c21024aed70eb72c1cf38 100644 (file)
@@ -19,9 +19,8 @@ notmuch_database_open (const char *path,
     char *status_string = NULL;
     notmuch_status_t status;
 
-    status = notmuch_database_open_verbose (path, mode, database,
-                                           &status_string);
-
+    status = notmuch_database_open_with_config (path, mode, "", NULL,
+                                               database, &status_string);
     if (status_string) {
        fputs (status_string, stderr);
        free (status_string);
@@ -187,11 +186,10 @@ _db_dir_exists (const char *database_path, char **message)
 }
 
 static notmuch_status_t
-_choose_database_path (void *ctx,
+_choose_database_path (notmuch_database_t *notmuch,
                       const char *profile,
                       GKeyFile *key_file,
                       const char **database_path,
-                      bool *split,
                       char **message)
 {
     if (! *database_path) {
@@ -199,24 +197,24 @@ _choose_database_path (void *ctx,
     }
 
     if (! *database_path && key_file) {
-       char *path = g_key_file_get_value (key_file, "database", "path", NULL);
+       char *path = g_key_file_get_string (key_file, "database", "path", NULL);
        if (path) {
            if (path[0] == '/')
-               *database_path = talloc_strdup (ctx, path);
+               *database_path = talloc_strdup (notmuch, path);
            else
-               *database_path = talloc_asprintf (ctx, "%s/%s", getenv ("HOME"), path);
+               *database_path = talloc_asprintf (notmuch, "%s/%s", getenv ("HOME"), path);
            g_free (path);
        }
     }
     if (! *database_path) {
        notmuch_status_t status;
 
-       *database_path = _xdg_dir (ctx, "XDG_DATA_HOME", ".local/share", profile);
+       *database_path = _xdg_dir (notmuch, "XDG_DATA_HOME", ".local/share", profile);
        status = _db_dir_exists (*database_path, message);
        if (status) {
            *database_path = NULL;
        } else {
-           *split = true;
+           notmuch->params |= NOTMUCH_PARAM_SPLIT;
        }
     }
 
@@ -227,7 +225,7 @@ _choose_database_path (void *ctx,
     if (! *database_path) {
        notmuch_status_t status;
 
-       *database_path = talloc_asprintf (ctx, "%s/mail", getenv ("HOME"));
+       *database_path = talloc_asprintf (notmuch, "%s/mail", getenv ("HOME"));
        status = _db_dir_exists (*database_path, message);
        if (status) {
            *database_path = NULL;
@@ -511,11 +509,9 @@ notmuch_database_open_with_config (const char *database_path,
                                   char **status_string)
 {
     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
-    void *local = talloc_new (NULL);
     notmuch_database_t *notmuch = NULL;
     char *message = NULL;
     GKeyFile *key_file = NULL;
-    bool split = false;
 
     _notmuch_init ();
 
@@ -531,8 +527,8 @@ notmuch_database_open_with_config (const char *database_path,
        goto DONE;
     }
 
-    if ((status = _choose_database_path (local, profile, key_file,
-                                        &database_path, &split,
+    if ((status = _choose_database_path (notmuch, profile, key_file,
+                                        &database_path,
                                         &message)))
        goto DONE;
 
@@ -550,8 +546,6 @@ notmuch_database_open_with_config (const char *database_path,
     status = _finish_open (notmuch, profile, mode, key_file, &message);
 
   DONE:
-    talloc_free (local);
-
     if (key_file)
        g_key_file_free (key_file);
 
@@ -613,9 +607,7 @@ notmuch_database_create_with_config (const char *database_path,
     const char *notmuch_path = NULL;
     char *message = NULL;
     GKeyFile *key_file = NULL;
-    void *local = talloc_new (NULL);
     int err;
-    bool split = false;
 
     _notmuch_init ();
 
@@ -631,8 +623,8 @@ notmuch_database_create_with_config (const char *database_path,
        goto DONE;
     }
 
-    if ((status = _choose_database_path (local, profile, key_file,
-                                        &database_path, &split, &message)))
+    if ((status = _choose_database_path (notmuch, profile, key_file,
+                                        &database_path, &message)))
        goto DONE;
 
     status = _db_dir_exists (database_path, &message);
@@ -641,37 +633,34 @@ notmuch_database_create_with_config (const char *database_path,
 
     _set_database_path (notmuch, database_path);
 
-    if (key_file && ! split) {
+    if (key_file && ! (notmuch->params & NOTMUCH_PARAM_SPLIT)) {
        char *mail_root = notmuch_canonicalize_file_name (
-           g_key_file_get_value (key_file, "database", "mail_root", NULL));
+           g_key_file_get_string (key_file, "database", "mail_root", NULL));
        char *db_path = notmuch_canonicalize_file_name (database_path);
 
-       split = (mail_root && (0 != strcmp (mail_root, db_path)));
+       if (mail_root && (0 != strcmp (mail_root, db_path)))
+           notmuch->params |= NOTMUCH_PARAM_SPLIT;
 
        free (mail_root);
        free (db_path);
     }
 
-    if (split) {
+    if (notmuch->params & NOTMUCH_PARAM_SPLIT) {
        notmuch_path = database_path;
     } else {
-       if (! (notmuch_path = talloc_asprintf (local, "%s/%s", database_path, ".notmuch"))) {
+       if (! (notmuch_path = talloc_asprintf (notmuch, "%s/%s", database_path, ".notmuch"))) {
            status = NOTMUCH_STATUS_OUT_OF_MEMORY;
            goto DONE;
        }
 
        err = mkdir (notmuch_path, 0755);
        if (err) {
-           if (errno == EEXIST) {
-               status = NOTMUCH_STATUS_DATABASE_EXISTS;
-               talloc_free (notmuch);
-               notmuch = NULL;
-           } else {
+           if (errno != EEXIST) {
                IGNORE_RESULT (asprintf (&message, "Error: Cannot create directory %s: %s.\n",
                                         notmuch_path, strerror (errno)));
                status = NOTMUCH_STATUS_FILE_ERROR;
+               goto DONE;
            }
-           goto DONE;
        }
     }
 
@@ -712,8 +701,6 @@ notmuch_database_create_with_config (const char *database_path,
     }
 
   DONE:
-    talloc_free (local);
-
     if (key_file)
        g_key_file_free (key_file);
 
@@ -813,11 +800,9 @@ notmuch_database_load_config (const char *database_path,
                              char **status_string)
 {
     notmuch_status_t status = NOTMUCH_STATUS_SUCCESS, warning = NOTMUCH_STATUS_SUCCESS;
-    void *local = talloc_new (NULL);
     notmuch_database_t *notmuch = NULL;
     char *message = NULL;
     GKeyFile *key_file = NULL;
-    bool split = false;
 
     _notmuch_init ();
 
@@ -839,8 +824,8 @@ notmuch_database_load_config (const char *database_path,
        goto DONE;
     }
 
-    status = _choose_database_path (local, profile, key_file,
-                                   &database_path, &split, &message);
+    status = _choose_database_path (notmuch, profile, key_file,
+                                   &database_path, &message);
     switch (status) {
     case NOTMUCH_STATUS_NO_DATABASE:
     case NOTMUCH_STATUS_SUCCESS:
@@ -875,8 +860,6 @@ notmuch_database_load_config (const char *database_path,
        goto DONE;
 
   DONE:
-    talloc_free (local);
-
     if (status_string)
        *status_string = message;
 
index 356c32ea254675749e4883743ba75f6fdd648121..08fd703708e86e6c75d960f19e2a404f46bcd20c 100644 (file)
@@ -32,6 +32,8 @@ typedef enum {
     SEXP_FLAG_EXPAND   = 1 << 6,
     SEXP_FLAG_DO_EXPAND = 1 << 7,
     SEXP_FLAG_ORPHAN   = 1 << 8,
+    SEXP_FLAG_RANGE    = 1 << 9,
+    SEXP_FLAG_PATHNAME = 1 << 10,
 } _sexp_flag_t;
 
 /*
@@ -66,16 +68,21 @@ static _sexp_prefix_t prefixes[] =
       SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_EXPAND },
     { "body",           Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
       SEXP_FLAG_FIELD },
+    { "date",           Xapian::Query::OP_INVALID,      Xapian::Query::MatchAll,
+      SEXP_FLAG_RANGE },
     { "from",           Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
       SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
     { "folder",         Xapian::Query::OP_OR,           Xapian::Query::MatchNothing,
-      SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
+      SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND |
+      SEXP_FLAG_PATHNAME },
     { "id",             Xapian::Query::OP_OR,           Xapian::Query::MatchNothing,
       SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
     { "infix",          Xapian::Query::OP_INVALID,      Xapian::Query::MatchAll,
       SEXP_FLAG_SINGLE | SEXP_FLAG_ORPHAN },
     { "is",             Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
       SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
+    { "lastmod",           Xapian::Query::OP_INVALID,      Xapian::Query::MatchAll,
+      SEXP_FLAG_RANGE },
     { "matching",       Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
       SEXP_FLAG_DO_EXPAND },
     { "mid",            Xapian::Query::OP_OR,           Xapian::Query::MatchNothing,
@@ -89,7 +96,8 @@ static _sexp_prefix_t prefixes[] =
     { "or",             Xapian::Query::OP_OR,           Xapian::Query::MatchNothing,
       SEXP_FLAG_NONE },
     { "path",           Xapian::Query::OP_OR,           Xapian::Query::MatchNothing,
-      SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
+      SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX |
+      SEXP_FLAG_PATHNAME },
     { "property",       Xapian::Query::OP_AND,          Xapian::Query::MatchAll,
       SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
     { "query",          Xapian::Query::OP_INVALID,      Xapian::Query::MatchNothing,
@@ -446,6 +454,79 @@ _sexp_expand_param (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
     return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
 }
 
+static notmuch_status_t
+_sexp_parse_range (notmuch_database_t *notmuch,  const _sexp_prefix_t *prefix,
+                  const sexp_t *sx, Xapian::Query &output)
+{
+    const char *from, *to;
+    std::string msg;
+
+    /* empty range matches everything */
+    if (! sx) {
+       output = Xapian::Query::MatchAll;
+       return NOTMUCH_STATUS_SUCCESS;
+    }
+
+    if (sx->ty == SEXP_LIST) {
+       _notmuch_database_log (notmuch, "expected atom as first argument of '%s'\n", prefix->name);
+       return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+    }
+
+    from = sx->val;
+    to = from;
+
+    if (sx->next) {
+       if (sx->next->ty == SEXP_LIST) {
+           _notmuch_database_log (notmuch, "expected atom as second argument of '%s'\n",
+                                  prefix->name);
+           return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+       }
+
+       if (sx->next->next) {
+           _notmuch_database_log (notmuch, "'%s' expects maximum of two arguments\n", prefix->name);
+           return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+       }
+
+       to = sx->next->val;
+    }
+
+    if (strcmp (prefix->name, "date") == 0) {
+       notmuch_status_t status;
+       status = _notmuch_date_strings_to_query (NOTMUCH_VALUE_TIMESTAMP, from, to, output, msg);
+       if (status) {
+           if (! msg.empty ())
+               _notmuch_database_log (notmuch, "%s\n", msg.c_str ());
+       }
+       return status;
+    }
+
+    if (strcmp (prefix->name, "lastmod") == 0) {
+       long from_idx, to_idx;
+
+       try {
+           from_idx = std::stol (from);
+       } catch (std::logic_error &e) {
+           _notmuch_database_log (notmuch, "bad 'from' revision: '%s'\n", from);
+           return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+       }
+
+       try {
+           to_idx = std::stol (to);
+       } catch (std::logic_error &e) {
+           _notmuch_database_log (notmuch, "bad 'to' revision: '%s'\n", to);
+           return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+       }
+
+       output = Xapian::Query (Xapian::Query::OP_VALUE_RANGE, NOTMUCH_VALUE_LAST_MOD,
+                               Xapian::sortable_serialise (from_idx),
+                               Xapian::sortable_serialise (to_idx));
+       return NOTMUCH_STATUS_SUCCESS;
+    }
+
+    _notmuch_database_log (notmuch, "unimplimented range prefix: '%s'\n", prefix->name);
+    return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+}
+
 /* Here we expect the s-expression to be a proper list, with first
  * element defining and operation, or as a special case the empty
  * list */
@@ -467,8 +548,13 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
            return _sexp_parse_wildcard (notmuch, parent, env, "", output);
        }
 
+       char *atom = sx->val;
+
+       if (parent && parent->flags & SEXP_FLAG_PATHNAME)
+           strip_trailing (atom, '/');
+
        if (parent && (parent->flags & SEXP_FLAG_BOOLEAN)) {
-           output = Xapian::Query (term_prefix + sx->val);
+           output = Xapian::Query (term_prefix + atom);
            return NOTMUCH_STATUS_SUCCESS;
        }
 
@@ -519,7 +605,7 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
 
     for (_sexp_prefix_t *prefix = prefixes; prefix && prefix->name; prefix++) {
        if (strcmp (prefix->name, sx->list->val) == 0) {
-           if (prefix->flags & SEXP_FLAG_FIELD) {
+           if (prefix->flags & (SEXP_FLAG_FIELD | SEXP_FLAG_RANGE)) {
                if (parent) {
                    _notmuch_database_log (notmuch, "nested field: '%s' inside '%s'\n",
                                           prefix->name, parent->name);
@@ -541,6 +627,9 @@ _sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent
                return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
            }
 
+           if (prefix->flags & SEXP_FLAG_RANGE)
+               return _sexp_parse_range (notmuch, prefix, sx->list->next, output);
+
            if (strcmp (prefix->name, "infix") == 0) {
                return _sexp_parse_infix (notmuch, sx->list->next, output);
            }
index 22bf2ab5336980f8cbd9660e3ae4eb58bd7a9b97..6b07970b7960254b240bb68312f61200fa976336 100644 (file)
 #include "parse-time-vrp.h"
 #include "parse-time-string.h"
 
-Xapian::Query
-ParseTimeRangeProcessor::operator() (const std::string &begin, const std::string &end)
+notmuch_status_t
+_notmuch_date_strings_to_query (Xapian::valueno slot,
+                               const std::string &begin, const std::string &end,
+                               Xapian::Query &output, std::string &msg)
 {
     double from = DBL_MIN, to = DBL_MAX;
     time_t parsed_time, now;
     std::string str;
 
     /* Use the same 'now' for begin and end. */
-    if (time (&now) == (time_t) -1)
-       throw Xapian::QueryParserError ("unable to get current time");
+    if (time (&now) == (time_t) -1) {
+       msg = "unable to get current time";
+       return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
+    }
 
     if (! begin.empty ()) {
-       if (parse_time_string (begin.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_DOWN))
-           throw Xapian::QueryParserError ("Didn't understand date specification '" + begin + "'");
-       else
-           from = (double) parsed_time;
+       if (parse_time_string (begin.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_DOWN)) {
+           msg = "Didn't understand date specification '" + begin + "'";
+           return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+       }
+
+       from = (double) parsed_time;
     }
 
     if (! end.empty ()) {
@@ -48,15 +54,30 @@ ParseTimeRangeProcessor::operator() (const std::string &begin, const std::string
        else
            str = end;
 
-       if (parse_time_string (str.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_UP_INCLUSIVE))
-           throw Xapian::QueryParserError ("Didn't understand date specification '" + str + "'");
-       else
-           to = (double) parsed_time;
+       if (parse_time_string (str.c_str (), &parsed_time, &now, PARSE_TIME_ROUND_UP_INCLUSIVE)) {
+           msg = "Didn't understand date specification '" + str + "'";
+           return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+       }
+       to = (double) parsed_time;
     }
 
-    return Xapian::Query (Xapian::Query::OP_VALUE_RANGE, slot,
-                         Xapian::sortable_serialise (from),
-                         Xapian::sortable_serialise (to));
+    output = Xapian::Query (Xapian::Query::OP_VALUE_RANGE, slot,
+                           Xapian::sortable_serialise (from),
+                           Xapian::sortable_serialise (to));
+    return NOTMUCH_STATUS_SUCCESS;
+}
+
+Xapian::Query
+ParseTimeRangeProcessor::operator() (const std::string &begin, const std::string &end)
+{
+
+    Xapian::Query output;
+    std::string msg;
+
+    if (_notmuch_date_strings_to_query (slot, begin, end, output, msg))
+       throw Xapian::QueryParserError (msg);
+
+    return output;
 }
 
 /* XXX TODO: is throwing an exception the right thing to do here? */
index 0d92bdd7ddaecfc75801afd2f6f41bf1fac8621a..857c05b9b4d49a9659f7cef3c721521f5c92503b 100644 (file)
@@ -46,7 +46,7 @@ prefix_t prefix_table[] = {
     { "mid",                    "Q",            NOTMUCH_FIELD_EXTERNAL |
       NOTMUCH_FIELD_PROCESSOR },
     { "path",                   "P",            NOTMUCH_FIELD_EXTERNAL |
-      NOTMUCH_FIELD_PROCESSOR },
+      NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
     { "property",               "XPROPERTY",    NOTMUCH_FIELD_EXTERNAL },
     /*
      * Unconditionally add ':' to reduce potential ambiguity with
@@ -55,7 +55,7 @@ prefix_t prefix_table[] = {
      * discussion.
      */
     { "folder",                 "XFOLDER:",     NOTMUCH_FIELD_EXTERNAL |
-      NOTMUCH_FIELD_PROCESSOR },
+      NOTMUCH_FIELD_PROCESSOR | NOTMUCH_FIELD_STRIP_TRAILING_SLASH },
     { "date",                   NULL,           NOTMUCH_FIELD_EXTERNAL |
       NOTMUCH_FIELD_PROCESSOR },
     { "query",                  NULL,           NOTMUCH_FIELD_EXTERNAL |
index c6d9d94f7686063bb59f76fbd4daf0a19ae81500..7e9d959c9b0bc15ffc5b10a9754246750384e668 100644 (file)
@@ -235,7 +235,15 @@ RegexpFieldProcessor::operator() (const std::string & str)
            return parser.parse_query (query_str, NOTMUCH_QUERY_PARSER_FLAGS, term_prefix);
        } else {
            /* Boolean prefix */
-           std::string term = term_prefix + str;
+           std::string query_str;
+           std::string term;
+
+           if (str.length () > 1 && str.at (str.size () - 1) == '/')
+               query_str = str.substr (0, str.size () - 1);
+           else
+               query_str = str;
+
+           term = term_prefix + query_str;
            return Xapian::Query (term);
        }
     }
index 96d81166d2f52342b721f7be293eb2641ea4e868..9f57ac5e3935c430fb983645860780a8a937986b 100644 (file)
@@ -65,7 +65,7 @@ struct sprinter;
 struct notmuch_show_params;
 
 typedef struct notmuch_show_format {
-    struct sprinter *(*new_sprinter)(const void *ctx, FILE *stream);
+    struct sprinter *(*new_sprinter)(notmuch_database_t * db, FILE *stream);
     notmuch_status_t (*part)(const void *ctx, struct sprinter *sprinter,
                             struct mime_node *node, int indent,
                             const struct notmuch_show_params *params);
@@ -426,13 +426,13 @@ mime_node_seek_dfs (mime_node_t *node, int n);
 const _notmuch_message_crypto_t *
 mime_node_get_message_crypto_status (mime_node_t *node);
 
-typedef enum dump_formats {
+typedef enum {
     DUMP_FORMAT_AUTO,
     DUMP_FORMAT_BATCH_TAG,
     DUMP_FORMAT_SUP
 } dump_format_t;
 
-typedef enum dump_includes {
+typedef enum {
     DUMP_INCLUDE_TAGS          = 1,
     DUMP_INCLUDE_CONFIG                = 2,
     DUMP_INCLUDE_PROPERTIES    = 4
@@ -499,11 +499,10 @@ int notmuch_minimal_options (const char *subcommand_name,
 struct _notmuch_client_indexing_cli_choices {
     int decrypt_policy;
     bool decrypt_policy_set;
-    notmuch_indexopts_t *opts;
 };
 extern struct _notmuch_client_indexing_cli_choices indexing_cli_choices;
 extern const notmuch_opt_desc_t notmuch_shared_indexing_options [];
 notmuch_status_t
-notmuch_process_shared_indexing_options (notmuch_database_t *notmuch);
+notmuch_process_shared_indexing_options (notmuch_indexopts_t *opts);
 
 #endif
index db00a26cd4327ce87f148f49bca9994450470656..e9456d794456c080e81abbd6a00efcb6b123c3c4 100644 (file)
@@ -383,7 +383,10 @@ _config_set_list (notmuch_conffile_t *config,
                  const char *list[],
                  size_t length)
 {
-    g_key_file_set_string_list (config->key_file, group, key, list, length);
+    if (length > 1)
+       g_key_file_set_string_list (config->key_file, group, key, list, length);
+    else
+       g_key_file_set_string (config->key_file, group, key, list[0]);
 }
 
 void
@@ -680,9 +683,9 @@ _notmuch_config_list_built_with ()
     printf ("%sretry_lock=%s\n",
            BUILT_WITH_PREFIX,
            notmuch_built_with ("retry_lock") ? "true" : "false");
-    printf ("%ssexpr_query=%s\n",
+    printf ("%ssexp_queries=%s\n",
            BUILT_WITH_PREFIX,
-           notmuch_built_with ("sexpr_query") ? "true" : "false");
+           notmuch_built_with ("sexp_queries") ? "true" : "false");
 }
 
 static int
index 72e2e35fc4a828d209186b70f5aaf4d51d912f54..214d4d03a0810d6845d845288455eb971441574f 100644 (file)
@@ -461,6 +461,8 @@ notmuch_insert_command (notmuch_database_t *notmuch, int argc, char *argv[])
     char *maildir;
     char *newpath;
     int opt_index;
+    notmuch_indexopts_t *indexopts = notmuch_database_get_default_indexopts (notmuch);
+
     void *local = talloc_new (NULL);
 
     notmuch_opt_desc_t options[] = {
@@ -550,7 +552,7 @@ notmuch_insert_command (notmuch_database_t *notmuch, int argc, char *argv[])
        return EXIT_FAILURE;
     }
 
-    status = notmuch_process_shared_indexing_options (notmuch);
+    status = notmuch_process_shared_indexing_options (indexopts);
     if (status != NOTMUCH_STATUS_SUCCESS) {
        fprintf (stderr, "Error: Failed to process index options. (%s)\n",
                 notmuch_status_to_string (status));
@@ -558,7 +560,7 @@ notmuch_insert_command (notmuch_database_t *notmuch, int argc, char *argv[])
     }
 
     /* Index the message. */
-    status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, indexing_cli_choices.opts);
+    status = add_file (notmuch, newpath, tag_ops, synchronize_flags, keep, indexopts);
 
     /* Commit changes. */
     close_status = notmuch_database_close (notmuch);
index b7a5f2eabcfc5cc736ee4601807060c73d3db088..346e64697aaf02763a38eb09e93ef428348aae4a 100644 (file)
@@ -45,6 +45,7 @@ typedef struct {
     const char *db_path;
     const char *mail_root;
 
+    notmuch_indexopts_t *indexopts;
     int output_is_a_tty;
     enum verbosity verbosity;
     bool debug;
@@ -376,7 +377,7 @@ add_file (notmuch_database_t *notmuch, const char *filename,
     if (status)
        goto DONE;
 
-    status = notmuch_database_index_file (notmuch, filename, indexing_cli_choices.opts, &message);
+    status = notmuch_database_index_file (notmuch, filename, state->indexopts, &message);
     switch (status) {
     /* Success. */
     case NOTMUCH_STATUS_SUCCESS:
@@ -600,11 +601,12 @@ add_files (notmuch_database_t *notmuch,
            continue;
        }
 
-       /* Ignore the .notmuch directory and any "tmp" directory
+       /* Ignore any top level .notmuch directory and any "tmp" directory
         * that appears within a maildir.
         */
        if ((is_maildir && strcmp (entry->d_name, "tmp") == 0) ||
-           strcmp (entry->d_name, ".notmuch") == 0)
+           (strcmp (entry->d_name, ".notmuch") == 0
+            && (strcmp (path, state->mail_root)) == 0))
            continue;
 
        next = talloc_asprintf (notmuch, "%s/%s", path, entry->d_name);
@@ -1150,6 +1152,8 @@ notmuch_new_command (notmuch_database_t *notmuch, int argc, char *argv[])
     else if (verbose)
        add_files_state.verbosity = VERBOSITY_VERBOSE;
 
+    add_files_state.indexopts = notmuch_database_get_default_indexopts (notmuch);
+
     add_files_state.new_tags = notmuch_config_get_values (notmuch, NOTMUCH_CONFIG_NEW_TAGS);
 
     if (print_status_database (
@@ -1217,7 +1221,7 @@ notmuch_new_command (notmuch_database_t *notmuch, int argc, char *argv[])
     if (notmuch == NULL)
        return EXIT_FAILURE;
 
-    status = notmuch_process_shared_indexing_options (notmuch);
+    status = notmuch_process_shared_indexing_options (add_files_state.indexopts);
     if (status != NOTMUCH_STATUS_SUCCESS) {
        fprintf (stderr, "Error: Failed to process index options. (%s)\n",
                 notmuch_status_to_string (status));
index 49eacd47d64196e525d2a7c208c14ee9a6c855de..e9a6545688034993b822c22c27e13f3cb9063515 100644 (file)
@@ -90,6 +90,7 @@ notmuch_reindex_command (notmuch_database_t *notmuch, int argc, char *argv[])
     int opt_index;
     int ret;
     notmuch_status_t status;
+    notmuch_indexopts_t *indexopts = notmuch_database_get_default_indexopts (notmuch);
 
     /* Set up our handler for SIGINT */
     memset (&action, 0, sizeof (struct sigaction));
@@ -110,7 +111,7 @@ notmuch_reindex_command (notmuch_database_t *notmuch, int argc, char *argv[])
 
     notmuch_process_shared_options (notmuch, argv[0]);
 
-    status = notmuch_process_shared_indexing_options (notmuch);
+    status = notmuch_process_shared_indexing_options (indexopts);
     if (status != NOTMUCH_STATUS_SUCCESS) {
        fprintf (stderr, "Error: Failed to process index options. (%s)\n",
                 notmuch_status_to_string (status));
@@ -128,7 +129,7 @@ notmuch_reindex_command (notmuch_database_t *notmuch, int argc, char *argv[])
        return EXIT_FAILURE;
     }
 
-    ret = reindex_query (notmuch, query_string, indexing_cli_choices.opts);
+    ret = reindex_query (notmuch, query_string, indexopts);
 
     notmuch_database_destroy (notmuch);
 
index 2848c9c39372fcbad778231c625632119b8e4faa..6a54d9c16a0684b28bbc9e98e2c62bed340ba03e 100644 (file)
@@ -209,6 +209,30 @@ _is_from_line (const char *line)
        return 0;
 }
 
+/* Output extra headers if configured with the `show.extra_headers'
+ * configuration option
+ */
+static void
+format_extra_headers_sprinter (sprinter_t *sp, GMimeMessage *message)
+{
+    GMimeHeaderList *header_list = g_mime_object_get_header_list (GMIME_OBJECT (message));
+
+    for (notmuch_config_values_t *extra_headers = notmuch_config_get_values (
+            sp->notmuch, NOTMUCH_CONFIG_EXTRA_HEADERS);
+        notmuch_config_values_valid (extra_headers);
+        notmuch_config_values_move_to_next (extra_headers)) {
+       GMimeHeader *header;
+       const char *header_name = notmuch_config_values_get (extra_headers);
+
+       header = g_mime_header_list_get_header (header_list, header_name);
+       if (header == NULL)
+           continue;
+
+       sp->map_key (sp, g_mime_header_get_name (header));
+       sp->string (sp, g_mime_header_get_value (header));
+    }
+}
+
 void
 format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
                         bool reply, const _notmuch_message_crypto_t *msg_crypto)
@@ -269,6 +293,9 @@ format_headers_sprinter (sprinter_t *sp, GMimeMessage *message,
        sp->string (sp, g_mime_message_get_date_string (sp, message));
     }
 
+    /* Output extra headers the user has configured, if any */
+    if (! reply)
+       format_extra_headers_sprinter (sp, message);
     sp->end (sp);
     talloc_free (local);
 }
index 3fb58bf2e822f606ca9a38e8cbc6aa22422bfd10..ac25ae189b78d049d65b70afc09e3dee031f4312 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -141,21 +141,18 @@ const notmuch_opt_desc_t notmuch_shared_indexing_options [] = {
 
 
 notmuch_status_t
-notmuch_process_shared_indexing_options (notmuch_database_t *notmuch)
+notmuch_process_shared_indexing_options (notmuch_indexopts_t *opts)
 {
-    if (indexing_cli_choices.opts == NULL)
-       indexing_cli_choices.opts = notmuch_database_get_default_indexopts (notmuch);
+    if (opts == NULL)
+       return NOTMUCH_STATUS_NULL_POINTER;
+
     if (indexing_cli_choices.decrypt_policy_set) {
        notmuch_status_t status;
-       if (indexing_cli_choices.opts == NULL)
-           return NOTMUCH_STATUS_OUT_OF_MEMORY;
-       status = notmuch_indexopts_set_decrypt_policy (indexing_cli_choices.opts,
+       status = notmuch_indexopts_set_decrypt_policy (opts,
                                                       indexing_cli_choices.decrypt_policy);
        if (status != NOTMUCH_STATUS_SUCCESS) {
            fprintf (stderr, "Error: Failed to set index decryption policy to %d. (%s)\n",
                     indexing_cli_choices.decrypt_policy, notmuch_status_to_string (status));
-           notmuch_indexopts_destroy (indexing_cli_choices.opts);
-           indexing_cli_choices.opts = NULL;
            return status;
        }
     }
index c7f4851cdd53bc6231d7a56e1d19e80834a09536..502f89fbb70ef8424543009278dc9d8c23b17f74 100644 (file)
@@ -172,7 +172,7 @@ json_separator (struct sprinter *sp)
 }
 
 struct sprinter *
-sprinter_json_create (const void *ctx, FILE *stream)
+sprinter_json_create (notmuch_database_t *db, FILE *stream)
 {
     static const struct sprinter_json template = {
        .vtable = {
@@ -192,11 +192,12 @@ sprinter_json_create (const void *ctx, FILE *stream)
     };
     struct sprinter_json *res;
 
-    res = talloc (ctx, struct sprinter_json);
+    res = talloc (db, struct sprinter_json);
     if (! res)
        return NULL;
 
     *res = template;
+    res->vtable.notmuch = db;
     res->stream = stream;
     return &res->vtable;
 }
index 63b254284b4a6d73861896587b12bc2a6281144f..e37cb1f97e30cc48366fdf7fc4ae0c00fe6efa0d 100644 (file)
@@ -207,7 +207,7 @@ sexp_separator (struct sprinter *sp)
 }
 
 struct sprinter *
-sprinter_sexp_create (const void *ctx, FILE *stream)
+sprinter_sexp_create (notmuch_database_t *db, FILE *stream)
 {
     static const struct sprinter_sexp template = {
        .vtable = {
@@ -227,11 +227,12 @@ sprinter_sexp_create (const void *ctx, FILE *stream)
     };
     struct sprinter_sexp *res;
 
-    res = talloc (ctx, struct sprinter_sexp);
+    res = talloc (db, struct sprinter_sexp);
     if (! res)
        return NULL;
 
     *res = template;
+    res->vtable.notmuch = db;
     res->stream = stream;
     return &res->vtable;
 }
index c75ec5be1cbc5632f56332b40563c87959367547..99330a94768605ebbd9977ae2563b90c7cda4e15 100644 (file)
@@ -114,7 +114,7 @@ text_map_key (unused (struct sprinter *sp), unused (const char *key))
 }
 
 struct sprinter *
-sprinter_text_create (const void *ctx, FILE *stream)
+sprinter_text_create (notmuch_database_t *db, FILE *stream)
 {
     static const struct sprinter_text template = {
        .vtable = {
@@ -134,21 +134,22 @@ sprinter_text_create (const void *ctx, FILE *stream)
     };
     struct sprinter_text *res;
 
-    res = talloc (ctx, struct sprinter_text);
+    res = talloc (db, struct sprinter_text);
     if (! res)
        return NULL;
 
     *res = template;
+    res->vtable.notmuch = db;
     res->stream = stream;
     return &res->vtable;
 }
 
 struct sprinter *
-sprinter_text0_create (const void *ctx, FILE *stream)
+sprinter_text0_create (notmuch_database_t *db, FILE *stream)
 {
     struct sprinter *sp;
 
-    sp = sprinter_text_create (ctx, stream);
+    sp = sprinter_text_create (db, stream);
     if (! sp)
        return NULL;
 
index 528d8a2db332bdaea6801424a15f7ca42db673ec..fd08641cf0a4aa792ab969524fe3560e01998648 100644 (file)
@@ -9,6 +9,11 @@
  * (strings, integers and booleans).
  */
 typedef struct sprinter {
+    /*
+     * Open notmuch database
+     */
+    notmuch_database_t *notmuch;
+
     /* Start a new map/dictionary structure. This should be followed by
      * a sequence of alternating calls to map_key and one of the
      * value-printing functions until the map is ended by end.
@@ -65,20 +70,20 @@ typedef struct sprinter {
 /* Create a new unstructured printer that emits the default text format
  * for "notmuch search". */
 struct sprinter *
-sprinter_text_create (const void *ctx, FILE *stream);
+sprinter_text_create (notmuch_database_t *db, FILE *stream);
 
 /* Create a new unstructured printer that emits the text format for
  * "notmuch search", with each field separated by a null character
  * instead of the newline character. */
 struct sprinter *
-sprinter_text0_create (const void *ctx, FILE *stream);
+sprinter_text0_create (notmuch_database_t *db, FILE *stream);
 
 /* Create a new structure printer that emits JSON. */
 struct sprinter *
-sprinter_json_create (const void *ctx, FILE *stream);
+sprinter_json_create (notmuch_database_t *db, FILE *stream);
 
 /* Create a new structure printer that emits S-Expressions. */
 struct sprinter *
-sprinter_sexp_create (const void *ctx, FILE *stream);
+sprinter_sexp_create (notmuch_database_t *db, FILE *stream);
 
 #endif // NOTMUCH_SPRINTER_H
index 3a585d1b872db1b5694e3ec18527cc56ba389b28..43bbce31057d4fc108621c505057618d5b0d9990 100755 (executable)
@@ -51,7 +51,7 @@ cat <<EOF > EXPECTED
 built_with.compact=something
 built_with.field_processor=something
 built_with.retry_lock=something
-built_with.sexpr_query=something
+built_with.sexp_queries=something
 database.autocommit=8000
 database.mail_root=MAIL_DIR
 database.path=MAIL_DIR
@@ -67,6 +67,35 @@ user.primary_email=test_suite@notmuchmail.org
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "Round trip config item with leading spaces"
+test_subtest_known_broken
+notmuch config set foo.bar "  thing"
+output=$(notmuch config get foo.bar)
+test_expect_equal "${output}" "  thing"
+
+test_begin_subtest "Round trip config item with leading tab"
+test_subtest_known_broken
+notmuch config set foo.bar "   thing"
+output=$(notmuch config get foo.bar)
+test_expect_equal "${output}" "        thing"
+
+test_begin_subtest "Round trip config item with embedded tab"
+notmuch config set foo.bar "thing      other"
+output=$(notmuch config get foo.bar)
+test_expect_equal "${output}" "thing   other"
+
+test_begin_subtest "Round trip config item with embedded backslash"
+notmuch config set foo.bar 'thing\other'
+output=$(notmuch config get foo.bar)
+test_expect_equal "${output}" "thing\other"
+
+test_begin_subtest "Round trip config item with embedded NL/CR"
+notmuch config set foo.bar 'thing
+\rother'
+output=$(notmuch config get foo.bar)
+test_expect_equal "${output}" "thing
+\rother"
+
 test_begin_subtest "Top level --config=FILE option"
 cp "${NOTMUCH_CONFIG}" alt-config
 notmuch --config=alt-config config set user.name "Another Name"
index 42c621c87b26c216c8172982f939c2b12dc0dacd..10b29ec33f3a726f261feb5df98d58789643cf3e 100755 (executable)
@@ -23,6 +23,13 @@ EOF
 expected_dir=$NOTMUCH_SRCDIR/test/setup.expected-output
 test_expect_equal_file ${expected_dir}/config-with-comments new-notmuch-config
 
+test_begin_subtest "setup consistent with config-set for single items"
+# note this relies on the config state from the previous test.
+notmuch --config=new-notmuch-config config list > list.setup
+notmuch --config=new-notmuch-config config set search.exclude_tags baz
+notmuch --config=new-notmuch-config config list > list.config
+test_expect_equal_file list.setup list.config
+
 test_begin_subtest "notmuch with a config but without a database suggests notmuch new"
 notmuch 2>&1 | notmuch_dir_sanitize > OUTPUT
 cat <<EOF > EXPECTED
index 1141c1e3b7da84f6a0e9df20650fa97ddfc8430c..6791f87c5c62914b48f36b7804d90316db9807fe 100755 (executable)
@@ -329,6 +329,18 @@ notmuch config set new.tags "foo;;bar"
 output=$(NOTMUCH_NEW --quiet 2>&1)
 test_expect_equal "$output" ""
 
+test_begin_subtest "leading/trailing whitespace in new.tags is ignored"
+# avoid complications with leading spaces and "notmuch config"
+sed -i 's/^tags=.*$/tags= fu bar ; ; bar /' notmuch-config
+add_message
+NOTMUCH_NEW --quiet
+notmuch dump id:$gen_msg_id | sed 's/ --.*$//' > OUTPUT
+cat <<EOF >EXPECTED
+#notmuch-dump batch-tag:3 config,properties,tags
++bar +fu%20bar
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "Tags starting with '-' in new.tags are forbidden"
 notmuch config set new.tags "-foo;bar"
 output=$(NOTMUCH_NEW --debug 2>&1)
@@ -339,6 +351,16 @@ test_expect_code 1 "NOTMUCH_NEW --debug 2>&1"
 
 notmuch config set new.tags $OLDCONFIG
 
+test_begin_subtest ".notmuch only ignored at top level"
+generate_message '[dir]=foo/bar/.notmuch/cur' '[subject]="Do not ignore, very important"'
+NOTMUCH_NEW > OUTPUT
+notmuch search subject:Do-not-ignore | notmuch_search_sanitize >> OUTPUT
+cat <<EOF > EXPECTED
+Added 1 new message to the database.
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Do not ignore, very important (inbox unread)
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "RFC822 group names are indexed"
 test_subtest_known_broken
 generate_message [to]="undisclosed-recipients:"
@@ -368,31 +390,26 @@ chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.*
 test_expect_equal "$output" "A Xapian exception occurred opening database"
 
 
-test_begin_subtest "Handle files vanishing between scandir and add_file"
+make_shim dif-shim<<EOF
+#include <notmuch-test.h>
 
-# A file for scandir to find. It won't get indexed, so can be empty.
-touch ${MAIL_DIR}/vanish
+WRAP_DLFUNC(notmuch_status_t, notmuch_database_index_file, \
+ (notmuch_database_t *database, const char *filename, notmuch_indexopts_t *indexopts, notmuch_message_t **message))
 
-# Breakpoint to remove the file before indexing
-cat <<EOF > notmuch-new-vanish.gdb
-set breakpoint pending on
-set logging file notmuch-new-vanish-gdb.log
-set logging on
-break notmuch_database_index_file
-commands
-shell rm -f ${MAIL_DIR}/vanish
-continue
-end
-run
+  if (unlink ("${MAIL_DIR}/vanish")) {
+     fprintf (stderr, "unlink failed\n");
+     exit (42);
+  }
+  return notmuch_database_index_file_orig (database, filename, indexopts, message);
+}
 EOF
 
-${TEST_GDB} --batch-silent --return-child-result -x notmuch-new-vanish.gdb \
-    --args notmuch new 2>OUTPUT 1>/dev/null
-echo "exit status: $?" >> OUTPUT
-
-# Clean up the file in case gdb isn't available.
-rm -f ${MAIL_DIR}/vanish
+test_begin_subtest "Handle files vanishing between scandir and add_file"
 
+# A file for scandir to find. It won't get indexed, so can be empty.
+touch ${MAIL_DIR}/vanish
+notmuch_with_shim dif-shim new 2>OUTPUT 1>/dev/null
+echo "exit status: $?" >> OUTPUT
 cat <<EOF > EXPECTED
 Unexpected error with file ${MAIL_DIR}/vanish
 add_file: Something went wrong trying to read or write a file
diff --git a/test/T051-new-renames.sh b/test/T051-new-renames.sh
new file mode 100755 (executable)
index 0000000..ebd06be
--- /dev/null
@@ -0,0 +1,40 @@
+#!/usr/bin/env bash
+test_description='"notmuch new" with directory renames'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+for loop in {1..10}; do
+
+rm -rf ${MAIL_DIR}
+
+for i in {1..10}; do
+    generate_message '[dir]=foo' '[subject]="Message foo $i"'
+done
+
+for i in {1..10}; do
+    generate_message '[dir]=bar' '[subject]="Message bar $i"'
+done
+
+test_begin_subtest "Index the messages, round $loop"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 20 new messages to the database."
+
+all_files=$(notmuch search --output=files \*)
+count_foo=$(notmuch count folder:foo)
+
+test_begin_subtest "Rename folder"
+mv ${MAIL_DIR}/foo ${MAIL_DIR}/baz
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
+
+test_begin_subtest "Rename folder back"
+mv ${MAIL_DIR}/baz ${MAIL_DIR}/foo
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected $count_foo file renames."
+
+test_begin_subtest "Files remain the same"
+output=$(notmuch search --output=files \*)
+test_expect_equal "$output" "$all_files"
+
+done
+
+test_done
index 6d9fb40291bf2e1692ade64599f748d1bd8fe5fd..1df240dd0cec2665c17faebc2b833cf4ccac73c3 100755 (executable)
@@ -277,7 +277,7 @@ EOF
 built_with.compact=something
 built_with.field_processor=something
 built_with.retry_lock=something
-built_with.sexpr_query=something
+built_with.sexp_queries=something
 database.autocommit=8000
 database.backup_dir
 database.hook_dir
@@ -318,7 +318,14 @@ to=m.header('To')
 print(to)
 EOF
           test_expect_equal_file EXPECTED OUTPUT
-          ;& # fall through
+          ;;
+       *)
+          backup_database
+          test_begin_subtest ".notmuch without xapian/ handled gracefully ($config)"
+          rm -r $XAPIAN_PATH
+          test_expect_success "notmuch new"
+          restore_database
+          ;;
    esac
 
    case $config in
index 6ad80df99724ba9293a6853ecae35004f7cd66eb..4814670681165322d4769487991ef4f12542ea64 100755 (executable)
@@ -102,22 +102,25 @@ output=$(sed 's/^\(A Xapian exception [^:]*\):.*$/\1/' OUTPUT)
 test_expect_equal "${output}" "A Xapian exception occurred opening database"
 restore_database
 
-cat <<EOF > count-files.gdb
-set breakpoint pending on
-set logging file count-files-gdb.log
-set logging on
-break count_files
-commands
-shell cp /dev/null ${MAIL_DIR}/.notmuch/xapian/postlist.*
-continue
-end
-run
+make_shim qsm-shim<<EOF
+#include <notmuch-test.h>
+
+WRAP_DLFUNC (notmuch_status_t, notmuch_query_search_messages, (notmuch_query_t *query, notmuch_messages_t **messages))
+
+  /* XXX WARNING THIS CORRUPTS THE DATABASE */
+  int fd = open ("target_postlist", O_WRONLY|O_TRUNC);
+  if (fd < 0)
+    exit (8);
+  close (fd);
+
+  return notmuch_query_search_messages_orig(query, messages);
+}
 EOF
 
 backup_database
 test_begin_subtest "error message from query_search_messages"
-${TEST_GDB} --batch-silent --return-child-result -x count-files.gdb \
-    --args notmuch count --output=files '*' 2>OUTPUT 1>/dev/null
+ln -s ${MAIL_DIR}/.notmuch/xapian/postlist.* target_postlist
+notmuch_with_shim qsm-shim count --output=files '*' 2>OUTPUT 1>/dev/null
 cat <<EOF > EXPECTED
 notmuch count: A Xapian exception occurred
 A Xapian exception occurred performing query
index 208deb1c210c68b4cbfaa0586c6320da94c7c20f..ec170b30a42e18ce8fbc6866ee06bbbec7dc302b 100755 (executable)
@@ -234,6 +234,18 @@ output=$(notmuch show --format=json id:$gen_msg_id)
 test_json_nodes <<<"$output" \
                'new_tags:[0][0][0]["tags"] = ["bar", "foo"]'
 
+test_begin_subtest "leading/trailing whitespace in new.tags is ignored"
+# avoid complications with leading spaces and "notmuch config"
+sed -i 's/^tags=.*$/tags= fu bar ; ; bar /' notmuch-config
+gen_insert_msg
+notmuch insert < $gen_msg_filename
+notmuch dump id:$gen_msg_id | sed 's/ --.*$//' > OUTPUT
+cat <<EOF >EXPECTED
+#notmuch-dump batch-tag:3 config,properties,tags
++bar +fu%20bar
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "Tags starting with '-' in new.tags are forbidden"
 notmuch config set new.tags "-foo;bar"
 gen_insert_msg
index 2a8ad5f1a1bb37d6cf39c780690e2a59f6a94aa5..e2936cd74f297bbc2b11b15ba41571434266553e 100755 (executable)
@@ -185,6 +185,50 @@ notmuch search folder:'""' > EXPECTED
 notmuch search --query=sexp '(folder "")'  > OUTPUT
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "Search by 'folder' with --output=files"
+output=$(notmuch search --output=files --query=sexp '(folder bad/news)' | notmuch_search_files_sanitize)
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-XXX
+MAIL_DIR/duplicate/bad/news/msg-XXX"
+
+test_begin_subtest "Search by 'folder' with --output=files (trailing /)"
+output=$(notmuch search --output=files --query=sexp '(folder bad/news/)' | notmuch_search_files_sanitize)
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-XXX
+MAIL_DIR/duplicate/bad/news/msg-XXX"
+
+test_begin_subtest "Search by 'folder' (multiple)"
+output=$(notmuch search --query=sexp '(folder bad bad/news things/bad)' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
+thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
+
+test_begin_subtest "Search by 'folder' (multiple, trailing /)"
+output=$(notmuch search --query=sexp '(folder bad bad/news/ things/bad)' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
+thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
+
+test_begin_subtest "Search by 'path' with --output=files"
+output=$(notmuch search --output=files --query=sexp '(path bad/news)' | notmuch_search_files_sanitize)
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-XXX
+MAIL_DIR/duplicate/bad/news/msg-XXX"
+
+test_begin_subtest "Search by 'path' with --output=files (trailing /)"
+output=$(notmuch search --output=files --query=sexp '(path bad/news/)' | notmuch_search_files_sanitize)
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-XXX
+MAIL_DIR/duplicate/bad/news/msg-XXX"
+
+test_begin_subtest "Search by 'path' specification (multiple)"
+output=$(notmuch search --query=sexp '(path bad bad/news things/bad)' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
+thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
+
+test_begin_subtest "Search by 'path' specification (multiple, trailing /)"
+output=$(notmuch search --query=sexp '(path bad bad/news/ things/bad)' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
+thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
+
 test_begin_subtest "Search by 'id'"
 add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
 output=$(notmuch search --query=sexp "(id ${gen_msg_id})" | notmuch_search_sanitize)
@@ -346,7 +390,7 @@ output=$(notmuch search --query=sexp '(attachment (starts-with not))' | notmuch_
 test_expect_equal "$output" 'thread:XXX   2009-11-18 [2/2] Lars Kellogg-Stedman; [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)'
 
 test_begin_subtest "starts-with, folder"
-notmuch search --output=files --query=sexp '(folder (starts-with bad))' | notmuch_dir_sanitize | sed 's/[0-9]*$/XXX/' > OUTPUT
+notmuch search --output=files --query=sexp '(folder (starts-with bad))' | notmuch_search_files_sanitize > OUTPUT
 cat <<EOF > EXPECTED
 MAIL_DIR/bad/msg-XXX
 MAIL_DIR/bad/news/msg-XXX
@@ -768,6 +812,144 @@ notmuch search date:2009-11-18..2009-11-18 and tag:unread > EXPECTED
 notmuch search --query=sexp  '(and (infix "date:2009-11-18..2009-11-18") (infix "tag:unread"))' > OUTPUT
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "date query, empty"
+notmuch search from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp  '(and (date) (from keithp))'| notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, one argument"
+notmuch search date:2009-11-18 and from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp  '(and (date 2009-11-18) (from keithp))' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, two arguments"
+notmuch search date:2009-11-17..2009-11-18 and from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp  '(and (date 2009-11-17 2009-11-18) (from keithp))' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, illegal nesting 1"
+notmuch search --query=sexp '(to (date))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+nested field: 'date' inside 'to'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, illegal nesting 2"
+notmuch search --query=sexp '(to (date 2021-11-18))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+nested field: 'date' inside 'to'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, illegal nesting 3"
+notmuch search --query=sexp '(date (to))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+expected atom as first argument of 'date'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, illegal nesting 4"
+notmuch search --query=sexp '(date today (to))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+expected atom as second argument of 'date'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, too many arguments"
+notmuch search --query=sexp '(date yesterday and tommorow)' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+'date' expects maximum of two arguments
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "date query, bad date"
+notmuch search --query=sexp '(date "hawaiian-pizza-day")' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+Didn't understand date specification 'hawaiian-pizza-day'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, empty"
+notmuch search from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp  '(and (lastmod) (from keithp))'| notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, one argument"
+notmuch tag +4EFC743A.3060609@april.org id:4EFC743A.3060609@april.org
+revision=$(notmuch count --lastmod '*' | cut -f3)
+notmuch search lastmod:$revision..$revision | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp  "(and (lastmod $revision))" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, two arguments"
+notmuch tag +keithp from:keithp
+revision2=$(notmuch count --lastmod '*' | cut -f3)
+notmuch search lastmod:$revision..$revision2 | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp  "(and (lastmod $revision $revision2))" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, illegal nesting 1"
+notmuch search --query=sexp '(to (lastmod))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+nested field: 'lastmod' inside 'to'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, bad from revision"
+notmuch search --query=sexp '(lastmod apples)' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+bad 'from' revision: 'apples'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, bad to revision"
+notmuch search --query=sexp '(lastmod 0 apples)' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+bad 'to' revision: 'apples'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, illegal nesting 2"
+notmuch search --query=sexp '(to (lastmod 2021-11-18))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+nested field: 'lastmod' inside 'to'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, illegal nesting 3"
+notmuch search --query=sexp '(lastmod (to))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+expected atom as first argument of 'lastmod'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, illegal nesting 4"
+notmuch search --query=sexp '(lastmod today (to))' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+expected atom as second argument of 'lastmod'
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, too many arguments"
+notmuch search --query=sexp '(lastmod yesterday and tommorow)' > OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+'lastmod' expects maximum of two arguments
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "user header (unknown header)"
 notmuch search --query=sexp '(FooBar)' >& OUTPUT
 cat <<EOF > EXPECTED
index 409cfdcc851c2eeb9f4011fdefa60f875ea6b138..b4f6294e306edc2daf222f847b9e4ccddb8a5fd2 100755 (executable)
@@ -18,6 +18,12 @@ test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; T
 thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
 thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
 
+test_begin_subtest "search by path: specification (multiple)"
+output=$(notmuch search path:bad path:bad/news path:things/bad | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
+thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
+thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
+
 test_begin_subtest "Top level folder"
 output=$(notmuch search folder:'""' | notmuch_search_sanitize)
 test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Top level (inbox unread)"
@@ -28,8 +34,13 @@ test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1(2)] Notmuch Test Suite
 
 test_begin_subtest "Folder search with --output=files"
 output=$(notmuch search --output=files folder:bad/news | notmuch_search_files_sanitize)
-test_expect_equal "$output" "MAIL_DIR/bad/news/msg-003
-MAIL_DIR/duplicate/bad/news/msg-003"
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-XXX
+MAIL_DIR/duplicate/bad/news/msg-XXX"
+
+test_begin_subtest "Folder search with --output=files (trailing /)"
+output=$(notmuch search --output=files folder:bad/news/ | notmuch_search_files_sanitize)
+test_expect_equal "$output" "MAIL_DIR/bad/news/msg-XXX
+MAIL_DIR/duplicate/bad/news/msg-XXX"
 
 test_begin_subtest "After removing duplicate instance of matching path"
 rm -r "${MAIL_DIR}/bad/news"
@@ -39,7 +50,7 @@ test_expect_equal "$output" ""
 
 test_begin_subtest "Folder search with --output=files part #2"
 output=$(notmuch search --output=files folder:duplicate/bad/news | notmuch_search_files_sanitize)
-test_expect_equal "$output" "MAIL_DIR/duplicate/bad/news/msg-003"
+test_expect_equal "$output" "MAIL_DIR/duplicate/bad/news/msg-XXX"
 
 test_begin_subtest "After removing duplicate instance of matching path part #2"
 output=$(notmuch search folder:duplicate/bad/news | notmuch_search_sanitize)
@@ -120,6 +131,13 @@ test_expect_equal "$output" "MAIL_DIR/bar/17:2,
 MAIL_DIR/bar/18:2,
 MAIL_DIR/cur/51:2,"
 
+test_begin_subtest "path: search (trailing /)"
+output=$(notmuch search --output=files path:"bar/" | notmuch_search_files_sanitize | sort)
+# cur/51:2, is a duplicate of bar/18:2,
+test_expect_equal "$output" "MAIL_DIR/bar/17:2,
+MAIL_DIR/bar/18:2,
+MAIL_DIR/cur/51:2,"
+
 test_begin_subtest "top level path: search"
 output=$(notmuch search --output=files path:'""' | notmuch_search_files_sanitize | sort)
 test_expect_equal "$output" "MAIL_DIR/01:2,
index 6a3e581284ed8796e931d7ec5dbe6bc41a480748..ec7b1461754cb92bc2863bb66d8c4b30bbc9d94b 100755 (executable)
@@ -156,4 +156,46 @@ EOF
 output=$(notmuch show --format=json --body=false --format-version=2 id:message-id@example.com)
 test_expect_equal_json "$output" "$(cat EXPECTED)"
 
+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\""\
+          "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.notmuchmail.org (some MTA) with ESMTP id 12345678
+       for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+
+notmuch config set show.extra_headers "in-reply-to;received"
+output=$(notmuch show --format=json --body=false id:${gen_msg_id} | notmuch_json_show_sanitize)
+cat <<EOF > EXPECTED
+[
+    [
+        [
+            {
+                "crypto": {},
+                "date_relative": "2000-01-01",
+                "excluded": false,
+                "filename": [
+                    "YYYYY"
+                ],
+                "headers": {
+                    "Date": "Sat, 01 Jan 2000 12:00:00 +0000",
+                    "From": "Notmuch Test Suite <test_suite@notmuchmail.org>",
+                    "In-Reply-To": "<parent@notmuch-test-suite>",
+                    "Received": "from mail.example.com (mail.example.com [1.1.1.1])\tby mail.notmuchmail.org (some MTA) with ESMTP id 12345678\tfor <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)",
+                    "Subject": "extra-headers",
+                    "To": "Notmuch Test Suite <test_suite@notmuchmail.org>"
+                },
+                "id": "XXXXX",
+                "match": true,
+                "tags": [
+                    "inbox",
+                    "unread"
+                ],
+                "timestamp": 946728000
+            },
+            []
+        ]
+    ]
+]
+EOF
+test_expect_equal_json "${output}" "$(cat EXPECTED)"
+
 test_done
index 18084273f3eaa446ff1722262f6ab2b8ff97af03..0d32560cc1b54d8eaaf5af04bb6859cf7b7e4e50 100755 (executable)
@@ -47,4 +47,18 @@ filename=$(notmuch search --output=files "id:$id")
 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_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\""\
+          "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])
+       by mail.notmuchmail.org (some MTA) with ESMTP id 12345678
+       for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+
+notmuch config set show.extra_headers "in-reply-to;received"
+notmuch show --format=sexp --body=false id:${gen_msg_id} | \
+    notmuch_dir_sanitize | sed 's/msg-[0-9]*/MSG/g'> OUTPUT
+cat <<EOF > EXPECTED
+((((:id "MSG@notmuch-test-suite" :match t :excluded nil :filename ("MAIL_DIR/MSG") :timestamp 946728000 :date_relative "2000-01-01" :tags ("inbox" "unread") :crypto () :headers (:Subject "extra-headers" :From "Notmuch Test Suite <test_suite@notmuchmail.org>" :To "Notmuch Test Suite <test_suite@notmuchmail.org>" :Date "Sat, 01 Jan 2000 12:00:00 +0000" :In-Reply-To "<parent@notmuch-test-suite>" :Received "from mail.example.com (mail.example.com [1.1.1.1])\011by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\011for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)")) ())))
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
 test_done
index 1b6660f0a3598b6ec482c563ff02960b469284e1..a05b828ae9f759f31c12a41c015cc77224d8a4ea 100755 (executable)
@@ -485,6 +485,31 @@ Sender <sender@example.com> writes:
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
+test_begin_subtest "Reply with show.extra_headers set"
+notmuch config set show.extra_headers Received
+add_message '[from]="Sender <sender@example.com>"' \
+            [to]=test_suite_other@notmuchmail.org
+
+test_emacs "(let ((message-hidden-headers '()))
+           (notmuch-search \"id:\\\"${gen_msg_id}\\\"\")
+           (notmuch-test-wait)
+           (notmuch-search-reply-to-thread)
+           (test-output))"
+cat <<EOF >EXPECTED
+From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+To: Sender <sender@example.com>
+Subject: Re: ${test_subtest_name}
+In-Reply-To: <${gen_msg_id}>
+Fcc: ${MAIL_DIR}/sent
+References: <${gen_msg_id}>
+--text follows this line--
+Sender <sender@example.com> writes:
+
+> This is just a test message (#${gen_msg_cnt})
+EOF
+notmuch config set show.extra_headers
+test_expect_equal_file EXPECTED OUTPUT
+
 test_begin_subtest "Reply from address in named group list within emacs"
 add_message '[from]="Sender <sender@example.com>"' \
             '[to]=group:test_suite@notmuchmail.org,someone@example.com\;' \
@@ -680,7 +705,7 @@ References: <XXX>
 --text follows this line--
 test_suite@notmuchmail.org writes:
 
-> This is just a test message (#7)
+> This is just a test message (#${gen_msg_cnt})
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
index 8dbf89352a60e4af75efd19d6c3c0eb9bfe246c3..3c6626b41d3649f1c6ed56ebafb41665b24df5d1 100755 (executable)
@@ -13,16 +13,29 @@ test_description='PGP/MIME signature verification and decryption'
 test_require_emacs
 add_gnupg_home
 
-test_begin_subtest "emacs delivery of signed message"
+test_begin_subtest "emacs delivery of signed message via fcc"
 test_expect_success \
 'emacs_fcc_message \
     "test signed message 001" \
     "This is a test signed message." \
     "(mml-secure-message-sign)"'
 
+test_begin_subtest "emacs delivery of signed message via fcc and smtp"
+emacs_deliver_message \
+    'signed message sent via SMTP' \
+    'This is a test that messages are sent via SMTP' \
+    "(add-hook 'message-send-mail-hook (lambda () (sleep-for 1)))
+     (mml-secure-message-sign)"
+msg_file=$(notmuch search --output=files subject:signed-message-sent-via-SMTP)
+test_expect_equal_message_body sent_message "$msg_file"
+
 test_begin_subtest "signed part content-type indexing"
-output=$(notmuch search mimetype:multipart/signed and mimetype:application/pgp-signature | notmuch_search_sanitize)
-test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; test signed message 001 (inbox signed)"
+notmuch search mimetype:multipart/signed and mimetype:application/pgp-signature | notmuch_search_sanitize > OUTPUT
+cat <<EOF >EXPECTED
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; test signed message 001 (inbox signed)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; signed message sent via SMTP (inbox signed)
+EOF
+test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "signature verification"
 output=$(notmuch show --format=json --verify subject:"test signed message 001" \
index 0a2727e74fb4573a36294ac3f68260b9f26f3e2e..cf202bb3e0a03f24fcce0c51b14e6e33de76d28b 100755 (executable)
@@ -24,8 +24,8 @@ test_expect_equal "$output" "No new mail."
 
 test_begin_subtest "Multiple files for same message"
 cat <<EOF >EXPECTED
-MAIL_DIR/msg-001
-MAIL_DIR/spam/msg-001
+MAIL_DIR/msg-XXX
+MAIL_DIR/spam/msg-XXX
 EOF
 notmuch search --output=files id:$id_x | notmuch_search_files_sanitize >OUTPUT
 test_expect_equal_file EXPECTED OUTPUT
diff --git a/test/T392-python-cffi-notmuch.sh b/test/T392-python-cffi-notmuch.sh
new file mode 100755 (executable)
index 0000000..15c8fc6
--- /dev/null
@@ -0,0 +1,48 @@
+#!/usr/bin/env bash
+test_description="python bindings (notmuch test suite)"
+. $(dirname "$0")/test-lib.sh || exit 1
+
+if [ $NOTMUCH_HAVE_PYTHON3_CFFI -eq 0 -o $NOTMUCH_HAVE_PYTHON3_PYTEST -eq 0 ]; then
+    test_done
+fi
+
+add_email_corpus
+
+cat <<EOF > recurse.py
+from notmuch2 import Database
+def show_msgs(msgs, level):
+    print('{:s} {:s}'.format(' ' * level*4, type(msgs).__name__))
+    for msg in msgs:
+        print('{:s} {:s}'.format(' ' * (level*4+2), type(msg).__name__))
+        replies=msg.replies()
+        show_msgs(replies, level+1)
+db = Database(config=Database.CONFIG.SEARCH)
+msg=db.find("87ocn0qh6d.fsf@yoom.home.cworth.org")
+threads = db.threads(query="thread:"+msg.threadid)
+thread = next (threads)
+show_msgs(thread, 0)
+EOF
+
+test_begin_subtest "recursive traversal of replies (no crash)"
+test_python < recurse.py
+error=$?
+test_expect_equal "${error}" 0
+
+test_begin_subtest "recursive traversal of replies (output)"
+test_python < recurse.py
+tail -n 10 < OUTPUT > OUTPUT.sample
+cat <<EOF > EXPECTED
+   OwnedMessage
+     MessageIter
+   OwnedMessage
+     MessageIter
+       OwnedMessage
+         MessageIter
+   OwnedMessage
+     MessageIter
+   OwnedMessage
+     MessageIter
+EOF
+test_expect_equal_file EXPECTED OUTPUT.sample
+
+test_done
index a1ed1c2b90ec7c821d6033a013ba44155a2296f1..842781a48c3512c82c259688947f8b87b1f53167 100755 (executable)
@@ -68,6 +68,16 @@ test_emacs '(notmuch-hello)
 notmuch tag -$tag '*'
 test_expect_equal_file $EXPECTED/notmuch-hello-long-names OUTPUT
 
+test_begin_subtest "All tags show up"
+tag=exclude_me
+notmuch tag +$tag '*'
+notmuch config set search.exclude_tags $tag
+test_emacs '(notmuch-hello)
+            (test-output)'
+notmuch tag -$tag '*'
+test_expect_equal_file $EXPECTED/notmuch-hello-all-tags OUTPUT
+
+test_done
 test_begin_subtest "notmuch-hello with nonexistent CWD"
 test_emacs '
       (notmuch-hello)
index 4b5f5fdecafde48f5e36bf822a6b27534dcc93e4..057ad37ee30e1a1e90fef01f82edac906c5101f4 100755 (executable)
@@ -220,7 +220,9 @@ test_emacs '(notmuch-show "id:basic-encrypted@crypto.notmuchmail.org")
 test_expect_equal_file $EXPECTED/notmuch-show-decrypted-message OUTPUT
 
 test_begin_subtest "show encrypted rfc822 message"
-test_subtest_known_broken
+if ${TEST_EMACS} --quick --batch --eval '(kill-emacs (if (version< emacs-version "28") 0 1))'; then
+    test_subtest_known_broken
+fi
 test_emacs '(notmuch-show "id:encrypted-rfc822-attachment@crypto.notmuchmail.org")
             (test-visible-output)'
 test_expect_code 1 'fgrep "!!!" OUTPUT'
index 769fe86e6f8a347d86c9d49af17c26fd31ae94d5..2314efd220a2bd79575809094b8fffffa0122848 100755 (executable)
@@ -9,10 +9,8 @@ test_begin_subtest "building database"
 test_expect_success "NOTMUCH_NEW"
 
 cat <<EOF > c_head
-#include <stdio.h>
-#include <notmuch.h>
 #include <notmuch-test.h>
-#include <talloc.h>
+
 int main (int argc, char** argv)
 {
    notmuch_database_t *db;
@@ -82,7 +80,7 @@ cat <<EOF > EXPECTED
 == stdout ==
 0
 == stderr ==
-A Xapian exception occurred at lib/database.cc:XXX: Database has been closed
+A Xapian exception occurred at database.cc:XXX: Database has been closed
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
@@ -147,7 +145,7 @@ cat <<EOF > EXPECTED
 == stdout ==
 1
 == stderr ==
-A Xapian exception occurred at lib/database.cc:XXX: Database has been closed
+A Xapian exception occurred at database.cc:XXX: Database has been closed
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
index 28325ff23605cae0ea644d13c69b2fc0b2bb4896..ebd7fcb2d358d3be118d11c40362facbed9547e2 100755 (executable)
@@ -9,10 +9,8 @@ test_begin_subtest "building database"
 test_expect_success "NOTMUCH_NEW"
 
 cat <<EOF > c_head
-#include <stdio.h>
-#include <notmuch.h>
 #include <notmuch-test.h>
-#include <talloc.h>
+
 int main (int argc, char** argv)
 {
    notmuch_database_t *db;
@@ -53,7 +51,7 @@ cat <<EOF > EXPECTED
 == stdout ==
 1
 == stderr ==
-A Xapian exception occurred at lib/directory.cc:XXX: Database has been closed
+A Xapian exception occurred at directory.cc:XXX: Database has been closed
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
@@ -70,7 +68,7 @@ cat <<EOF > EXPECTED
 == stdout ==
 1
 == stderr ==
-A Xapian exception occurred at lib/directory.cc:XXX: Database has been closed
+A Xapian exception occurred at directory.cc:XXX: Database has been closed
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
index 50b0a88e0793ada49f486cf463865ac064445085..ff1d49849bc571775d69a9af3d46ddaf89d23cb7 100755 (executable)
@@ -9,10 +9,8 @@ test_begin_subtest "building database"
 test_expect_success "NOTMUCH_NEW"
 
 cat <<EOF > c_head
-#include <stdio.h>
-#include <notmuch.h>
 #include <notmuch-test.h>
-#include <talloc.h>
+
 int main (int argc, char** argv)
 {
    notmuch_database_t *db;
index ee55ef291d7a44c23a2728cf26c9af36be8f1107..8b61d18215af0698803c0c0b4b4fab0762a6f3e4 100755 (executable)
@@ -19,9 +19,8 @@ cat <<'EOF' > c_tail
 EOF
 
 cat <<EOF > c_head0
-#include <stdio.h>
-#include <notmuch.h>
 #include <notmuch-test.h>
+
 int main (int argc, char** argv)
 {
    notmuch_database_t *db;
index 088e66dd937313bd1f9c44d60513f998f2b3dc7a..b45836cdd989ca6ac25bc58bc15f7a13fa38dd63 100755 (executable)
@@ -24,9 +24,8 @@ cat <<'EOF' > c_tail
 EOF
 
 cat <<EOF > c_head
-#include <stdio.h>
-#include <notmuch.h>
 #include <notmuch-test.h>
+
 int main (int argc, char** argv)
 {
    notmuch_database_t *db;
index 9fa51fc010c4da643194f4079e5015678b6b783c..26a1f033085bf4470cac95e9a3dd24c380b53bd9 100755 (executable)
@@ -23,8 +23,6 @@ EOF
 }
 
 cat <<EOF > c_head
-#include <string.h>
-#include <stdlib.h>
 #include <notmuch-test.h>
 
 int main (int argc, char** argv)
@@ -272,6 +270,29 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 restore_database
 
+test_begin_subtest "notmuch_config_get_values (ignore leading/trailing whitespace)"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL%
+{
+    notmuch_config_values_t *values;
+    EXPECT0(notmuch_config_set (db, NOTMUCH_CONFIG_NEW_TAGS, " a ; b c ; d "));
+    for (values = notmuch_config_get_values (db, NOTMUCH_CONFIG_NEW_TAGS);
+        notmuch_config_values_valid (values);
+        notmuch_config_values_move_to_next (values))
+    {
+         puts (notmuch_config_values_get (values));
+    }
+}
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+a
+b c
+d
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+restore_database
+
 test_begin_subtest "notmuch_config_get_values_string"
 cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} %NULL%
 {
@@ -418,6 +439,7 @@ cat <<'EOF' >EXPECTED
 09: 'NULL'
 10: 'USER_FULL_NAME'
 11: '8000'
+12: 'NULL'
 == stderr ==
 EOF
 unset MAILDIR
@@ -616,8 +638,6 @@ cp notmuch-config.bak notmuch-config
 test_expect_equal_file EXPECTED OUTPUT
 
 cat <<EOF > c_head2
-#include <string.h>
-#include <stdlib.h>
 #include <notmuch-test.h>
 
 int main (int argc, char** argv)
@@ -730,6 +750,7 @@ cat <<'EOF' >EXPECTED
 09: 'test_suite_other@notmuchmail.org;test_suite@otherdomain.org'
 10: 'Notmuch Test Suite'
 11: '8000'
+12: 'NULL'
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT
@@ -763,6 +784,7 @@ cat <<'EOF' >EXPECTED
 09: 'NULL'
 10: 'USER_FULL_NAME'
 11: '8000'
+12: 'NULL'
 == stderr ==
 EOF
 test_expect_equal_file EXPECTED OUTPUT.clean
@@ -839,6 +861,7 @@ maildir.synchronize_flags true
 new.ignore sekrit_junk
 new.tags unread;inbox
 search.exclude_tags foo;bar;fub
+show.extra_headers (null)
 test.key1 testvalue1
 test.key2 testvalue2
 user.name Notmuch Test Suite
@@ -953,6 +976,7 @@ EOF
 test_expect_equal_file EXPECTED OUTPUT
 
 test_begin_subtest "open: database parameter overrides implicit config"
+cp $NOTMUCH_CONFIG ${NOTMUCH_CONFIG}.bak
 notmuch config set database.path ${MAIL_DIR}/nonexistent
 cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
   const char *path = NULL;
@@ -963,6 +987,7 @@ cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
   path = notmuch_database_get_path (db);
   printf ("path: %s\n", path ? path : "(null)");
 EOF
+cp ${NOTMUCH_CONFIG}.bak ${NOTMUCH_CONFIG}
 cat <<EOF> EXPECTED
 == stdout ==
 status: 0
@@ -973,4 +998,43 @@ EOF
 notmuch_dir_sanitize < OUTPUT > OUTPUT.clean
 test_expect_equal_file EXPECTED OUTPUT.clean
 
+cat <<EOF > c_body
+  notmuch_status_t st = notmuch_database_open_with_config(NULL,
+                                                         NOTMUCH_DATABASE_MODE_READ_ONLY,
+                                                         "", NULL, &db, NULL);
+  printf ("status == SUCCESS: %d\n", st == NOTMUCH_STATUS_SUCCESS);
+  if (db) {
+    const char *mail_root = NULL;
+    mail_root = notmuch_config_get (db, NOTMUCH_CONFIG_MAIL_ROOT);
+    printf ("mail_root: %s\n", mail_root ? mail_root : "(null)");
+  }
+EOF
+
+cat <<EOF> EXPECTED.common
+== stdout ==
+status == SUCCESS: 0
+db == NULL: 1
+== stderr ==
+EOF
+
+test_begin_subtest "open/error: config=empty with no mail root in db "
+old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG}
+unset NOTMUCH_CONFIG
+cat c_head3 c_body c_tail3 | test_C
+export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
+notmuch_dir_sanitize < OUTPUT > OUTPUT.clean
+test_expect_equal_file EXPECTED.common OUTPUT.clean
+
+test_begin_subtest "open/error: config=empty with no mail root in db (xdg)"
+old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG}
+unset NOTMUCH_CONFIG
+backup_database
+mkdir -p home/.local/share/notmuch
+mv mail/.notmuch home/.local/share/notmuch/default
+cat c_head3 c_body c_tail3 | test_C
+restore_database
+export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
+notmuch_dir_sanitize < OUTPUT > OUTPUT.clean
+test_expect_equal_file EXPECTED.common OUTPUT.clean
+
 test_done
index 7375e2acefbdb7ce947acc475e0af68778f4d600..1a517423e3e476a2acaa4c8b871badadfb7084f8 100755 (executable)
@@ -6,8 +6,6 @@ test_description="library reopen API"
 add_email_corpus
 
 cat <<EOF > c_head
-#include <string.h>
-#include <stdlib.h>
 #include <notmuch-test.h>
 
 int main (int argc, char** argv)
index d0e52f4ac46cbb481b76197682f43ecb3adce728..4ec854748532d105cd5e1a71256536b066b89cf3 100755 (executable)
@@ -6,10 +6,6 @@ test_description="message property API"
 add_email_corpus
 
 cat <<EOF > c_head
-#include <stdio.h>
-#include <string.h>
-#include <stdlib.h>
-#include <talloc.h>
 #include <notmuch-test.h>
 
 void print_properties (notmuch_message_t *message, const char *prefix, notmuch_bool_t exact) {
index 7aaaff2ababea2bc9ef6a6cd7bc9844782a30f38..8f4c380f110891f402038927d5c5e37c872b3dc5 100755 (executable)
@@ -9,9 +9,6 @@ if [ $NOTMUCH_HAVE_XAPIAN_DB_RETRY_LOCK -ne 1 ]; then
     test_subtest_known_broken
 fi
 test_C ${MAIL_DIR} <<'EOF'
-#include <unistd.h>
-#include <stdlib.h>
-#include <sys/wait.h>
 #include <notmuch-test.h>
 
 void
index 274105c708fa2d4c73429520e3811fcdce6debff..636b20c71eda0ab214f2c7b9da98e10d43398b6c 100755 (executable)
@@ -10,11 +10,8 @@ test_begin_subtest "catching DatabaseModifiedError in _notmuch_message_ensure_me
 first_id=$(notmuch search --output=messages '*'| head -1 | sed s/^id://)
 
 test_C ${MAIL_DIR} <<EOF
-#include <unistd.h>
-#include <stdlib.h>
 #include <notmuch-test.h>
-#include <talloc.h>
-#include <assert.h>
+
 int
 main (int argc, char **argv)
 {
index 4408d085a58031046d81148375695d55ce5eef25..5648896fba377b231e7cb688a316ccb06787049c 100755 (executable)
@@ -58,13 +58,13 @@ test_expect_equal_file EXPECTED OUTPUT
 test_begin_subtest "notmuch search --output=files with partially gzipped mail store"
 notmuch search --output=files '*' | notmuch_search_files_sanitize > OUTPUT
 cat <<EOF > EXPECTED
-MAIL_DIR/msg-001.gz
-MAIL_DIR/msg-002.gz
-MAIL_DIR/msg-003.gz
-MAIL_DIR/msg-004
-MAIL_DIR/msg-005.gz
-MAIL_DIR/msg-006
-MAIL_DIR/msg-007.gz
+MAIL_DIR/msg-XXX.gz
+MAIL_DIR/msg-XXX.gz
+MAIL_DIR/msg-XXX.gz
+MAIL_DIR/msg-XXX
+MAIL_DIR/msg-XXX.gz
+MAIL_DIR/msg-XXX
+MAIL_DIR/msg-XXX.gz
 EOF
 test_expect_equal_file EXPECTED OUTPUT
 
diff --git a/test/T800-asan.sh b/test/T800-asan.sh
new file mode 100755 (executable)
index 0000000..8607732
--- /dev/null
@@ -0,0 +1,39 @@
+#!/usr/bin/env bash
+test_description='run code with ASAN enabled against the library'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+if [ $NOTMUCH_HAVE_ASAN -ne 1 ]; then
+    printf "Skipping due to missing ASAN support\n"
+    test_done
+fi
+
+add_email_corpus
+
+TEST_CFLAGS="-fsanitize=address"
+
+test_begin_subtest "open and destroy"
+test_C ${MAIL_DIR} ${NOTMUCH_CONFIG} <<EOF
+#include <notmuch.h>
+#include <stdio.h>
+
+int main(int argc, char **argv) {
+  notmuch_database_t *db = NULL;
+
+  notmuch_status_t st = notmuch_database_open_with_config(argv[1],
+                                                          NOTMUCH_DATABASE_MODE_READ_ONLY,
+                                                          argv[2], NULL, &db, NULL);
+
+  printf("db != NULL: %d\n", db != NULL);
+  if (db != NULL)
+    notmuch_database_destroy(db);
+  return 0;
+}
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+db != NULL: 1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
diff --git a/test/emacs.expected-output/notmuch-hello-all-tags b/test/emacs.expected-output/notmuch-hello-all-tags
new file mode 100644 (file)
index 0000000..65e479f
--- /dev/null
@@ -0,0 +1,11 @@
+   Welcome to notmuch. You have 52 messages.
+
+Search:                                                                     .
+
+All tags: [hide]
+
+          4 attachment            52 inbox                 52 unread
+         52 exclude_me             7 signed
+
+        Hit `?' for context-sensitive help in any Notmuch screen.
+                     Customize Notmuch or this page.
index 34dbb8e0403feefc4be402178943d8576a8d9c01..ed713099b1d3b435024abae6d100d6c1ddf75a4d 100644 (file)
@@ -1,7 +1,21 @@
 #ifndef _NOTMUCH_TEST_H
 #define _NOTMUCH_TEST_H
+#ifndef _GNU_SOURCE
+#define _GNU_SOURCE
+#endif
+
+#include <assert.h>
+#include <dlfcn.h>
+#include <fcntl.h>
 #include <stdio.h>
 #include <stdlib.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <sys/wait.h>
+#include <talloc.h>
+#include <unistd.h>
+
 #include <notmuch.h>
 
 inline static void
@@ -14,4 +28,23 @@ expect0 (int line, notmuch_status_t ret)
 }
 
 #define EXPECT0(v)  expect0 (__LINE__, v);
+
+inline static void *
+dlsym_next (const char *symbol)
+{
+    void *sym = dlsym (RTLD_NEXT, symbol);
+    char *str = dlerror ();
+
+    if (str != NULL) {
+       fprintf (stderr, "finding symbol '%s' failed: %s", symbol, str);
+       exit (77);
+    }
+    return sym;
+}
+
+#define WRAP_DLFUNC(_rtype, _func, _args)                               \
+    _rtype _func _args;                                                 \
+    _rtype _func _args {                                                \
+       static _rtype (*_func##_orig) _args = NULL;                         \
+       if (! _func##_orig ) *(void **) (&_func##_orig) = dlsym_next (#_func);
 #endif
index 56c628e56b29f5007f49e78908238f8370d872cd..d8397714874cbc4236422aa61bdda53f7e322205 100644 (file)
@@ -31,7 +31,7 @@ path=/path/to/maildir
 [user]
 name=Test Suite
 primary_email=test.suite@example.com
-other_email=another.suite@example.com;
+other_email=another.suite@example.com
 
 # Configuration for "notmuch new"
 #
@@ -60,7 +60,7 @@ tags=foo;bar;
 #              query will override that exclusion.
 #
 [search]
-exclude_tags=baz;
+exclude_tags=baz
 
 # Maildir compatibility configuration
 #
index 9d73a571a71e5f733ca09c0cca57725a4dda7de3..9e956ddf228b6dc87482fa559625deec04a62252 100644 (file)
@@ -12,8 +12,10 @@ main (int argc, char **argv)
     if (argc != 3)
        return 1;
 
-    if (notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY,
-                                      &notmuch, &message)) {
+    if (notmuch_database_open_with_config (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY,
+                                          "",
+                                          NULL,
+                                          &notmuch, &message)) {
        if (message) {
            fputs (message, stderr);
            free (message);
index dde32177aae035e27b215c5b6e721b984d36fd48..a298526d5fedc7a310ffc98d44bf753ae649efa6 100644 (file)
@@ -54,8 +54,9 @@ emacs_deliver_message () {
           (message-goto-body)
           (insert \"${body}\")
           $*
-          (notmuch-mua-send-and-exit))"
-
+          (let ((mml-secure-smime-sign-with-sender t)
+                (mml-secure-openpgp-sign-with-sender t))
+            (notmuch-mua-send-and-exit)))"
     # In case message was sent properly, client waits for confirmation
     # before exiting and resuming control here; therefore making sure
     # that server exits by sending (KILL) signal to it is safe.
index e476a69b2a50f9c06f4b673881ee4024d464e6ab..833bf5fe6d83ba22de3939cc56720a543c0f87c8 100644 (file)
@@ -145,7 +145,7 @@ add_gpgsm_home () {
     mkdir -p -m 0700 "$GNUPGHOME"
     gpgsm --batch --no-tty --no-common-certs-import --pinentry-mode=loopback --passphrase-fd 3 \
          --disable-dirmngr --import  >"$GNUPGHOME"/import.log 2>&1 3<<<'' <$NOTMUCH_SRCDIR/test/smime/0xE0972A47.p12
-    fpr=$(gpgsm --batch --list-key test_suite@notmuchmail.org | sed -n 's/.*fingerprint: //p')
+    fpr=$(gpgsm --batch --with-colons --list-key test_suite@notmuchmail.org | awk -F: '/^fpr/ {print $10}')
     echo "$fpr S relax" >> "$GNUPGHOME/trustlist.txt"
     gpgsm --quiet --batch --no-tty --no-common-certs-import --disable-dirmngr --import < $NOTMUCH_SRCDIR/test/smime/ca.crt
     echo "4D:E0:FF:63:C0:E9:EC:01:29:11:C8:7A:EE:DA:3A:9A:7F:6E:C1:0D S" >> "$GNUPGHOME/trustlist.txt"
@@ -432,6 +432,20 @@ test_expect_equal_file () {
     test_diff_file_ "$1" "$2"
 }
 
+# Like test_expect_equal_file, but compare the part of the two files after the first blank line
+test_expect_equal_message_body () {
+    exec 1>&6 2>&7             # Restore stdout and stderr
+    if [ -z "$inside_subtest" ]; then
+       error "bug in the test script: test_expect_equal_file without test_begin_subtest"
+    fi
+    test "$#" = 2 ||
+       error "bug in the test script: not 2 parameters to test_expect_equal_file"
+
+    expected=$(sed '1,/^$/d' "$1")
+    output=$(sed '1,/^$/d' "$2")
+    test_expect_equal "$expected" "$output"
+}
+
 # Like test_expect_equal, but takes two filenames. Fails if either is empty
 test_expect_equal_file_nonempty () {
     exec 1>&6 2>&7             # Restore stdout and stderr
@@ -529,7 +543,7 @@ notmuch_debug_sanitize () {
 }
 
 notmuch_exception_sanitize () {
-    perl -pe 's/(A Xapian exception occurred at .*[.]cc?):([0-9]*)/\1:XXX/'
+    perl -pe 's,(A Xapian exception occurred at) .*?([^/]*[.]cc?):([0-9]*),\1 \2:XXX,'
 }
 
 notmuch_search_sanitize () {
@@ -537,7 +551,7 @@ notmuch_search_sanitize () {
 }
 
 notmuch_search_files_sanitize () {
-    notmuch_dir_sanitize
+    notmuch_dir_sanitize |  sed 's/msg-[0-9][0-9][0-9]/msg-XXX/'
 }
 
 notmuch_dir_sanitize () {
index 8703334cda8d4836660bed1f8cf41c65e2fec023..83a4c6f146fe15387b0ef8c151ae6e5b4dcbc766 100644 (file)
@@ -5,7 +5,7 @@
 extern "C" {
 #endif
 
-typedef enum hex_status {
+typedef enum {
     HEX_SUCCESS = 0,
     HEX_SYNTAX_ERROR,
     HEX_OUT_OF_MEMORY
index 9c46a81a58e20030b586953e577efd777ef2af17..03d7648d329a2e7b3667a894b558e93d440d1a6a 100644 (file)
@@ -42,13 +42,15 @@ const char *
 strsplit_len (const char *s, char delim, size_t *len)
 {
     bool escaping = false;
-    size_t count = 0;
+    size_t count = 0, last_nonspace = 0;
 
-    /* Skip initial unescaped delimiters */
-    while (*s && *s == delim)
+    /* Skip initial unescaped delimiters and whitespace */
+    while (*s && (*s == delim || isspace (*s)))
        s++;
 
     while (s[count] && (escaping || s[count] != delim)) {
+       if (! isspace (s[count]))
+           last_nonspace = count;
        escaping = (s[count] == '\\');
        count++;
     }
@@ -56,7 +58,7 @@ strsplit_len (const char *s, char delim, size_t *len)
     if (count == 0)
        return NULL;
 
-    *len = count;
+    *len = last_nonspace + 1;
     return s;
 }
 
index 3f8003cd4c4fdbfa727f3764b19cdf7d8ac046db..c74e8a041aa2b1393680678e162e49730668a3ee 100644 (file)
@@ -1 +1 @@
-0.34.2
+0.35