aboutsummaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Makefile.local45
-rw-r--r--NEWS13
-rw-r--r--RELEASING6
-rw-r--r--bindings/python/docs/source/index.rst17
-rw-r--r--bindings/python/notmuch/__init__.py2
-rw-r--r--bindings/python/notmuch/database.py216
-rw-r--r--bindings/python/notmuch/filename.py25
-rw-r--r--bindings/python/notmuch/globals.py88
-rw-r--r--bindings/python/notmuch/message.py143
-rw-r--r--bindings/python/notmuch/tag.py39
-rw-r--r--bindings/python/notmuch/thread.py103
-rw-r--r--contrib/.gitattributes1
-rw-r--r--contrib/notmuch-deliver/COPYING340
-rw-r--r--contrib/notmuch-deliver/README.mkd8
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/Makefile.am1
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/configure.in2
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/maildircreate.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/maildircreate.h1
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/maildirmisc.h1
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/maildirmkdir.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/maildir/maildiropen.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/Makefile.am1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/atotimet.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/atouidt.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/changeuidgid.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/configure.in2
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/numlib.h1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strdevt.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strgidt.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strhdevt.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strhinot.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strhpidt.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strhtimet.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strinot.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strofft.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strpidt.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strsize.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strsizet.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/strtimet.c1
-rw-r--r--contrib/notmuch-deliver/maildrop/numlib/struidt.c1
-rw-r--r--contrib/notmuch-deliver/src/main.c29
-rw-r--r--debian/control2
-rw-r--r--emacs/notmuch-hello.el28
-rw-r--r--emacs/notmuch-show.el54
-rw-r--r--emacs/notmuch.el14
-rw-r--r--lib/Makefile.local2
-rw-r--r--lib/message.cc14
-rw-r--r--notmuch-dump.c7
-rw-r--r--notmuch-tag.c85
-rw-r--r--test/.gitignore1
-rw-r--r--test/Makefile.local10
-rw-r--r--test/README3
-rwxr-xr-xtest/basic14
-rwxr-xr-xtest/emacs37
-rw-r--r--test/emacs.expected-output/emacs-stashing9
-rw-r--r--test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation215
-rw-r--r--test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation215
-rwxr-xr-xtest/notmuch-test5
-rwxr-xr-xtest/raw2
-rwxr-xr-xtest/symbol-hiding3
-rw-r--r--test/symbol-test.cc14
-rw-r--r--[-rwxr-xr-x]test/test-lib.sh105
-rw-r--r--util/Makefile.local2
63 files changed, 1268 insertions, 676 deletions
diff --git a/Makefile.local b/Makefile.local
index d97fa618..15e6d882 100644
--- a/Makefile.local
+++ b/Makefile.local
@@ -12,16 +12,18 @@ PACKAGE=notmuch
IS_GIT=$(shell if [ -d .git ] ; then echo yes ; else echo no; fi)
+ifeq ($(IS_GIT),yes)
+DATE:=$(shell git log --date=short -1 --pretty=format:%cd)
+else
+DATE:=$(shell date +%F)
+endif
+
VERSION:=$(shell cat ${srcdir}/version)
-ifneq ($(MAKECMDGOALS),release)
-ifneq ($(MAKECMDGOALS),release-message)
-ifneq ($(MAKECMDGOALS),pre-release)
+ifeq ($(filter release release-message pre-release update-versions,$(MAKECMDGOALS)),)
ifeq ($(IS_GIT),yes)
VERSION:=$(shell git describe --match '[0-9.]*'|sed -e s/_/~/ -e s/-/+/ -e s/-/~/)
endif
endif
-endif
-endif
UPSTREAM_TAG=$(subst ~,_,$(VERSION))
DEB_TAG=debian/$(UPSTREAM_TAG)-1
@@ -91,6 +93,12 @@ $(GPG_FILE): $(SHA1_FILE)
.PHONY: dist
dist: $(TAR_FILE)
+.PHONY: update-versions
+
+update-versions:
+ sed -i "s/^.TH NOTMUCH 1.*$$/.TH NOTMUCH 1 ${DATE} \"Notmuch ${VERSION}\"/" notmuch.1
+ sed -i "s/^__VERSION__[[:blank:]]*=.*$$/__VERSION__ = \'${VERSION}\'/" $(PV_FILE)
+
# We invoke make recursively only to force ordering of our phony
# targets in the case of parallel invocation of make (-j).
#
@@ -114,7 +122,7 @@ release: verify-source-tree-and-version
ifeq ($(REALLY_UPLOAD),yes)
git push origin $(VERSION)
cd releases && scp $(TAR_FILE) $(SHA1_FILE) $(GPG_FILE) $(RELEASE_HOST):$(RELEASE_DIR)
- ssh $(RELEASE_HOST) "rm -f $(RELEASE_DIR)/LATEST-$(PACKAGE)-[0-9]* ; ln -s $(TAR_FILE) $(RELEASE_DIR)/LATEST-$(PACKAGE)-$(VERSION)"
+ ssh $(RELEASE_HOST) "rm -f $(RELEASE_DIR)/LATEST-$(PACKAGE)-* ; ln -s $(TAR_FILE) $(RELEASE_DIR)/LATEST-$(TAR_FILE)"
endif
@echo "Please send a release announcement using $(PACKAGE)-$(VERSION).announce as a template."
@@ -179,7 +187,7 @@ release-message:
verify-source-tree-and-version: verify-no-dirty-code
.PHONY: verify-no-dirty-code
-verify-no-dirty-code: verify-version-debian verify-version-python
+verify-no-dirty-code: verify-version-debian verify-version-python verify-version-manpage
ifeq ($(IS_GIT),yes)
@printf "Checking that source tree is clean..."
ifneq ($(shell git ls-files -m),)
@@ -199,28 +207,33 @@ endif
.PHONY: verify-version-debian
verify-version-debian: verify-version-components
@echo -n "Checking that Debian package version is $(VERSION)-1..."
- @if [ "$(VERSION)-1" != $$(dpkg-parsechangelog | grep ^Version | awk '{print $$2}') ] ; then \
+ @[ "$(VERSION)-1" = $$(sed '1{ s/).*//; s/.*(//; q; }' debian/changelog) ] || \
(echo "No." && \
- echo "Please edit version and debian/changelog to have consistent versions." && false) \
- fi
+ echo "Please edit version and debian/changelog to have consistent versions." && false)
@echo "Good."
.PHONY: verify-version-python
verify-version-python: verify-version-components
@echo -n "Checking that python bindings version is $(VERSION)..."
- @if [ "$(VERSION)" != $$(python -c "execfile('$(PV_FILE)'); print __VERSION__") ] ; then \
+ @[ "$(VERSION)" = $$(python -c "execfile('$(PV_FILE)'); print __VERSION__") ] || \
+ (echo "No." && \
+ echo "Please edit version and $(PV_FILE) to have consistent versions." && false)
+ @echo "Good."
+
+.PHONY: verify-version-manpage
+verify-version-manpage: verify-version-components
+ @echo -n "Checking that manual page version is $(VERSION)..."
+ @[ "$(VERSION)" = $$(sed -n '/^[.]TH NOTMUCH 1/{s/.*"Notmuch //;s/".*//p;}' notmuch.1) ] || \
(echo "No." && \
- echo "Please edit version and $(PV_FILE) to have consistent versions." && false) \
- fi
+ echo "Please edit version and notmuch.1 to have consistent versions." && false)
@echo "Good."
.PHONY: verify-version-components
verify-version-components:
@echo -n "Checking that $(VERSION) consists only of digits and periods..."
- @if echo $(VERSION) | grep -q -v -x '[0-9.]*'; then \
+ @echo $(VERSION) | grep -q -x '^[0-9.]*$$' || \
(echo "No." && \
- echo "Please follow the instructions in RELEASING to choose a version" && false) \
- else :; fi
+ echo "Please follow the instructions in RELEASING to choose a version" && false)
@echo "Good."
.PHONY: verify-newer
diff --git a/NEWS b/NEWS
index 3f577e42..bb5e4d55 100644
--- a/NEWS
+++ b/NEWS
@@ -1,3 +1,16 @@
+Notmuch 0.11 (201x-xx-xx)
+=========================
+
+Performance
+-----------
+
+Automatic tag query optimization
+
+ "notmuch tag" now automatically optimizes the user's query to
+ exclude messages whose tags won't change. In the past, we've
+ suggested that people do this by hand; this is no longer necessary.
+
+
Notmuch 0.10.2 (2011-12-04)
===========================
diff --git a/RELEASING b/RELEASING
index e3e0cefe..88dab04e 100644
--- a/RELEASING
+++ b/RELEASING
@@ -62,11 +62,11 @@ repository. From here, there are just a few steps to release:
be "1.0.1" and a subsequent bug-fix release would be "1.0.2"
etc.
- Update bindings/python/notmuch/version.py to match version.
+ When you are happy with the file 'version', run
- Update the version in notmuch.1 to match version.
+ make update-versions
- XXX: Probably these last two steps should be (semi-)automated.
+ to propagate the version to the other places needed.
Commit these changes.
diff --git a/bindings/python/docs/source/index.rst b/bindings/python/docs/source/index.rst
index 73d2a3b0..f7d3d605 100644
--- a/bindings/python/docs/source/index.rst
+++ b/bindings/python/docs/source/index.rst
@@ -138,10 +138,10 @@ More information on specific topics can be found on the following pages:
.. method:: __len__()
- .. warning:: :meth:`__len__` was removed in version 0.6 as it exhausted
- the iterator and broke list(Messages()). Use the
- :meth:`Query.count_messages` function or use
- `len(list(msgs))`.
+ .. warning::
+
+ :meth:`__len__` was removed in version 0.6 as it exhausted the iterator and broke
+ list(Messages()). Use the :meth:`Query.count_messages` function or use `len(list(msgs))`.
:class:`Message` -- A single message
----------------------------------------
@@ -205,10 +205,11 @@ More information on specific topics can be found on the following pages:
.. method:: __len__
- .. warning:: :meth:`__len__` was removed in version 0.6 as it
- exhausted the iterator and broke list(Tags()). Use
- :meth:`len(list(msgs))` instead if you need to know the
- number of tags.
+ .. warning::
+
+ :meth:`__len__` was removed in version 0.6 as it exhausted the iterator and broke
+ list(Tags()). Use :meth:`len(list(msgs))` instead if you need to know the number of
+ tags.
.. automethod:: __str__
diff --git a/bindings/python/notmuch/__init__.py b/bindings/python/notmuch/__init__.py
index 539afedf..f3ff9874 100644
--- a/bindings/python/notmuch/__init__.py
+++ b/bindings/python/notmuch/__init__.py
@@ -69,7 +69,7 @@ from notmuch.globals import (
TagTooLongError,
UnbalancedFreezeThawError,
UnbalancedAtomicError,
- NotInitializedError
+ NotInitializedError,
)
from notmuch.version import __VERSION__
__LICENSE__ = "GPL v3+"
diff --git a/bindings/python/notmuch/database.py b/bindings/python/notmuch/database.py
index f4bc53e0..7923f768 100644
--- a/bindings/python/notmuch/database.py
+++ b/bindings/python/notmuch/database.py
@@ -18,13 +18,16 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
import os
-from ctypes import c_int, c_char_p, c_void_p, c_uint, c_long, byref
+from ctypes import c_char_p, c_void_p, c_uint, c_long, byref, POINTER
from notmuch.globals import (nmlib, STATUS, NotmuchError, NotInitializedError,
- NullPointerError, OutOfMemoryError, XapianError, Enum, _str)
+ NullPointerError, Enum, _str,
+ NotmuchDatabaseP, NotmuchDirectoryP, NotmuchMessageP, NotmuchTagsP,
+ NotmuchQueryP, NotmuchMessagesP, NotmuchThreadsP, NotmuchFilenamesP)
from notmuch.thread import Threads
from notmuch.message import Messages, Message
from notmuch.tag import Tags
+
class Database(object):
"""The :class:`Database` is the highest-level object that notmuch
provides. It references a notmuch database, and can be opened in
@@ -37,16 +40,19 @@ class Database(object):
:exc:`XapianError` as the underlying database has been
modified. Close and reopen the database to continue working with it.
- .. note:: Any function in this class can and will throw an
- :exc:`NotInitializedError` if the database was not
- intitialized properly.
+ .. note::
+
+ Any function in this class can and will throw an
+ :exc:`NotInitializedError` if the database was not intitialized
+ properly.
+
+ .. note::
- .. note:: Do remember that as soon as we tear down (e.g. via `del
- db`) this object, all underlying derived objects such as
- queries, threads, messages, tags etc will be freed by the
- underlying library as well. Accessing these objects will lead
- to segfaults and other unexpected behavior. See above for
- more details.
+ Do remember that as soon as we tear down (e.g. via `del db`) this
+ object, all underlying derived objects such as queries, threads,
+ messages, tags etc will be freed by the underlying library as well.
+ Accessing these objects will lead to segfaults and other unexpected
+ behavior. See above for more details.
"""
_std_db_path = None
"""Class attribute to cache user's default database"""
@@ -56,37 +62,50 @@ class Database(object):
"""notmuch_database_get_directory"""
_get_directory = nmlib.notmuch_database_get_directory
- _get_directory.restype = c_void_p
+ _get_directory.argtypes = [NotmuchDatabaseP, c_char_p]
+ _get_directory.restype = NotmuchDirectoryP
"""notmuch_database_get_path"""
_get_path = nmlib.notmuch_database_get_path
+ _get_path.argtypes = [NotmuchDatabaseP]
_get_path.restype = c_char_p
"""notmuch_database_get_version"""
_get_version = nmlib.notmuch_database_get_version
+ _get_version.argtypes = [NotmuchDatabaseP]
_get_version.restype = c_uint
"""notmuch_database_open"""
_open = nmlib.notmuch_database_open
- _open.restype = c_void_p
+ _open.argtypes = [c_char_p, c_uint]
+ _open.restype = NotmuchDatabaseP
"""notmuch_database_upgrade"""
_upgrade = nmlib.notmuch_database_upgrade
- _upgrade.argtypes = [c_void_p, c_void_p, c_void_p]
+ _upgrade.argtypes = [NotmuchDatabaseP, c_void_p, c_void_p]
+ _upgrade.restype = c_uint
""" notmuch_database_find_message"""
_find_message = nmlib.notmuch_database_find_message
+ _find_message.argtypes = [NotmuchDatabaseP, c_char_p,
+ POINTER(NotmuchMessageP)]
+ _find_message.restype = c_uint
"""notmuch_database_find_message_by_filename"""
_find_message_by_filename = nmlib.notmuch_database_find_message_by_filename
+ _find_message_by_filename.argtypes = [NotmuchDatabaseP, c_char_p,
+ POINTER(NotmuchMessageP)]
+ _find_message_by_filename.restype = c_uint
"""notmuch_database_get_all_tags"""
_get_all_tags = nmlib.notmuch_database_get_all_tags
- _get_all_tags.restype = c_void_p
+ _get_all_tags.argtypes = [NotmuchDatabaseP]
+ _get_all_tags.restype = NotmuchTagsP
"""notmuch_database_create"""
_create = nmlib.notmuch_database_create
- _create.restype = c_void_p
+ _create.argtypes = [c_char_p]
+ _create.restype = NotmuchDatabaseP
def __init__(self, path=None, create=False, mode=0):
"""If *path* is `None`, we will try to read a users notmuch
@@ -164,8 +183,8 @@ class Database(object):
:param status: Open the database in read-only or read-write mode
:type status: :attr:`MODE`
:returns: Nothing
- :exception: Raises :exc:`NotmuchError` in case
- of any failure (possibly after printing an error message on stderr).
+ :exception: Raises :exc:`NotmuchError` in case of any failure
+ (possibly after printing an error message on stderr).
"""
res = Database._open(_str(path), mode)
@@ -186,6 +205,10 @@ class Database(object):
self._assert_db_is_initialized()
return Database._get_version(self._db)
+ _needs_upgrade = nmlib.notmuch_database_needs_upgrade
+ _needs_upgrade.argtypes = [NotmuchDatabaseP]
+ _needs_upgrade.restype = bool
+
def needs_upgrade(self):
"""Does this database need to be upgraded before writing to it?
@@ -197,7 +220,7 @@ class Database(object):
:returns: `True` or `False`
"""
self._assert_db_is_initialized()
- return nmlib.notmuch_database_needs_upgrade(self._db)
+ return self._needs_upgrade(self._db)
def upgrade(self):
"""Upgrades the current database
@@ -219,6 +242,10 @@ class Database(object):
#TODO: catch exceptions, document return values and etc
return status
+ _begin_atomic = nmlib.notmuch_database_begin_atomic
+ _begin_atomic.argtypes = [NotmuchDatabaseP]
+ _begin_atomic.restype = c_uint
+
def begin_atomic(self):
"""Begin an atomic database operation
@@ -229,18 +256,20 @@ class Database(object):
neither begin nor end necessarily flush modifications to disk.
:returns: :attr:`STATUS`.SUCCESS or raises
-
- :exception: :exc:`NotmuchError`:
- :attr:`STATUS`.XAPIAN_EXCEPTION
- Xapian exception occurred; atomic section not entered.
+ :exception: :exc:`NotmuchError`: :attr:`STATUS`.XAPIAN_EXCEPTION
+ Xapian exception occurred; atomic section not entered.
*Added in notmuch 0.9*"""
self._assert_db_is_initialized()
- status = nmlib.notmuch_database_begin_atomic(self._db)
+ status = self._begin_atomic(self._db)
if status != STATUS.SUCCESS:
raise NotmuchError(status)
return status
+ _end_atomic = nmlib.notmuch_database_end_atomic
+ _end_atomic.argtypes = [NotmuchDatabaseP]
+ _end_atomic.restype = c_uint
+
def end_atomic(self):
"""Indicate the end of an atomic database operation
@@ -258,7 +287,7 @@ class Database(object):
*Added in notmuch 0.9*"""
self._assert_db_is_initialized()
- status = nmlib.notmuch_database_end_atomic(self._db)
+ status = self._end_atomic(self._db)
if status != STATUS.SUCCESS:
raise NotmuchError(status)
return status
@@ -267,13 +296,15 @@ class Database(object):
"""Returns a :class:`Directory` of path,
(creating it if it does not exist(?))
- .. warning:: This call needs a writeable database in
- :attr:`Database.MODE`.READ_WRITE mode. The underlying library will exit the
- program if this method is used on a read-only database!
+ .. warning::
+
+ This call needs a writeable database in
+ :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
+ exit the program if this method is used on a read-only database!
:param path: An unicode string containing the path relative to the path
- of database (see :meth:`get_path`), or else should be an absolute path
- with initial components that match the path of 'database'.
+ of database (see :meth:`get_path`), or else should be an absolute
+ path with initial components that match the path of 'database'.
:returns: :class:`Directory` or raises an exception.
:exception:
:exc:`NotmuchError` with :attr:`STATUS`.FILE_ERROR
@@ -299,6 +330,11 @@ class Database(object):
# return the Directory, init it with the absolute path
return Directory(_str(abs_dirpath), dir_p, self)
+ _add_message = nmlib.notmuch_database_add_message
+ _add_message.argtypes = [NotmuchDatabaseP, c_char_p,
+ POINTER(NotmuchMessageP)]
+ _add_message.restype = c_uint
+
def add_message(self, filename, sync_maildir_flags=False):
"""Adds a new message to the database
@@ -349,10 +385,8 @@ class Database(object):
be added.
"""
self._assert_db_is_initialized()
- msg_p = c_void_p()
- status = nmlib.notmuch_database_add_message(self._db,
- _str(filename),
- byref(msg_p))
+ msg_p = NotmuchMessageP()
+ status = self._add_message(self._db, _str(filename), byref(msg_p))
if not status in [STATUS.SUCCESS, STATUS.DUPLICATE_MESSAGE_ID]:
raise NotmuchError(status)
@@ -364,6 +398,10 @@ class Database(object):
msg.maildir_flags_to_tags()
return (msg, status)
+ _remove_message = nmlib.notmuch_database_remove_message
+ _remove_message.argtypes = [NotmuchDatabaseP, c_char_p]
+ _remove_message.restype = c_uint
+
def remove_message(self, filename):
"""Removes a message (filename) from the given notmuch database
@@ -392,8 +430,7 @@ class Database(object):
removed.
"""
self._assert_db_is_initialized()
- return nmlib.notmuch_database_remove_message(self._db,
- filename)
+ return self._remove_message(self._db, filename)
def find_message(self, msgid):
"""Returns a :class:`Message` as identified by its message ID
@@ -416,7 +453,7 @@ class Database(object):
the database was not intitialized.
"""
self._assert_db_is_initialized()
- msg_p = c_void_p()
+ msg_p = NotmuchMessageP()
status = Database._find_message(self._db, _str(msgid), byref(msg_p))
if status != STATUS.SUCCESS:
raise NotmuchError(status)
@@ -425,10 +462,11 @@ class Database(object):
def find_message_by_filename(self, filename):
"""Find a message with the given filename
- .. warning:: This call needs a writeable database in
- :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
- exit the program if this method is used on a read-only
- database!
+ .. warning::
+
+ This call needs a writeable database in
+ :attr:`Database.MODE`.READ_WRITE mode. The underlying library will
+ exit the program if this method is used on a read-only database!
:returns: If the database contains a message with the given
filename, then a class:`Message:` is returned. This
@@ -449,7 +487,7 @@ class Database(object):
*Added in notmuch 0.9*"""
self._assert_db_is_initialized()
- msg_p = c_void_p()
+ msg_p = NotmuchMessageP()
status = Database._find_message_by_filename(self._db, _str(filename),
byref(msg_p))
if status != STATUS.SUCCESS:
@@ -460,7 +498,8 @@ class Database(object):
"""Returns :class:`Tags` with a list of all tags found in the database
:returns: :class:`Tags`
- :execption: :exc:`NotmuchError` with :attr:`STATUS`.NULL_POINTER on error
+ :execption: :exc:`NotmuchError` with :attr:`STATUS`.NULL_POINTER
+ on error
"""
self._assert_db_is_initialized()
tags_p = Database._get_all_tags(self._db)
@@ -491,10 +530,14 @@ class Database(object):
def __repr__(self):
return "'Notmuch DB " + self.get_path() + "'"
+ _close = nmlib.notmuch_database_close
+ _close.argtypes = [NotmuchDatabaseP]
+ _close.restype = None
+
def __del__(self):
"""Close and free the notmuch database if needed"""
if self._db is not None:
- nmlib.notmuch_database_close(self._db)
+ self._close(self._db)
def _get_user_default_db(self):
""" Reads a user's notmuch config and returns his db location
@@ -545,18 +588,22 @@ class Query(object):
"""notmuch_query_create"""
_create = nmlib.notmuch_query_create
- _create.restype = c_void_p
+ _create.argtypes = [NotmuchDatabaseP, c_char_p]
+ _create.restype = NotmuchQueryP
"""notmuch_query_search_threads"""
_search_threads = nmlib.notmuch_query_search_threads
- _search_threads.restype = c_void_p
+ _search_threads.argtypes = [NotmuchQueryP]
+ _search_threads.restype = NotmuchThreadsP
"""notmuch_query_search_messages"""
_search_messages = nmlib.notmuch_query_search_messages
- _search_messages.restype = c_void_p
+ _search_messages.argtypes = [NotmuchQueryP]
+ _search_messages.restype = NotmuchMessagesP
"""notmuch_query_count_messages"""
_count_messages = nmlib.notmuch_query_count_messages
+ _count_messages.argtypes = [NotmuchQueryP]
_count_messages.restype = c_uint
def __init__(self, db, querystr):
@@ -602,6 +649,10 @@ class Query(object):
raise NullPointerError
self._query = query_p
+ _set_sort = nmlib.notmuch_query_set_sort
+ _set_sort.argtypes = [NotmuchQueryP, c_uint]
+ _set_sort.argtypes = None
+
def set_sort(self, sort):
"""Set the sort order future results will be delivered in
@@ -609,7 +660,7 @@ class Query(object):
"""
self._assert_query_is_initialized()
self.sort = sort
- nmlib.notmuch_query_set_sort(self._query, sort)
+ self._set_sort(self._query, sort)
def search_threads(self):
"""Execute a query for threads
@@ -661,10 +712,14 @@ class Query(object):
self._assert_query_is_initialized()
return Query._count_messages(self._query)
+ _destroy = nmlib.notmuch_query_destroy
+ _destroy.argtypes = [NotmuchQueryP]
+ _destroy.restype = None
+
def __del__(self):
"""Close and free the Query"""
if self._query is not None:
- nmlib.notmuch_query_destroy(self._query)
+ self._destroy(self._query)
class Directory(object):
@@ -683,22 +738,27 @@ class Directory(object):
"""notmuch_directory_get_mtime"""
_get_mtime = nmlib.notmuch_directory_get_mtime
+ _get_mtime.argtypes = [NotmuchDirectoryP]
_get_mtime.restype = c_long
"""notmuch_directory_set_mtime"""
_set_mtime = nmlib.notmuch_directory_set_mtime
- _set_mtime.argtypes = [c_char_p, c_long]
+ _set_mtime.argtypes = [NotmuchDirectoryP, c_long]
+ _set_mtime.restype = c_uint
"""notmuch_directory_get_child_files"""
_get_child_files = nmlib.notmuch_directory_get_child_files
- _get_child_files.restype = c_void_p
+ _get_child_files.argtypes = [NotmuchDirectoryP]
+ _get_child_files.restype = NotmuchFilenamesP
"""notmuch_directory_get_child_directories"""
_get_child_directories = nmlib.notmuch_directory_get_child_directories
- _get_child_directories.restype = c_void_p
+ _get_child_directories.argtypes = [NotmuchDirectoryP]
+ _get_child_directories.restype = NotmuchFilenamesP
def _assert_dir_is_initialized(self):
- """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED) if dir_p is None"""
+ """Raises a NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
+ if dir_p is None"""
if self._dir_p is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
@@ -735,10 +795,12 @@ class Directory(object):
and know that it only needs to add files if the mtime of the
directory and files are newer than the stored timestamp.
- .. note:: :meth:`get_mtime` function does not allow the caller
- to distinguish a timestamp of 0 from a non-existent
- timestamp. So don't store a timestamp of 0 unless you are
- comfortable with that.
+ .. note::
+
+ :meth:`get_mtime` function does not allow the caller to
+ distinguish a timestamp of 0 from a non-existent timestamp. So
+ don't store a timestamp of 0 unless you are comfortable with
+ that.
:param mtime: A (time_t) timestamp
:returns: Nothing on success, raising an exception on failure.
@@ -815,10 +877,14 @@ class Directory(object):
"""Object representation"""
return "<notmuch Directory object '%s'>" % self._path
+ _destroy = nmlib.notmuch_directory_destroy
+ _destroy.argtypes = [NotmuchDirectoryP]
+ _destroy.argtypes = None
+
def __del__(self):
"""Close and free the Directory"""
if self._dir_p is not None:
- nmlib.notmuch_directory_destroy(self._dir_p)
+ self._destroy(self._dir_p)
class Filenames(object):
@@ -826,6 +892,7 @@ class Filenames(object):
#notmuch_filenames_get
_get = nmlib.notmuch_filenames_get
+ _get.argtypes = [NotmuchFilenamesP]
_get.restype = c_char_p
def __init__(self, files_p, parent):
@@ -844,41 +911,56 @@ class Filenames(object):
""" Make Filenames an iterator """
return self
+ _valid = nmlib.notmuch_filenames_valid
+ _valid.argtypes = [NotmuchFilenamesP]
+ _valid.restype = bool
+
+ _move_to_next = nmlib.notmuch_filenames_move_to_next
+ _move_to_next.argtypes = [NotmuchFilenamesP]
+ _move_to_next.restype = None
+
def next(self):
if self._files_p is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- if not nmlib.notmuch_filenames_valid(self._files_p):
+ if not self._valid(self._files_p):
self._files_p = None
raise StopIteration
file = Filenames._get(self._files_p)
- nmlib.notmuch_filenames_move_to_next(self._files_p)
+ self._move_to_next(self._files_p)
return file
def __len__(self):
"""len(:class:`Filenames`) returns the number of contained files
- .. note:: As this iterates over the files, we will not be able to
- iterate over them again! So this will fail::
+ .. note::
+
+ As this iterates over the files, we will not be able to
+ iterate over them again! So this will fail::
#THIS FAILS
files = Database().get_directory('').get_child_files()
- if len(files) > 0: #this 'exhausts' msgs
- # next line raises NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)!!!
+ if len(files) > 0: # this 'exhausts' msgs
+ # next line raises
+ # NotmuchError(:attr:`STATUS`.NOT_INITIALIZED)
for file in files: print file
"""
if self._files_p is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
i = 0
- while nmlib.notmuch_filenames_valid(self._files_p):
- nmlib.notmuch_filenames_move_to_next(self._files_p)
+ while self._valid(self._files_p):
+ self._move_to_next(self._files_p)
i += 1
self._files_p = None
return i
+ _destroy = nmlib.notmuch_filenames_destroy
+ _destroy.argtypes = [NotmuchFilenamesP]
+ _destroy.restype = None
+
def __del__(self):
"""Close and free Filenames"""
if self._files_p is not None:
- nmlib.notmuch_filenames_destroy(self._files_p)
+ self._destroy(self._files_p)
diff --git a/bindings/python/notmuch/filename.py b/bindings/python/notmuch/filename.py
index de4d785a..a7cd7e63 100644
--- a/bindings/python/notmuch/filename.py
+++ b/bindings/python/notmuch/filename.py
@@ -17,7 +17,8 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
from ctypes import c_char_p
-from notmuch.globals import nmlib, STATUS, NotmuchError
+from notmuch.globals import (nmlib, STATUS, NotmuchError,
+ NotmuchFilenamesP, NotmuchMessageP)
class Filenames(object):
@@ -50,6 +51,7 @@ class Filenames(object):
#notmuch_filenames_get
_get = nmlib.notmuch_filenames_get
+ _get.argtypes = [NotmuchFilenamesP]
_get.restype = c_char_p
def __init__(self, files_p, parent):
@@ -74,6 +76,14 @@ class Filenames(object):
#save reference to parent object so we keep it alive
self._parent = parent
+ _valid = nmlib.notmuch_filenames_valid
+ _valid.argtypes = [NotmuchFilenamesP]
+ _valid.restype = bool
+
+ _move_to_next = nmlib.notmuch_filenames_move_to_next
+ _move_to_next.argtypes = [NotmuchFilenamesP]
+ _move_to_next.restype = None
+
def as_generator(self):
"""Return generator of Filenames
@@ -82,13 +92,16 @@ class Filenames(object):
if self._files is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- while nmlib.notmuch_filenames_valid(self._files):
+ while self._valid(self._files):
yield Filenames._get(self._files)
- nmlib.notmuch_filenames_move_to_next(self._files)
+ self._move_to_next(self._files)
self._files = None
def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
"""Represent Filenames() as newline-separated list of full paths
.. note:: As this iterates over the filenames, we will not be
@@ -101,7 +114,11 @@ class Filenames(object):
"""
return "\n".join(self)
+ _destroy = nmlib.notmuch_filenames_destroy
+ _destroy.argtypes = [NotmuchMessageP]
+ _destroy.restype = None
+
def __del__(self):
"""Close and free the notmuch filenames"""
if self._files is not None:
- nmlib.notmuch_filenames_destroy(self._files)
+ self._destroy(self._files)
diff --git a/bindings/python/notmuch/globals.py b/bindings/python/notmuch/globals.py
index de1db161..54a49b2d 100644
--- a/bindings/python/notmuch/globals.py
+++ b/bindings/python/notmuch/globals.py
@@ -17,8 +17,7 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
-from ctypes import CDLL, c_char_p, c_int
-from ctypes.util import find_library
+from ctypes import CDLL, c_char_p, c_int, Structure, POINTER
#-----------------------------------------------------------------------------
#package-global instance of the notmuch library
@@ -49,11 +48,11 @@ class Status(Enum):
@classmethod
def status2str(self, status):
- """Get a string representation of a notmuch_status_t value."""
+ """Get a (unicode) string representation of a notmuch_status_t value."""
# define strings for custom error messages
if status == STATUS.NOT_INITIALIZED:
- return "Operation on uninitialized object impossible."
- return str(Status._status2str(status))
+ return u"Operation on uninitialized object impossible."
+ return unicode(Status._status2str(status))
STATUS = Status(['SUCCESS',
'OUT_OF_MEMORY',
@@ -89,6 +88,7 @@ Invoke the class method `notmuch.STATUS.status2str` with a status value as
argument to receive a human readable string"""
STATUS.__name__ = 'STATUS'
+
class NotmuchError(Exception):
"""Is initiated with a (notmuch.STATUS[, message=None]). It will not
return an instance of the class NotmuchError, but a derived instance
@@ -97,7 +97,8 @@ class NotmuchError(Exception):
@classmethod
def get_exc_subclass(cls, status):
- """Returns a fine grained Exception() type,detailing the error status"""
+ """Returns a fine grained Exception() type,
+ detailing the error status"""
subclasses = {
STATUS.OUT_OF_MEMORY: OutOfMemoryError,
STATUS.READ_ONLY_DATABASE: ReadOnlyDatabaseError,
@@ -109,7 +110,7 @@ class NotmuchError(Exception):
STATUS.TAG_TOO_LONG: TagTooLongError,
STATUS.UNBALANCED_FREEZE_THAW: UnbalancedFreezeThawError,
STATUS.UNBALANCED_ATOMIC: UnbalancedAtomicError,
- STATUS.NOT_INITIALIZED: NotInitializedError
+ STATUS.NOT_INITIALIZED: NotInitializedError,
}
assert 0 < status <= len(subclasses)
return subclasses[status]
@@ -125,7 +126,7 @@ class NotmuchError(Exception):
# no 'status' or cls is subclass already, return 'cls' instance
if not status or cls != NotmuchError:
return super(NotmuchError, cls).__new__(cls)
- subclass = cls.get_exc_subclass(status) # which class to use?
+ subclass = cls.get_exc_subclass(status) # which class to use?
return subclass.__new__(subclass, *args, **kwargs)
def __init__(self, status=None, message=None):
@@ -133,35 +134,59 @@ class NotmuchError(Exception):
self.message = message
def __str__(self):
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
if self.message is not None:
return self.message
elif self.status is not None:
return STATUS.status2str(self.status)
else:
- return 'Unknown error'
+ return u'Unknown error'
+
# List of Subclassed exceptions that correspond to STATUS values and are
# subclasses of NotmuchError.
class OutOfMemoryError(NotmuchError):
status = STATUS.OUT_OF_MEMORY
+
+
class ReadOnlyDatabaseError(NotmuchError):
status = STATUS.READ_ONLY_DATABASE
+
+
class XapianError(NotmuchError):
status = STATUS.XAPIAN_EXCEPTION
+
+
class FileError(NotmuchError):
status = STATUS.FILE_ERROR
+
+
class FileNotEmailError(NotmuchError):
status = STATUS.FILE_NOT_EMAIL
+
+
class DuplicateMessageIdError(NotmuchError):
status = STATUS.DUPLICATE_MESSAGE_ID
+
+
class NullPointerError(NotmuchError):
status = STATUS.NULL_POINTER
+
+
class TagTooLongError(NotmuchError):
status = STATUS.TAG_TOO_LONG
+
+
class UnbalancedFreezeThawError(NotmuchError):
status = STATUS.UNBALANCED_FREEZE_THAW
+
+
class UnbalancedAtomicError(NotmuchError):
status = STATUS.UNBALANCED_ATOMIC
+
+
class NotInitializedError(NotmuchError):
"""Derived from NotmuchError, this occurs if the underlying data
structure (e.g. database is not initialized (yet) or an iterator has
@@ -170,7 +195,6 @@ class NotInitializedError(NotmuchError):
status = STATUS.NOT_INITIALIZED
-
def _str(value):
"""Ensure a nicely utf-8 encoded string to pass to libnotmuch
@@ -182,3 +206,47 @@ def _str(value):
return value.encode('UTF-8')
return value
+
+class NotmuchDatabaseS(Structure):
+ pass
+NotmuchDatabaseP = POINTER(NotmuchDatabaseS)
+
+
+class NotmuchQueryS(Structure):
+ pass
+NotmuchQueryP = POINTER(NotmuchQueryS)
+
+
+class NotmuchThreadsS(Structure):
+ pass
+NotmuchThreadsP = POINTER(NotmuchThreadsS)
+
+
+class NotmuchThreadS(Structure):
+ pass
+NotmuchThreadP = POINTER(NotmuchThreadS)
+
+
+class NotmuchMessagesS(Structure):
+ pass
+NotmuchMessagesP = POINTER(NotmuchMessagesS)
+
+
+class NotmuchMessageS(Structure):
+ pass
+NotmuchMessageP = POINTER(NotmuchMessageS)
+
+
+class NotmuchTagsS(Structure):
+ pass
+NotmuchTagsP = POINTER(NotmuchTagsS)
+
+
+class NotmuchDirectoryS(Structure):
+ pass
+NotmuchDirectoryP = POINTER(NotmuchDirectoryS)
+
+
+class NotmuchFilenamesS(Structure):
+ pass
+NotmuchFilenamesP = POINTER(NotmuchFilenamesS)
diff --git a/bindings/python/notmuch/message.py b/bindings/python/notmuch/message.py
index 4bf90c22..ce8e7181 100644
--- a/bindings/python/notmuch/message.py
+++ b/bindings/python/notmuch/message.py
@@ -19,14 +19,14 @@ Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
-from ctypes import c_char_p, c_void_p, c_long, c_uint, c_int
+from ctypes import c_char_p, c_long, c_uint, c_int
from datetime import date
-from notmuch.globals import nmlib, STATUS, NotmuchError, Enum, _str
+from notmuch.globals import (nmlib, STATUS, NotmuchError, Enum, _str,
+ NotmuchTagsP, NotmuchMessagesP, NotmuchMessageP, NotmuchFilenamesP)
from notmuch.tag import Tags
from notmuch.filename import Filenames
import sys
import email
-import types
try:
import simplejson as json
except ImportError:
@@ -92,10 +92,12 @@ class Messages(object):
#notmuch_messages_get
_get = nmlib.notmuch_messages_get
- _get.restype = c_void_p
+ _get.argtypes = [NotmuchMessagesP]
+ _get.restype = NotmuchMessageP
_collect_tags = nmlib.notmuch_messages_collect_tags
- _collect_tags.restype = c_void_p
+ _collect_tags.argtypes = [NotmuchMessagesP]
+ _collect_tags.restype = NotmuchTagsP
def __init__(self, msgs_p, parent=None):
"""
@@ -125,10 +127,12 @@ class Messages(object):
"""Return the unique :class:`Tags` in the contained messages
:returns: :class:`Tags`
- :exceptions: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if not inited
+ :exceptions: :exc:`NotmuchError` STATUS.NOT_INITIALIZED if not init'ed
- .. note:: :meth:`collect_tags` will iterate over the messages and
- therefore will not allow further iterations.
+ .. note::
+
+ :meth:`collect_tags` will iterate over the messages and therefore
+ will not allow further iterations.
"""
if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
@@ -146,16 +150,24 @@ class Messages(object):
""" Make Messages an iterator """
return self
+ _valid = nmlib.notmuch_messages_valid
+ _valid.argtypes = [NotmuchMessagesP]
+ _valid.restype = bool
+
+ _move_to_next = nmlib.notmuch_messages_move_to_next
+ _move_to_next.argtypes = [NotmuchMessagesP]
+ _move_to_next.restype = None
+
def next(self):
if self._msgs is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- if not nmlib.notmuch_messages_valid(self._msgs):
+ if not self._valid(self._msgs):
self._msgs = None
raise StopIteration
msg = Message(Messages._get(self._msgs), self)
- nmlib.notmuch_messages_move_to_next(self._msgs)
+ self._move_to_next(self._msgs)
return msg
def __nonzero__(self):
@@ -163,12 +175,16 @@ class Messages(object):
:return: True if there is at least one more thread in the
Iterator, False if not."""
return self._msgs is not None and \
- nmlib.notmuch_messages_valid(self._msgs) > 0
+ self._valid(self._msgs) > 0
+
+ _destroy = nmlib.notmuch_messages_destroy
+ _destroy.argtypes = [NotmuchMessagesP]
+ _destroy.restype = None
def __del__(self):
"""Close and free the notmuch Messages"""
if self._msgs is not None:
- nmlib.notmuch_messages_destroy(self._msgs)
+ self._destroy(self._msgs)
def print_messages(self, format, indent=0, entire_thread=False):
"""Outputs messages as needed for 'notmuch show' to sys.stdout
@@ -235,44 +251,60 @@ class Message(object):
"""notmuch_message_get_filename (notmuch_message_t *message)"""
_get_filename = nmlib.notmuch_message_get_filename
+ _get_filename.argtypes = [NotmuchMessageP]
_get_filename.restype = c_char_p
"""return all filenames for a message"""
_get_filenames = nmlib.notmuch_message_get_filenames
- _get_filenames.restype = c_void_p
+ _get_filenames.argtypes = [NotmuchMessageP]
+ _get_filenames.restype = NotmuchFilenamesP
"""notmuch_message_get_flag"""
_get_flag = nmlib.notmuch_message_get_flag
- _get_flag.restype = c_uint
+ _get_flag.argtypes = [NotmuchMessageP, c_uint]
+ _get_flag.restype = bool
+
+ """notmuch_message_set_flag"""
+ _set_flag = nmlib.notmuch_message_set_flag
+ _set_flag.argtypes = [NotmuchMessageP, c_uint, c_int]
+ _set_flag.restype = None
"""notmuch_message_get_message_id (notmuch_message_t *message)"""
_get_message_id = nmlib.notmuch_message_get_message_id
+ _get_message_id.argtypes = [NotmuchMessageP]
_get_message_id.restype = c_char_p
"""notmuch_message_get_thread_id"""
_get_thread_id = nmlib.notmuch_message_get_thread_id
+ _get_thread_id.argtypes = [NotmuchMessageP]
_get_thread_id.restype = c_char_p
"""notmuch_message_get_replies"""
_get_replies = nmlib.notmuch_message_get_replies
- _get_replies.restype = c_void_p
+ _get_replies.argtypes = [NotmuchMessageP]
+ _get_replies.restype = NotmuchMessagesP
"""notmuch_message_get_tags (notmuch_message_t *message)"""
_get_tags = nmlib.notmuch_message_get_tags
- _get_tags.restype = c_void_p
+ _get_tags.argtypes = [NotmuchMessageP]
+ _get_tags.restype = NotmuchTagsP
_get_date = nmlib.notmuch_message_get_date
+ _get_date.argtypes = [NotmuchMessageP]
_get_date.restype = c_long
_get_header = nmlib.notmuch_message_get_header
+ _get_header.argtypes = [NotmuchMessageP, c_char_p]
_get_header.restype = c_char_p
"""notmuch_status_t ..._maildir_flags_to_tags (notmuch_message_t *)"""
_tags_to_maildir_flags = nmlib.notmuch_message_tags_to_maildir_flags
+ _tags_to_maildir_flags.argtypes = [NotmuchMessageP]
_tags_to_maildir_flags.restype = c_int
"""notmuch_status_t ..._tags_to_maildir_flags (notmuch_message_t *)"""
_maildir_flags_to_tags = nmlib.notmuch_message_maildir_flags_to_tags
+ _maildir_flags_to_tags.argtypes = [NotmuchMessageP]
_maildir_flags_to_tags.restype = c_int
#Constants: Flags that can be set/get with set_flag
@@ -328,14 +360,15 @@ class Message(object):
"""Gets all direct replies to this message as :class:`Messages`
iterator
- .. note:: This call only makes sense if 'message' was
- ultimately obtained from a :class:`Thread` object, (such as
- by coming directly from the result of calling
- :meth:`Thread.get_toplevel_messages` or by any number of
- subsequent calls to :meth:`get_replies`). If this message was
- obtained through some non-thread means, (such as by a call
- to :meth:`Query.search_messages`), then this function will
- return `None`.
+ .. note::
+
+ This call only makes sense if 'message' was ultimately obtained from
+ a :class:`Thread` object, (such as by coming directly from the
+ result of calling :meth:`Thread.get_toplevel_messages` or by any
+ number of subsequent calls to :meth:`get_replies`). If this message
+ was obtained through some non-thread means, (such as by a call to
+ :meth:`Query.search_messages`), then this function will return
+ `None`.
:returns: :class:`Messages` or `None` if there are no replies to
this message.
@@ -394,7 +427,7 @@ class Message(object):
header = Message._get_header(self._msg, header)
if header == None:
raise NotmuchError(STATUS.NULL_POINTER)
- return header.decode('UTF-8')
+ return header.decode('UTF-8', errors='ignore')
def get_filename(self):
"""Returns the file path of the message file
@@ -450,7 +483,7 @@ class Message(object):
"""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- nmlib.notmuch_message_set_flag(self._msg, flag, value)
+ self._set_flag(self._msg, flag, value)
def get_tags(self):
"""Returns the message tags
@@ -470,6 +503,10 @@ class Message(object):
raise NotmuchError(STATUS.NULL_POINTER)
return Tags(tags_p, self)
+ _add_tag = nmlib.notmuch_message_add_tag
+ _add_tag.argtypes = [NotmuchMessageP, c_char_p]
+ _add_tag.restype = c_uint
+
def add_tag(self, tag, sync_maildir_flags=False):
"""Adds a tag to the given message
@@ -504,7 +541,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = nmlib.notmuch_message_add_tag(self._msg, _str(tag))
+ status = self._add_tag(self._msg, _str(tag))
# bail out on failure
if status != STATUS.SUCCESS:
@@ -514,6 +551,10 @@ class Message(object):
self.tags_to_maildir_flags()
return STATUS.SUCCESS
+ _remove_tag = nmlib.notmuch_message_remove_tag
+ _remove_tag.argtypes = [NotmuchMessageP, c_char_p]
+ _remove_tag.restype = c_uint
+
def remove_tag(self, tag, sync_maildir_flags=False):
"""Removes a tag from the given message
@@ -548,7 +589,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = nmlib.notmuch_message_remove_tag(self._msg, _str(tag))
+ status = self._remove_tag(self._msg, _str(tag))
# bail out on error
if status != STATUS.SUCCESS:
raise NotmuchError(status)
@@ -557,6 +598,10 @@ class Message(object):
self.tags_to_maildir_flags()
return STATUS.SUCCESS
+ _remove_all_tags = nmlib.notmuch_message_remove_all_tags
+ _remove_all_tags.argtypes = [NotmuchMessageP]
+ _remove_all_tags.restype = c_uint
+
def remove_all_tags(self, sync_maildir_flags=False):
"""Removes all tags from the given message.
@@ -585,7 +630,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = nmlib.notmuch_message_remove_all_tags(self._msg)
+ status = self._remove_all_tags(self._msg)
# bail out on error
if status != STATUS.SUCCESS:
@@ -595,12 +640,16 @@ class Message(object):
self.tags_to_maildir_flags()
return STATUS.SUCCESS
+ _freeze = nmlib.notmuch_message_freeze
+ _freeze.argtypes = [NotmuchMessageP]
+ _freeze.restype = c_uint
+
def freeze(self):
"""Freezes the current state of 'message' within the database
This means that changes to the message state, (via :meth:`add_tag`,
:meth:`remove_tag`, and :meth:`remove_all_tags`), will not be
- committed to the database until the message is :meth:`thaw`ed.
+ committed to the database until the message is :meth:`thaw` ed.
Multiple calls to freeze/thaw are valid and these calls will
"stack". That is there must be as many calls to thaw as to freeze
@@ -639,7 +688,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = nmlib.notmuch_message_freeze(self._msg)
+ status = self._freeze(self._msg)
if STATUS.SUCCESS == status:
# return on success
@@ -647,6 +696,10 @@ class Message(object):
raise NotmuchError(status)
+ _thaw = nmlib.notmuch_message_thaw
+ _thaw.argtypes = [NotmuchMessageP]
+ _thaw.restype = c_uint
+
def thaw(self):
"""Thaws the current 'message'
@@ -674,7 +727,7 @@ class Message(object):
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = nmlib.notmuch_message_thaw(self._msg)
+ status = self._thaw(self._msg)
if STATUS.SUCCESS == status:
# return on success
@@ -705,11 +758,11 @@ class Message(object):
not work yet, as the modified tags have not been committed yet
to the database.
- :returns: a :class:`STATUS`. In short, you want to see
+ :returns: a :class:`STATUS` value. In short, you want to see
notmuch.STATUS.SUCCESS here. See there for details."""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = Message._tags_to_maildir_flags(self._msg)
+ return Message._tags_to_maildir_flags(self._msg)
def maildir_flags_to_tags(self):
"""Synchronize file Maildir flags to notmuch tags
@@ -736,19 +789,21 @@ class Message(object):
notmuch.STATUS.SUCCESS here. See there for details."""
if self._msg is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- status = Message._tags_to_maildir_flags(self._msg)
+ return Message._tags_to_maildir_flags(self._msg)
def __repr__(self):
"""Represent a Message() object by str()"""
return self.__str__()
def __str__(self):
- """A message() is represented by a 1-line summary"""
- msg = {}
- msg['from'] = self.get_header('from')
- msg['tags'] = self.get_tags()
- msg['date'] = date.fromtimestamp(self.get_date())
- return "%(from)s (%(date)s) (%(tags)s)" % (msg)
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ format = "%s (%s) (%s)"
+ return format % (self.get_header('from'),
+ self.get_tags(),
+ date.fromtimestamp(self.get_date()),
+ )
def get_message_parts(self):
"""Output like notmuch show"""
@@ -896,7 +951,11 @@ class Message(object):
res = cmp(list(self.get_filenames()), list(other.get_filenames()))
return res
+ _destroy = nmlib.notmuch_message_destroy
+ _destroy.argtypes = [NotmuchMessageP]
+ _destroy.restype = None
+
def __del__(self):
"""Close and free the notmuch Message"""
if self._msg is not None:
- nmlib.notmuch_message_destroy(self._msg)
+ self._destroy(self._msg)
diff --git a/bindings/python/notmuch/tag.py b/bindings/python/notmuch/tag.py
index 50e3686b..2fb7d328 100644
--- a/bindings/python/notmuch/tag.py
+++ b/bindings/python/notmuch/tag.py
@@ -17,7 +17,7 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
from ctypes import c_char_p
-from notmuch.globals import nmlib, STATUS, NotmuchError
+from notmuch.globals import nmlib, STATUS, NotmuchError, NotmuchTagsP
class Tags(object):
@@ -50,6 +50,7 @@ class Tags(object):
#notmuch_tags_get
_get = nmlib.notmuch_tags_get
+ _get.argtypes = [NotmuchTagsP]
_get.restype = c_char_p
def __init__(self, tags_p, parent=None):
@@ -80,14 +81,22 @@ class Tags(object):
""" Make Tags an iterator """
return self
+ _valid = nmlib.notmuch_tags_valid
+ _valid.argtypes = [NotmuchTagsP]
+ _valid.restype = bool
+
+ _move_to_next = nmlib.notmuch_tags_move_to_next
+ _move_to_next.argtypes = [NotmuchTagsP]
+ _move_to_next.restype = None
+
def next(self):
if self._tags is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- if not nmlib.notmuch_tags_valid(self._tags):
+ if not self._valid(self._tags):
self._tags = None
raise StopIteration
tag = Tags._get(self._tags).decode('UTF-8')
- nmlib.notmuch_tags_move_to_next(self._tags)
+ self._move_to_next(self._tags)
return tag
def __nonzero__(self):
@@ -99,20 +108,28 @@ class Tags(object):
:returns: True if the Tags() iterator has at least one more Tag
left."""
- return nmlib.notmuch_tags_valid(self._tags) > 0
+ return self._valid(self._tags) > 0
def __str__(self):
- """The str() representation of Tags() is a space separated list of tags
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ """string representation of :class:`Tags`: a space separated list of tags
- .. note:: As this iterates over the tags, we will not be able
- to iterate over them again (as in retrieve them)! If
- the tags have been exhausted already, this will raise a
- :exc:`NotmuchError` STATUS.NOT_INITIALIZED on
- subsequent attempts.
+ .. note::
+
+ As this iterates over the tags, we will not be able to iterate over
+ them again (as in retrieve them)! If the tags have been exhausted
+ already, this will raise a :exc:`NotmuchError`
+ STATUS.NOT_INITIALIZED on subsequent attempts.
"""
return " ".join(self)
+ _destroy = nmlib.notmuch_tags_destroy
+ _destroy.argtypes = [NotmuchTagsP]
+ _destroy.restype = None
+
def __del__(self):
"""Close and free the notmuch tags"""
if self._tags is not None:
- nmlib.notmuch_tags_destroy(self._tags)
+ self._destroy(self._tags)
diff --git a/bindings/python/notmuch/thread.py b/bindings/python/notmuch/thread.py
index 5e08eb31..5058846d 100644
--- a/bindings/python/notmuch/thread.py
+++ b/bindings/python/notmuch/thread.py
@@ -17,8 +17,10 @@ along with notmuch. If not, see <http://www.gnu.org/licenses/>.
Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
"""
-from ctypes import c_char_p, c_void_p, c_long
-from notmuch.globals import nmlib, STATUS, NotmuchError
+from ctypes import c_char_p, c_long, c_int
+from notmuch.globals import (nmlib, STATUS,
+ NotmuchError, NotmuchThreadP, NotmuchThreadsP, NotmuchMessagesP,
+ NotmuchTagsP,)
from notmuch.message import Messages
from notmuch.tag import Tags
from datetime import date
@@ -75,7 +77,8 @@ class Threads(object):
#notmuch_threads_get
_get = nmlib.notmuch_threads_get
- _get.restype = c_void_p
+ _get.argtypes = [NotmuchThreadsP]
+ _get.restype = NotmuchThreadP
def __init__(self, threads_p, parent=None):
"""
@@ -105,16 +108,24 @@ class Threads(object):
""" Make Threads an iterator """
return self
+ _valid = nmlib.notmuch_threads_valid
+ _valid.argtypes = [NotmuchThreadsP]
+ _valid.restype = bool
+
+ _move_to_next = nmlib.notmuch_threads_move_to_next
+ _move_to_next.argtypes = [NotmuchThreadsP]
+ _move_to_next.restype = None
+
def next(self):
if self._threads is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- if not nmlib.notmuch_threads_valid(self._threads):
+ if not self._valid(self._threads):
self._threads = None
raise StopIteration
thread = Thread(Threads._get(self._threads), self)
- nmlib.notmuch_threads_move_to_next(self._threads)
+ self._move_to_next(self._threads)
return thread
def __len__(self):
@@ -134,8 +145,8 @@ class Threads(object):
i = 0
# returns 'bool'. On out-of-memory it returns None
- while nmlib.notmuch_threads_valid(self._threads):
- nmlib.notmuch_threads_move_to_next(self._threads)
+ while self._valid(self._threads):
+ self._move_to_next(self._threads)
i += 1
# reset self._threads to mark as "exhausted"
self._threads = None
@@ -153,12 +164,16 @@ class Threads(object):
Iterator, False if not. None on a "Out-of-memory" error.
"""
return self._threads is not None and \
- nmlib.notmuch_threads_valid(self._threads) > 0
+ self._valid(self._threads) > 0
+
+ _destroy = nmlib.notmuch_threads_destroy
+ _destroy.argtypes = [NotmuchThreadsP]
+ _destroy.argtypes = None
def __del__(self):
"""Close and free the notmuch Threads"""
if self._threads is not None:
- nmlib.notmuch_messages_destroy(self._threads)
+ self._destroy(self._threads)
class Thread(object):
@@ -166,29 +181,36 @@ class Thread(object):
"""notmuch_thread_get_thread_id"""
_get_thread_id = nmlib.notmuch_thread_get_thread_id
+ _get_thread_id.argtypes = [NotmuchThreadP]
_get_thread_id.restype = c_char_p
"""notmuch_thread_get_authors"""
_get_authors = nmlib.notmuch_thread_get_authors
+ _get_authors.argtypes = [NotmuchThreadP]
_get_authors.restype = c_char_p
"""notmuch_thread_get_subject"""
_get_subject = nmlib.notmuch_thread_get_subject
+ _get_subject.argtypes = [NotmuchThreadP]
_get_subject.restype = c_char_p
"""notmuch_thread_get_toplevel_messages"""
_get_toplevel_messages = nmlib.notmuch_thread_get_toplevel_messages
- _get_toplevel_messages.restype = c_void_p
+ _get_toplevel_messages.argtypes = [NotmuchThreadP]
+ _get_toplevel_messages.restype = NotmuchMessagesP
_get_newest_date = nmlib.notmuch_thread_get_newest_date
+ _get_newest_date.argtypes = [NotmuchThreadP]
_get_newest_date.restype = c_long
_get_oldest_date = nmlib.notmuch_thread_get_oldest_date
+ _get_oldest_date.argtypes = [NotmuchThreadP]
_get_oldest_date.restype = c_long
"""notmuch_thread_get_tags"""
_get_tags = nmlib.notmuch_thread_get_tags
- _get_tags.restype = c_void_p
+ _get_tags.argtypes = [NotmuchThreadP]
+ _get_tags.restype = NotmuchTagsP
def __init__(self, thread_p, parent=None):
"""
@@ -225,6 +247,10 @@ class Thread(object):
raise NotmuchError(STATUS.NOT_INITIALIZED)
return Thread._get_thread_id(self._thread)
+ _get_total_messages = nmlib.notmuch_thread_get_total_messages
+ _get_total_messages.argtypes = [NotmuchThreadP]
+ _get_total_messages.restype = c_int
+
def get_total_messages(self):
"""Get the total number of messages in 'thread'
@@ -236,7 +262,7 @@ class Thread(object):
"""
if self._thread is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- return nmlib.notmuch_thread_get_total_messages(self._thread)
+ return self._get_total_messages(self._thread)
def get_toplevel_messages(self):
"""Returns a :class:`Messages` iterator for the top-level messages in
@@ -267,6 +293,10 @@ class Thread(object):
return Messages(msgs_p, self)
+ _get_matched_messages = nmlib.notmuch_thread_get_matched_messages
+ _get_matched_messages.argtypes = [NotmuchThreadP]
+ _get_matched_messages.restype = c_int
+
def get_matched_messages(self):
"""Returns the number of messages in 'thread' that matched the query
@@ -278,7 +308,7 @@ class Thread(object):
"""
if self._thread is None:
raise NotmuchError(STATUS.NOT_INITIALIZED)
- return nmlib.notmuch_thread_get_matched_messages(self._thread)
+ return self._get_matched_messages(self._thread)
def get_authors(self):
"""Returns the authors of 'thread'
@@ -295,7 +325,7 @@ class Thread(object):
authors = Thread._get_authors(self._thread)
if authors is None:
return None
- return authors.decode('UTF-8')
+ return authors.decode('UTF-8', errors='ignore')
def get_subject(self):
"""Returns the Subject of 'thread'
@@ -308,7 +338,7 @@ class Thread(object):
subject = Thread._get_subject(self._thread)
if subject is None:
return None
- return subject.decode('UTF-8')
+ return subject.decode('UTF-8', errors='ignore')
def get_newest_date(self):
"""Returns time_t of the newest message date
@@ -362,32 +392,25 @@ class Thread(object):
return Tags(tags_p, self)
def __str__(self):
- """A str(Thread()) is represented by a 1-line summary"""
- thread = {}
- thread['id'] = self.get_thread_id()
-
- ###TODO: How do we find out the current sort order of Threads?
- ###Add a "sort" attribute to the Threads() object?
- #if (sort == NOTMUCH_SORT_OLDEST_FIRST)
- # date = notmuch_thread_get_oldest_date (thread);
- #else
- # date = notmuch_thread_get_newest_date (thread);
- thread['date'] = date.fromtimestamp(self.get_newest_date())
- thread['matched'] = self.get_matched_messages()
- thread['total'] = self.get_total_messages()
- thread['authors'] = self.get_authors()
- thread['subject'] = self.get_subject()
- thread['tags'] = self.get_tags()
-
- return "thread:%s %12s [%d/%d] %s; %s (%s)" % (thread['id'],
- thread['date'],
- thread['matched'],
- thread['total'],
- thread['authors'],
- thread['subject'],
- thread['tags'])
+ return unicode(self).encode('utf-8')
+
+ def __unicode__(self):
+ frm = "thread:%s %12s [%d/%d] %s; %s (%s)"
+
+ return frm % (self.get_thread_id(),
+ date.fromtimestamp(self.get_newest_date()),
+ self.get_matched_messages(),
+ self.get_total_messages(),
+ self.get_authors(),
+ self.get_subject(),
+ self.get_tags(),
+ )
+
+ _destroy = nmlib.notmuch_thread_destroy
+ _destroy.argtypes = [NotmuchThreadP]
+ _destroy.restype = None
def __del__(self):
"""Close and free the notmuch Thread"""
if self._thread is not None:
- nmlib.notmuch_thread_destroy(self._thread)
+ self._destroy(self._thread)
diff --git a/contrib/.gitattributes b/contrib/.gitattributes
deleted file mode 100644
index 3d57a081..00000000
--- a/contrib/.gitattributes
+++ /dev/null
@@ -1 +0,0 @@
-notmuch-deliver export-ignore
diff --git a/contrib/notmuch-deliver/COPYING b/contrib/notmuch-deliver/COPYING
deleted file mode 100644
index 3912109b..00000000
--- a/contrib/notmuch-deliver/COPYING
+++ /dev/null
@@ -1,340 +0,0 @@
- GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.
- 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Library General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- <one line to give the program's name and a brief idea of what it does.>
- Copyright (C) <year> <name of author>
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
-
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- <signature of Ty Coon>, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Library General
-Public License instead of this License.
diff --git a/contrib/notmuch-deliver/README.mkd b/contrib/notmuch-deliver/README.mkd
index f1f2144f..06268bd8 100644
--- a/contrib/notmuch-deliver/README.mkd
+++ b/contrib/notmuch-deliver/README.mkd
@@ -38,12 +38,6 @@ My personal e-mail address is [alip@exherbo.org](mailto:alip@exherbo.org).
I'm available on IRC as `alip` on [Freenode](http://freenode.net) and [OFTC](http://www.oftc.net).
## License
-This program is free software; you can redistribute it and/or modify it under
-the terms of the GNU General Public License as published by the Free Software
-Foundation, version 2 of the License.
-
-This program is distributed in the hope that it will be useful, but WITHOUT ANY
-WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE. See the GNU General Public License for more details.
+You may redistribute this under the same terms as notmuch itself.
<!-- vim: set ft=mkd spell spelllang=en sw=4 sts=4 et : -->
diff --git a/contrib/notmuch-deliver/maildrop/maildir/Makefile.am b/contrib/notmuch-deliver/maildrop/maildir/Makefile.am
index 2495a83b..6fd0ae49 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/Makefile.am
+++ b/contrib/notmuch-deliver/maildrop/maildir/Makefile.am
@@ -1,4 +1,3 @@
-#$Id: Makefile.am,v 1.59 2009/06/27 16:32:38 mrsam Exp $
#
# Copyright 1998 - 2005 Double Precision, Inc. See COPYING for
# distribution information.
diff --git a/contrib/notmuch-deliver/maildrop/maildir/configure.in b/contrib/notmuch-deliver/maildrop/maildir/configure.in
index 1e724443..dfdd22a3 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/configure.in
+++ b/contrib/notmuch-deliver/maildrop/maildir/configure.in
@@ -1,4 +1,3 @@
-dnl $Id: configure.in,v 1.37 2008/11/27 21:51:16 mrsam Exp $
dnl Process this file with autoconf to produce a configure script.
dnl
dnl Copyright 1998 - 2001 Double Precision, Inc. See COPYING for
@@ -67,6 +66,7 @@ AC_TYPE_OFF_T
AC_TYPE_SIZE_T
AC_TYPE_UID_T
AC_STRUCT_TM
+AC_SYS_LARGEFILE
dnl Checks for library functions.
AC_CHECK_HEADER(fam.h, :, :)
diff --git a/contrib/notmuch-deliver/maildrop/maildir/maildircreate.c b/contrib/notmuch-deliver/maildrop/maildir/maildircreate.c
index 74030f41..5efc0afb 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/maildircreate.c
+++ b/contrib/notmuch-deliver/maildrop/maildir/maildircreate.c
@@ -35,7 +35,6 @@
#include "numlib/numlib.h"
-static const char rcsid[]="$Id: maildircreate.c,v 1.6 2003/01/26 04:07:03 mrsam Exp $";
FILE *maildir_tmpcreate_fp(struct maildir_tmpcreate_info *info)
{
diff --git a/contrib/notmuch-deliver/maildrop/maildir/maildircreate.h b/contrib/notmuch-deliver/maildrop/maildir/maildircreate.h
index ea1c71ac..85391454 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/maildircreate.h
+++ b/contrib/notmuch-deliver/maildrop/maildir/maildircreate.h
@@ -16,7 +16,6 @@
extern "C" {
#endif
-static const char maildircreate_h_rcsid[]="$Id: maildircreate.h,v 1.10 2006/10/29 00:03:53 mrsam Exp $";
/* Create messages in maildirs */
diff --git a/contrib/notmuch-deliver/maildrop/maildir/maildirmisc.h b/contrib/notmuch-deliver/maildrop/maildir/maildirmisc.h
index 545d11e1..e1e7c19a 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/maildirmisc.h
+++ b/contrib/notmuch-deliver/maildrop/maildir/maildirmisc.h
@@ -18,7 +18,6 @@
extern "C" {
#endif
-static const char maildirmisc_h_rcsid[]="$Id: maildirmisc.h,v 1.18 2006/07/22 02:48:15 mrsam Exp $";
/*
**
diff --git a/contrib/notmuch-deliver/maildrop/maildir/maildirmkdir.c b/contrib/notmuch-deliver/maildrop/maildir/maildirmkdir.c
index 754b2c70..28a3ac21 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/maildirmkdir.c
+++ b/contrib/notmuch-deliver/maildrop/maildir/maildirmkdir.c
@@ -18,7 +18,6 @@
#include "maildirmisc.h"
-static const char rcsid[]="$Id: maildirmkdir.c,v 1.2 2002/03/15 03:09:21 mrsam Exp $";
int maildir_mkdir(const char *dir)
{
diff --git a/contrib/notmuch-deliver/maildrop/maildir/maildiropen.c b/contrib/notmuch-deliver/maildrop/maildir/maildiropen.c
index 5071df76..25428737 100644
--- a/contrib/notmuch-deliver/maildrop/maildir/maildiropen.c
+++ b/contrib/notmuch-deliver/maildrop/maildir/maildiropen.c
@@ -22,7 +22,6 @@
#include "maildirmisc.h"
-static const char rcsid[]="$Id: maildiropen.c,v 1.8 2003/01/19 16:39:52 mrsam Exp $";
char *maildir_getlink(const char *filename)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/Makefile.am b/contrib/notmuch-deliver/maildrop/numlib/Makefile.am
index c0f129fa..0a5f103d 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/Makefile.am
+++ b/contrib/notmuch-deliver/maildrop/numlib/Makefile.am
@@ -1,4 +1,3 @@
-# $Id: Makefile.am,v 1.12 2007/06/30 15:40:53 mrsam Exp $
#
# Copyright 1998 - 2004 Double Precision, Inc. See COPYING for
# distribution information.
diff --git a/contrib/notmuch-deliver/maildrop/numlib/atotimet.c b/contrib/notmuch-deliver/maildrop/numlib/atotimet.c
index d494fd22..0360b5b2 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/atotimet.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/atotimet.c
@@ -9,6 +9,5 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: atotimet.c,v 1.1 2003/08/03 03:09:19 mrsam Exp $";
LIBMAIL_STRIMPL(time_t, libmail_strtotime_t, libmail_atotime_t)
diff --git a/contrib/notmuch-deliver/maildrop/numlib/atouidt.c b/contrib/notmuch-deliver/maildrop/numlib/atouidt.c
index 3c01ecf5..6f869d54 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/atouidt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/atouidt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: atouidt.c,v 1.1 2004/01/11 02:47:33 mrsam Exp $";
LIBMAIL_STRIMPL(uid_t, libmail_strtouid_t, libmail_atouid_t)
LIBMAIL_STRIMPL(gid_t, libmail_strtogid_t, libmail_atogid_t)
diff --git a/contrib/notmuch-deliver/maildrop/numlib/changeuidgid.c b/contrib/notmuch-deliver/maildrop/numlib/changeuidgid.c
index 56793927..adaee406 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/changeuidgid.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/changeuidgid.c
@@ -19,7 +19,6 @@
#include "numlib.h"
-static const char rcsid[]="$Id: changeuidgid.c,v 1.2 2003/01/05 04:01:17 mrsam Exp $";
void libmail_changegroup(gid_t gid)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/configure.in b/contrib/notmuch-deliver/maildrop/numlib/configure.in
index fc977aa9..7479725b 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/configure.in
+++ b/contrib/notmuch-deliver/maildrop/numlib/configure.in
@@ -1,5 +1,4 @@
dnl Process this file with autoconf to produce a configure script.
-dnl $Id: configure.in,v 1.7 2010/03/19 01:09:26 mrsam Exp $
dnl
dnl Copyright 1998 - 2010 Double Precision, Inc. See COPYING for
dnl distribution information.
@@ -39,6 +38,7 @@ AC_CHECK_TYPE(int64_t, [ : ],
dnl Checks for typedefs, structures, and compiler characteristics.
AC_TYPE_UID_T
AC_TYPE_PID_T
+AC_SYS_LARGEFILE
dnl Checks for library functions.
diff --git a/contrib/notmuch-deliver/maildrop/numlib/numlib.h b/contrib/notmuch-deliver/maildrop/numlib/numlib.h
index 7928aa7e..c31d9b1a 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/numlib.h
+++ b/contrib/notmuch-deliver/maildrop/numlib/numlib.h
@@ -10,7 +10,6 @@
extern "C" {
#endif
-static const char numlib_h_rcsid[]="$Id: numlib.h,v 1.11 2010/03/19 01:09:26 mrsam Exp $";
#if HAVE_CONFIG_H
#include "../numlib/config.h" /* VPATH build */
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strdevt.c b/contrib/notmuch-deliver/maildrop/numlib/strdevt.c
index 2e542d54..aa8b0282 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strdevt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strdevt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strdevt.c,v 1.1 2003/01/26 03:22:40 mrsam Exp $";
char *libmail_str_dev_t(dev_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strgidt.c b/contrib/notmuch-deliver/maildrop/numlib/strgidt.c
index 89472e3b..828a4550 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strgidt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strgidt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strgidt.c,v 1.4 2003/01/05 04:01:17 mrsam Exp $";
char *libmail_str_gid_t(gid_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strhdevt.c b/contrib/notmuch-deliver/maildrop/numlib/strhdevt.c
index 98e25e53..9ff45f28 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strhdevt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strhdevt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strhdevt.c,v 1.2 2003/03/12 02:45:56 mrsam Exp $";
static const char xdigit[]="0123456789ABCDEF";
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strhinot.c b/contrib/notmuch-deliver/maildrop/numlib/strhinot.c
index fa640915..c3cdc10e 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strhinot.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strhinot.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strhinot.c,v 1.5 2003/03/12 02:45:56 mrsam Exp $";
static const char xdigit[]="0123456789ABCDEF";
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strhpidt.c b/contrib/notmuch-deliver/maildrop/numlib/strhpidt.c
index 2723af0b..5a440f5d 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strhpidt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strhpidt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strhpidt.c,v 1.5 2003/03/12 02:45:56 mrsam Exp $";
static const char xdigit[]="0123456789ABCDEF";
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strhtimet.c b/contrib/notmuch-deliver/maildrop/numlib/strhtimet.c
index b86b05a2..cb9e6a9f 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strhtimet.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strhtimet.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strhtimet.c,v 1.5 2003/03/12 02:45:56 mrsam Exp $";
static const char xdigit[]="0123456789ABCDEF";
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strinot.c b/contrib/notmuch-deliver/maildrop/numlib/strinot.c
index eb544c3c..314b2f38 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strinot.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strinot.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strinot.c,v 1.4 2003/01/05 04:01:17 mrsam Exp $";
char *libmail_str_ino_t(ino_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strofft.c b/contrib/notmuch-deliver/maildrop/numlib/strofft.c
index 3435148e..567f912b 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strofft.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strofft.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strofft.c,v 1.6 2010/03/19 01:09:26 mrsam Exp $";
char *libmail_str_off_t(off_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strpidt.c b/contrib/notmuch-deliver/maildrop/numlib/strpidt.c
index 12ee9ce1..0c29b07e 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strpidt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strpidt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strpidt.c,v 1.4 2003/01/05 04:01:17 mrsam Exp $";
char *libmail_str_pid_t(pid_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strsize.c b/contrib/notmuch-deliver/maildrop/numlib/strsize.c
index 0a7dcaa8..1c903122 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strsize.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strsize.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strsize.c,v 1.2 2003/01/05 04:01:17 mrsam Exp $";
static void cat_n(char *buf, unsigned long n)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strsizet.c b/contrib/notmuch-deliver/maildrop/numlib/strsizet.c
index d4ec92d3..fd9d1d1a 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strsizet.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strsizet.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strsizet.c,v 1.4 2003/01/05 04:01:18 mrsam Exp $";
char *libmail_str_size_t(size_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/strtimet.c b/contrib/notmuch-deliver/maildrop/numlib/strtimet.c
index be7e051b..63307f22 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/strtimet.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/strtimet.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: strtimet.c,v 1.4 2003/01/05 04:01:18 mrsam Exp $";
char *libmail_str_time_t(time_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/maildrop/numlib/struidt.c b/contrib/notmuch-deliver/maildrop/numlib/struidt.c
index 50f3f742..5843ed24 100644
--- a/contrib/notmuch-deliver/maildrop/numlib/struidt.c
+++ b/contrib/notmuch-deliver/maildrop/numlib/struidt.c
@@ -9,7 +9,6 @@
#include "numlib.h"
#include <string.h>
-static const char rcsid[]="$Id: struidt.c,v 1.4 2003/01/05 04:01:18 mrsam Exp $";
char *libmail_str_uid_t(uid_t t, char *arg)
{
diff --git a/contrib/notmuch-deliver/src/main.c b/contrib/notmuch-deliver/src/main.c
index f7a4eaa6..6f32f73d 100644
--- a/contrib/notmuch-deliver/src/main.c
+++ b/contrib/notmuch-deliver/src/main.c
@@ -1,22 +1,23 @@
-/* vim: set cino= fo=croql sw=8 ts=8 sts=0 noet cin fdm=syntax : */
-
-/*
- * Copyright (c) 2010 Ali Polatel <alip@exherbo.org>
+/* notmuch-deliver - If you make the user a promise... make sure you deliver it!
+ *
+ * Copyright © 2010 Ali Polatel
* Based in part upon deliverquota of maildrop which is:
* Copyright 1998 - 2009 Double Precision, Inc.
*
- * This file is part of the notmuch-deliver. notmuch-deliver is free software;
- * you can redistribute it and/or modify it under the terms of the GNU General
- * Public License version 2, as published by the Free Software Foundation.
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
*
- * notmuch-deliver is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
- * or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
- * more details.
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see http://www.gnu.org/licenses/ .
*
- * You should have received a copy of the GNU General Public License along with
- * this program; if not, write to the Free Software Foundation, Inc., 59 Temple
- * Place, Suite 330, Boston, MA 02111-1307 USA
+ * Author: Ali Polatel <polatel@gmail.com>
*/
#ifdef HAVE_CONFIG_H
diff --git a/debian/control b/debian/control
index 3252f32c..f6f415ed 100644
--- a/debian/control
+++ b/debian/control
@@ -16,7 +16,7 @@ Build-Depends:
python-all (>= 2.6.6-3~),
emacs23-nox | emacs23 (>=23~) | emacs23-lucid (>=23~),
gdb,
- dtach
+ dtach (>= 0.8)
Standards-Version: 3.9.2
Homepage: http://notmuchmail.org/
Vcs-Git: git://notmuchmail.org/git/notmuch
diff --git a/emacs/notmuch-hello.el b/emacs/notmuch-hello.el
index 1a76c30a..0582cae7 100644
--- a/emacs/notmuch-hello.el
+++ b/emacs/notmuch-hello.el
@@ -42,6 +42,26 @@
:type 'boolean
:group 'notmuch)
+(defun notmuch-sort-saved-searches (alist)
+ "Generate an alphabetically sorted saved searches alist."
+ (sort alist (lambda (a b) (string< (car a) (car b)))))
+
+(defcustom notmuch-saved-search-sort-function nil
+ "Function used to sort the saved searches for the notmuch-hello view.
+
+This variable controls how saved searches should be sorted. No
+sorting (nil) displays the saved searches in the order they are
+stored in `notmuch-saved-searches'. Sort alphabetically sorts the
+saved searches in alphabetical order. Custom sort function should
+be a function or a lambda expression that takes the saved
+searches alist as a parameter, and returns a new saved searches
+alist to be used."
+ :type '(choice (const :tag "No sorting" nil)
+ (const :tag "Sort alphabetically" notmuch-sort-saved-searches)
+ (function :tag "Custom sort function"
+ :value notmuch-sort-saved-searches))
+ :group 'notmuch)
+
(defvar notmuch-hello-indent 4
"How much to indent non-headers.")
@@ -168,8 +188,8 @@ Typically \",\" in the US and UK and \".\" in Europe."
collect elem))
;; Add the new one.
(customize-save-variable 'notmuch-saved-searches
- (push (cons name search)
- notmuch-saved-searches))
+ (add-to-list 'notmuch-saved-searches
+ (cons name search) t))
(message "Saved '%s' as '%s'." search name)
(notmuch-hello-update)))
@@ -440,6 +460,10 @@ Complete list of currently available key bindings:
(widest (max saved-widest alltags-widest)))
(when saved-alist
+ ;; Sort saved searches if required.
+ (when notmuch-saved-search-sort-function
+ (setq saved-alist
+ (funcall notmuch-saved-search-sort-function saved-alist)))
(widget-insert "\nSaved searches: ")
(widget-create 'push-button
:notify (lambda (&rest ignore)
diff --git a/emacs/notmuch-show.el b/emacs/notmuch-show.el
index d5c95d80..d7fbbca9 100644
--- a/emacs/notmuch-show.el
+++ b/emacs/notmuch-show.el
@@ -91,6 +91,16 @@ any given message."
:group 'notmuch
:type 'boolean)
+(defcustom notmuch-indent-messages-width 1
+ "Width of message indentation in threads.
+
+Messages are shown indented according to their depth in a thread.
+This variable determines the width of this indentation measured
+in number of blanks. Defaults to `1', choose `0' to disable
+indentation."
+ :group 'notmuch
+ :type 'integer)
+
(defcustom notmuch-show-indent-multipart nil
"Should the sub-parts of a multipart/* part be indented?"
;; dme: Not sure which is a good default.
@@ -238,7 +248,7 @@ unchanged ADDRESS if parsing fails."
"Insert a notmuch style headerline based on HEADERS for a
message at DEPTH in the current thread."
(let ((start (point)))
- (insert (notmuch-show-spaces-n depth)
+ (insert (notmuch-show-spaces-n (* notmuch-indent-messages-width depth))
(notmuch-show-clean-address (plist-get headers :From))
" ("
date
@@ -739,7 +749,7 @@ current buffer, if possible."
(setq content-end (point-marker))
;; Indent according to the depth in the thread.
- (indent-rigidly content-start content-end depth)
+ (indent-rigidly content-start content-end (* notmuch-indent-messages-width depth))
(setq message-end (point-max-marker))
@@ -843,6 +853,8 @@ buffer."
(inhibit-read-only t))
(switch-to-buffer buffer)
(notmuch-show-mode)
+ ;; Don't track undo information for this buffer
+ (set 'buffer-undo-list t)
(setq notmuch-show-thread-id thread-id)
(setq notmuch-show-parent-buffer parent-buffer)
@@ -1135,26 +1147,18 @@ All currently available key bindings:
;; Commands typically bound to keys.
-(defun notmuch-show-advance-and-archive ()
- "Advance through thread and archive.
-
-This command is intended to be one of the simplest ways to
-process a thread of email. It does the following:
+(defun notmuch-show-advance ()
+ "Advance through thread.
If the current message in the thread is not yet fully visible,
scroll by a near screenful to read more of the message.
Otherwise, (the end of the current message is already within the
-current window), advance to the next open message.
-
-Finally, if there is no further message to advance to, and this
-last message is already read, then archive the entire current
-thread, (remove the \"inbox\" tag from each message). Also kill
-this buffer, and display the next thread from the search from
-which this thread was originally shown."
+current window), advance to the next open message."
(interactive)
(let* ((end-of-this-message (notmuch-show-message-bottom))
- (visible-end-of-this-message (1- end-of-this-message)))
+ (visible-end-of-this-message (1- end-of-this-message))
+ (ret nil))
(while (invisible-p visible-end-of-this-message)
(setq visible-end-of-this-message
(previous-single-char-property-change visible-end-of-this-message
@@ -1173,8 +1177,24 @@ which this thread was originally shown."
(notmuch-show-next-open-message))
(t
- ;; This is the last message - archive the thread.
- (notmuch-show-archive-thread)))))
+ ;; This is the last message - change the return value
+ (setq ret t)))
+ ret))
+
+(defun notmuch-show-advance-and-archive ()
+ "Advance through thread and archive.
+
+This command is intended to be one of the simplest ways to
+process a thread of email. It works exactly like
+notmuch-show-advance, in that it scrolls through messages in a
+show buffer, except that when it gets to the end of the buffer it
+archives the entire current thread, (remove the \"inbox\" tag
+from each message), kills the buffer, and displays the next
+thread from the search from which this thread was originally
+shown."
+ (interactive)
+ (if (notmuch-show-advance)
+ (notmuch-show-archive-thread)))
(defun notmuch-show-rewind ()
"Backup through the thread, (reverse scrolling compared to \\[notmuch-show-advance-and-archive]).
diff --git a/emacs/notmuch.el b/emacs/notmuch.el
index c1827cc2..89361498 100644
--- a/emacs/notmuch.el
+++ b/emacs/notmuch.el
@@ -805,12 +805,12 @@ non-authors is found, assume that all of the authors match."
(goto-char (point-max))
(if (/= (match-beginning 1) line)
(insert (concat "Error: Unexpected output from notmuch search:\n" (substring string line (match-beginning 1)) "\n")))
- (let ((beg (point-marker)))
+ (let ((beg (point)))
(notmuch-search-show-result date count authors subject tags)
- (notmuch-search-color-line beg (point-marker) tag-list)
- (put-text-property beg (point-marker) 'notmuch-search-thread-id thread-id)
- (put-text-property beg (point-marker) 'notmuch-search-authors authors)
- (put-text-property beg (point-marker) 'notmuch-search-subject subject)
+ (notmuch-search-color-line beg (point) tag-list)
+ (put-text-property beg (point) 'notmuch-search-thread-id thread-id)
+ (put-text-property beg (point) 'notmuch-search-authors authors)
+ (put-text-property beg (point) 'notmuch-search-subject subject)
(if (string= thread-id notmuch-search-target-thread)
(progn
(set 'found-target beg)
@@ -885,7 +885,7 @@ PROMPT is the string to prompt with."
"subject:" "attachment:")
(mapcar (lambda (tag)
(concat "tag:" tag))
- (process-lines "notmuch" "search" "--output=tags" "*")))))
+ (process-lines notmuch-command "search" "--output=tags" "*")))))
(let ((keymap (copy-keymap minibuffer-local-map))
(minibuffer-completion-table
(completion-table-dynamic
@@ -920,6 +920,8 @@ The optional parameters are used as follows:
(let ((buffer (get-buffer-create (notmuch-search-buffer-title query))))
(switch-to-buffer buffer)
(notmuch-search-mode)
+ ;; Don't track undo information for this buffer
+ (set 'buffer-undo-list t)
(set 'notmuch-search-query-string query)
(set 'notmuch-search-oldest-first oldest-first)
(set 'notmuch-search-target-thread target-thread)
diff --git a/lib/Makefile.local b/lib/Makefile.local
index 57dca702..54c4dea4 100644
--- a/lib/Makefile.local
+++ b/lib/Makefile.local
@@ -30,7 +30,7 @@ LIBRARY_SUFFIX = so
LINKER_NAME = libnotmuch.$(LIBRARY_SUFFIX)
SONAME = $(LINKER_NAME).$(LIBNOTMUCH_VERSION_MAJOR)
LIBNAME = $(SONAME).$(LIBNOTMUCH_VERSION_MINOR).$(LIBNOTMUCH_VERSION_RELEASE)
-LIBRARY_LINK_FLAG = -shared -Wl,--version-script=notmuch.sym,-soname=$(SONAME)
+LIBRARY_LINK_FLAG = -shared -Wl,--version-script=notmuch.sym,-soname=$(SONAME) -Wl,--no-undefined
ifeq ($(LIBDIR_IN_LDCONFIG),1)
ifeq ($(DESTDIR),)
LIBRARY_INSTALL_POST_COMMAND=ldconfig
diff --git a/lib/message.cc b/lib/message.cc
index ca7fbf21..00754254 100644
--- a/lib/message.cc
+++ b/lib/message.cc
@@ -49,16 +49,16 @@ struct visible _notmuch_message {
struct maildir_flag_tag {
char flag;
const char *tag;
- bool inverse;
+ notmuch_bool_t inverse;
};
/* ASCII ordered table of Maildir flags and associated tags */
static struct maildir_flag_tag flag2tag[] = {
- { 'D', "draft", false},
- { 'F', "flagged", false},
- { 'P', "passed", false},
- { 'R', "replied", false},
- { 'S', "unread", true }
+ { 'D', "draft", FALSE},
+ { 'F', "flagged", FALSE},
+ { 'P', "passed", FALSE},
+ { 'R', "replied", FALSE},
+ { 'S', "unread", TRUE }
};
/* We end up having to call the destructor explicitly because we had
@@ -1217,8 +1217,6 @@ _new_maildir_filename (void *ctx,
if (info == NULL) {
info = filename + strlen(filename);
} else {
- flags = info + 3;
-
/* Loop through existing flags in filename. */
for (flags = info + 3, last_flag = 0;
*flags;
diff --git a/notmuch-dump.c b/notmuch-dump.c
index 126593d1..a490917f 100644
--- a/notmuch-dump.c
+++ b/notmuch-dump.c
@@ -67,13 +67,16 @@ notmuch_dump_command (unused (void *ctx), int argc, char *argv[])
return 1;
}
}
-
+
query = notmuch_query_create (notmuch, query_str);
if (query == NULL) {
fprintf (stderr, "Out of memory\n");
return 1;
}
- notmuch_query_set_sort (query, NOTMUCH_SORT_MESSAGE_ID);
+ /* Don't ask xapian to sort by Message-ID. Xapian optimizes returning the
+ * first results quickly at the expense of total time.
+ */
+ notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED);
for (messages = notmuch_query_search_messages (query);
notmuch_messages_valid (messages);
diff --git a/notmuch-tag.c b/notmuch-tag.c
index dded39ea..537d5a4d 100644
--- a/notmuch-tag.c
+++ b/notmuch-tag.c
@@ -30,6 +30,81 @@ handle_sigint (unused (int sig))
interrupted = 1;
}
+static char *
+_escape_tag (char *buf, const char *tag)
+{
+ const char *in = tag;
+ char *out = buf;
+ /* Boolean terms surrounded by double quotes can contain any
+ * character. Double quotes are quoted by doubling them. */
+ *out++ = '"';
+ while (*in) {
+ if (*in == '"')
+ *out++ = '"';
+ *out++ = *in++;
+ }
+ *out++ = '"';
+ *out = 0;
+ return buf;
+}
+
+static char *
+_optimize_tag_query (void *ctx, const char *orig_query_string, char *argv[],
+ int *add_tags, int add_tags_count,
+ int *remove_tags, int remove_tags_count)
+{
+ /* This is subtler than it looks. Xapian ignores the '-' operator
+ * at the beginning both queries and parenthesized groups and,
+ * furthermore, the presence of a '-' operator at the beginning of
+ * a group can inhibit parsing of the previous operator. Hence,
+ * the user-provided query MUST appear first, but it is safe to
+ * parenthesize and the exclusion part of the query must not use
+ * the '-' operator (though the NOT operator is fine). */
+
+ char *escaped, *query_string;
+ const char *join = "";
+ int i;
+ unsigned int max_tag_len = 0;
+
+ /* Allocate a buffer for escaping tags. This is large enough to
+ * hold a fully escaped tag with every character doubled plus
+ * enclosing quotes and a NUL. */
+ for (i = 0; i < add_tags_count; i++)
+ if (strlen (argv[add_tags[i]] + 1) > max_tag_len)
+ max_tag_len = strlen (argv[add_tags[i]] + 1);
+ for (i = 0; i < remove_tags_count; i++)
+ if (strlen (argv[remove_tags[i]] + 1) > max_tag_len)
+ max_tag_len = strlen (argv[remove_tags[i]] + 1);
+ escaped = talloc_array(ctx, char, max_tag_len * 2 + 3);
+ if (!escaped)
+ return NULL;
+
+ /* Build the new query string */
+ if (strcmp (orig_query_string, "*") == 0)
+ query_string = talloc_strdup (ctx, "(");
+ else
+ query_string = talloc_asprintf (ctx, "( %s ) and (", orig_query_string);
+
+ for (i = 0; i < add_tags_count && query_string; i++) {
+ query_string = talloc_asprintf_append_buffer (
+ query_string, "%snot tag:%s", join,
+ _escape_tag (escaped, argv[add_tags[i]] + 1));
+ join = " or ";
+ }
+ for (i = 0; i < remove_tags_count && query_string; i++) {
+ query_string = talloc_asprintf_append_buffer (
+ query_string, "%stag:%s", join,
+ _escape_tag (escaped, argv[remove_tags[i]] + 1));
+ join = " or ";
+ }
+
+ if (query_string)
+ query_string = talloc_strdup_append_buffer (query_string, ")");
+
+ talloc_free (escaped);
+ return query_string;
+}
+
int
notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[]))
{
@@ -93,6 +168,16 @@ notmuch_tag_command (void *ctx, unused (int argc), unused (char *argv[]))
return 1;
}
+ /* Optimize the query so it excludes messages that already have
+ * the specified set of tags. */
+ query_string = _optimize_tag_query (ctx, query_string, argv,
+ add_tags, add_tags_count,
+ remove_tags, remove_tags_count);
+ if (query_string == NULL) {
+ fprintf (stderr, "Out of memory.\n");
+ return 1;
+ }
+
config = notmuch_config_open (ctx, NULL, NULL);
if (config == NULL)
return 1;
diff --git a/test/.gitignore b/test/.gitignore
index 9e97052d..7e30e8d5 100644
--- a/test/.gitignore
+++ b/test/.gitignore
@@ -1,4 +1,5 @@
test-results
corpus.mail
smtp-dummy
+symbol-test
tmp.*
diff --git a/test/Makefile.local b/test/Makefile.local
index 8eb04330..bffbbdbd 100644
--- a/test/Makefile.local
+++ b/test/Makefile.local
@@ -11,10 +11,16 @@ smtp_dummy_modules = $(smtp_dummy_srcs:.c=.o)
$(dir)/smtp-dummy: $(smtp_dummy_modules)
$(call quiet,CC) $^ -o $@
+$(dir)/symbol-test: $(dir)/symbol-test.o
+ $(call quiet,CC) $^ -o $@ -Llib -lnotmuch -lxapian
+
.PHONY: test check
-test: all $(dir)/smtp-dummy
+
+test-binaries: $(dir)/smtp-dummy $(dir)/symbol-test
+
+test: all test-binaries
@${dir}/notmuch-test $(OPTIONS)
check: test
-CLEAN := $(CLEAN) $(dir)/smtp-dummy
+CLEAN := $(CLEAN) $(dir)/smtp-dummy $(dir)/smtp-dummy.o $(dir)/symbol-test $(dir)/symbol-test.o
diff --git a/test/README b/test/README
index 2481f16d..2e757e0e 100644
--- a/test/README
+++ b/test/README
@@ -13,7 +13,8 @@ notmuch-test script). Either command will run all available tests.
Alternately, you can run a specific subset of tests by simply invoking
one of the executable scripts in this directory, (such as ./search,
-./reply, etc.)
+./reply, etc). Note that you will probably want "make test-binaries"
+before running individual tests.
The following command-line options are available when running tests:
diff --git a/test/basic b/test/basic
index 38db2baf..4edf8315 100755
--- a/test/basic
+++ b/test/basic
@@ -51,17 +51,11 @@ test_expect_code 2 'failure to clean up causes the test to fail' '
# Ensure that all tests are being run
test_begin_subtest 'Ensure that all available tests will be run by notmuch-test'
-eval $(sed -n -e '/^TESTS="$/,/^"$/p' notmuch-test $TEST_DIRECTORY/notmuch-test)
+eval $(sed -n -e '/^TESTS="$/,/^"$/p' $TEST_DIRECTORY/notmuch-test)
tests_in_suite=$(for i in $TESTS; do echo $i; done | sort)
-available=$(ls -1 $TEST_DIRECTORY/ | \
- sed -r -e "/^(aggregate-results.sh|Makefile|Makefile.local|notmuch-test)/d" \
- -e "/^(README|test-lib.sh|test-lib.el|test-results|tmp.*|valgrind|corpus*)/d" \
- -e "/^(emacs.expected-output|smtp-dummy|smtp-dummy.c|test-verbose|symbol-test.cc)/d" \
- -e "/^(test.expected-output|.*~)/d" \
- -e "/^(gnupg-secret-key.asc)/d" \
- -e "/^(gnupg-secret-key.NOTE)/d" \
- -e "/^(atomicity.gdb)/d" \
- | sort)
+available=$(find "$TEST_DIRECTORY" -maxdepth 1 -type f -executable -printf '%f\n' | \
+ sed -r -e "/^(aggregate-results.sh|notmuch-test|smtp-dummy|test-verbose|symbol-test)$/d" | \
+ sort)
test_expect_equal "$tests_in_suite" "$available"
EXPECTED=$TEST_DIRECTORY/test.expected-output
diff --git a/test/emacs b/test/emacs
index 75a0a744..3f8c72d3 100755
--- a/test/emacs
+++ b/test/emacs
@@ -50,6 +50,27 @@ test_emacs "(notmuch-show \"$maildir_storage_thread\")
(test-output)"
test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
+test_begin_subtest "Basic notmuch-show view in emacs default indentation"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(let ((notmuch-indent-messages-width 1))
+ (notmuch-show \"$maildir_storage_thread\")
+ (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage
+
+test_begin_subtest "Basic notmuch-show view in emacs without indentation"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(let ((notmuch-indent-messages-width 0))
+ (notmuch-show \"$maildir_storage_thread\")
+ (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage-without-indentation
+
+test_begin_subtest "Basic notmuch-show view in emacs with fourfold indentation"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+test_emacs "(let ((notmuch-indent-messages-width 4))
+ (notmuch-show \"$maildir_storage_thread\")
+ (test-output))"
+test_expect_equal_file OUTPUT $EXPECTED/notmuch-show-thread-maildir-storage-with-fourfold-indentation
+
test_begin_subtest "notmuch-show for message with invalid From"
add_message "[subject]=\"message-with-invalid-from\"" \
"[from]=\"\\\"Invalid \\\" From\\\" <test_suite@notmuchmail.org>\""
@@ -369,8 +390,18 @@ test_emacs '(notmuch-show "id:\"bought\"")
(rotate-yank-pointer 1))
(reverse-region (point-min) (point-max))
(test-output)'
-sed -i -e 's/^.*tmp.emacs\/mail.*$/FILENAME/' OUTPUT
-test_expect_equal_file OUTPUT $EXPECTED/emacs-stashing
+cat <<EOF >EXPECTED
+Sat, 01 Jan 2000 12:00:00 -0000
+Some One <someone@somewhere.org>
+Some One Else <notsomeone@somewhere.org>
+Notmuch <notmuch@notmuchmail.org>
+Stash my stashables
+id:"bought"
+bought
+inbox,stashtest
+${gen_msg_filename}
+EOF
+test_expect_equal_file OUTPUT EXPECTED
test_begin_subtest "Stashing in notmuch-search"
test_emacs '(notmuch-search "id:\"bought\"")
@@ -381,7 +412,7 @@ test_emacs '(notmuch-search "id:\"bought\"")
(yank)
(test-output)'
sed -i -e 's/^thread:.*$/thread:XXX/' OUTPUT
-test_expect_equal $(cat OUTPUT) "thread:XXX"
+test_expect_equal "$(cat OUTPUT)" "thread:XXX"
test_begin_subtest 'Hiding message following HTML part'
test_subtest_known_broken
diff --git a/test/emacs.expected-output/emacs-stashing b/test/emacs.expected-output/emacs-stashing
deleted file mode 100644
index 49235947..00000000
--- a/test/emacs.expected-output/emacs-stashing
+++ /dev/null
@@ -1,9 +0,0 @@
-Sat, 01 Jan 2000 12:00:00 -0000
-Some One <someone@somewhere.org>
-Some One Else <notsomeone@somewhere.org>
-Notmuch <notmuch@notmuchmail.org>
-Stash my stashables
-id:"bought"
-bought
-inbox,stashtest
-FILENAME
diff --git a/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation
new file mode 100644
index 00000000..41e2aaa3
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation
@@ -0,0 +1,215 @@
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ text/plain ]
+I saw the LWN article and decided to take a look at notmuch. I'm
+currently using mutt and mairix to index and read a collection of
+Maildir mail folders (around 40,000 messages total).
+
+notmuch indexed the messages without complaint, but my attempt at
+searching bombed out. Running, for example:
+
+ notmuch search storage
+
+Resulted in 4604 lines of errors along the lines of:
+
+ Error opening
+ /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ Too many open files
+
+I'm curious if this is expected behavior (i.e., notmuch does not work
+with Maildir) or if something else is going on.
+
+Cheers,
+
+[ 5-line signature. Click/Enter to show. ]
+--
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ text/plain ]
+
+ Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did gyre and gimble:
+
+ LK> Resulted in 4604 lines of errors along the lines of:
+
+ LK> Error opening
+ LK> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ LK> Too many open files
+
+ See the patch just posted here.
+
+ [ 2-line signature. Click/Enter to show. ]
+ --
+ http://fossarchy.blogspot.com/
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ _______________________________________________
+ notmuch mailing list
+ notmuch@notmuchmail.org
+ http://notmuchmail.org/mailman/listinfo/notmuch
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Mikhail Gusarov <dottedmag@dottedmag.net>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ text/plain ]
+ > See the patch just posted here.
+
+ Is the list archived anywhere? The obvious archives
+ (http://notmuchmail.org/pipermail/notmuch/) aren't available, and I
+ think I subscribed too late to get the patch (I only just saw the
+ discussion about it).
+
+ It doesn't look like the patch is in git yet.
+
+ -- Lars
+
+ [ 5-line signature. Click/Enter to show. ]
+ --
+ Lars Kellogg-Stedman <lars@seas.harvard.edu>
+ Senior Technologist, Computing and Information Technology
+ Harvard University School of Engineering and Applied Sciences
+
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ _______________________________________________
+ notmuch mailing list
+ notmuch@notmuchmail.org
+ http://notmuchmail.org/mailman/listinfo/notmuch
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+
+ Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at seas.harvard.edu did gyre and gimble:
+
+ LK> Is the list archived anywhere? The obvious archives
+ LK> (http://notmuchmail.org/pipermail/notmuch/) aren't available, and I
+ LK> think I subscribed too late to get the patch (I only just saw the
+ LK> discussion about it).
+
+ LK> It doesn't look like the patch is in git yet.
+
+ Just has been pushed
+
+ [ 10-line signature. Click/Enter to show. ]
+ --
+ http://fossarchy.blogspot.com/
+ -------------- next part --------------
+ A non-text attachment was scrubbed...
+ Name: not available
+ Type: application/pgp-signature
+ Size: 834 bytes
+ Desc: not available
+ URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20091118/0e33d964/attachment.pgp>
+
+ Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+ On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman <lars at seas.harvard.edu> wrote:
+ > > See the patch just posted here.
+
+ I've also pushed a slightly more complicated (and complete) fix to my
+ private notmuch repository
+
+ git://keithp.com/git/notmuch
+
+ > Is the list archived anywhere?
+
+ Oops. Looks like Carl's mail server is broken. He's traveling to
+ Barcelona today and so it won't get fixed for a while.
+
+ Thanks to everyone for trying out notmuch!
+
+ -keith
+
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Keith Packard <keithp@keithp.com>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ text/plain ]
+ > I've also pushed a slightly more complicated (and complete) fix to my
+ > private notmuch repository
+
+ The version of lib/messages.cc in your repo doesn't build because it's
+ missing "#include <stdint.h>" (for the uint32_t on line 466).
+
+ [ 5-line signature. Click/Enter to show. ]
+ --
+ Lars Kellogg-Stedman <lars@seas.harvard.edu>
+ Senior Technologist, Computing and Information Technology
+ Harvard University School of Engineering and Applied Sciences
+
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ _______________________________________________
+ notmuch mailing list
+ notmuch@notmuchmail.org
+ http://notmuchmail.org/mailman/listinfo/notmuch
+ Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+ On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman <lars at seas.harvard.edu> wrote:
+ > I saw the LWN article and decided to take a look at notmuch. I'm
+ > currently using mutt and mairix to index and read a collection of
+ > Maildir mail folders (around 40,000 messages total).
+
+ Welcome, Lars!
+
+ I hadn't even seen that Keith's blog post had been picked up by lwn.net.
+ That's very interesting. So, thanks for coming and trying out notmuch.
+
+ > Error opening
+ > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ > Too many open files
+
+ Sadly, the lwn article coincided with me having just introduced this
+ bug, and then getting on a Trans-Atlantic flight. So I fixed the bug
+ fairly quickly, but there was quite a bit of latency before I could push
+ the fix out. It should be fixed now.
+
+ > I'm curious if this is expected behavior (i.e., notmuch does not work
+ > with Maildir) or if something else is going on.
+
+ Notmuch works just fine with maildir---it's one of the things that it
+ likes the best.
+
+ Happy hacking,
+
+ -Carl
+
diff --git a/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation
new file mode 100644
index 00000000..fa2108ef
--- /dev/null
+++ b/test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation
@@ -0,0 +1,215 @@
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ text/plain ]
+I saw the LWN article and decided to take a look at notmuch. I'm
+currently using mutt and mairix to index and read a collection of
+Maildir mail folders (around 40,000 messages total).
+
+notmuch indexed the messages without complaint, but my attempt at
+searching bombed out. Running, for example:
+
+ notmuch search storage
+
+Resulted in 4604 lines of errors along the lines of:
+
+ Error opening
+ /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ Too many open files
+
+I'm curious if this is expected behavior (i.e., notmuch does not work
+with Maildir) or if something else is going on.
+
+Cheers,
+
+[ 5-line signature. Click/Enter to show. ]
+--
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+Subject: Re: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ text/plain ]
+
+Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did gyre and gimble:
+
+ LK> Resulted in 4604 lines of errors along the lines of:
+
+ LK> Error opening
+ LK> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ LK> Too many open files
+
+See the patch just posted here.
+
+[ 2-line signature. Click/Enter to show. ]
+--
+http://fossarchy.blogspot.com/
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed unread)
+Subject: Re: [notmuch] Working with Maildir storage?
+To: Mikhail Gusarov <dottedmag@dottedmag.net>
+Cc: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ text/plain ]
+> See the patch just posted here.
+
+Is the list archived anywhere? The obvious archives
+(http://notmuchmail.org/pipermail/notmuch/) aren't available, and I
+think I subscribed too late to get the patch (I only just saw the
+discussion about it).
+
+It doesn't look like the patch is in git yet.
+
+-- Lars
+
+[ 5-line signature. Click/Enter to show. ]
+--
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+
+Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at seas.harvard.edu did gyre and gimble:
+
+ LK> Is the list archived anywhere? The obvious archives
+ LK> (http://notmuchmail.org/pipermail/notmuch/) aren't available, and I
+ LK> think I subscribed too late to get the patch (I only just saw the
+ LK> discussion about it).
+
+ LK> It doesn't look like the patch is in git yet.
+
+Just has been pushed
+
+[ 10-line signature. Click/Enter to show. ]
+--
+http://fossarchy.blogspot.com/
+-------------- next part --------------
+A non-text attachment was scrubbed...
+Name: not available
+Type: application/pgp-signature
+Size: 834 bytes
+Desc: not available
+URL: <http://notmuchmail.org/pipermail/notmuch/attachments/20091118/0e33d964/attachment.pgp>
+
+Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+On Tue, 17 Nov 2009 15:33:01 -0500, Lars Kellogg-Stedman <lars at seas.harvard.edu> wrote:
+> > See the patch just posted here.
+
+I've also pushed a slightly more complicated (and complete) fix to my
+private notmuch repository
+
+git://keithp.com/git/notmuch
+
+> Is the list archived anywhere?
+
+Oops. Looks like Carl's mail server is broken. He's traveling to
+Barcelona today and so it won't get fixed for a while.
+
+Thanks to everyone for trying out notmuch!
+
+-keith
+
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+Subject: Re: [notmuch] Working with Maildir storage?
+To: Keith Packard <keithp@keithp.com>
+Cc: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ text/plain ]
+> I've also pushed a slightly more complicated (and complete) fix to my
+> private notmuch repository
+
+The version of lib/messages.cc in your repo doesn't build because it's
+missing "#include <stdint.h>" (for the uint32_t on line 466).
+
+[ 5-line signature. Click/Enter to show. ]
+--
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman <lars at seas.harvard.edu> wrote:
+> I saw the LWN article and decided to take a look at notmuch. I'm
+> currently using mutt and mairix to index and read a collection of
+> Maildir mail folders (around 40,000 messages total).
+
+Welcome, Lars!
+
+I hadn't even seen that Keith's blog post had been picked up by lwn.net.
+That's very interesting. So, thanks for coming and trying out notmuch.
+
+> Error opening
+> /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+> Too many open files
+
+Sadly, the lwn article coincided with me having just introduced this
+bug, and then getting on a Trans-Atlantic flight. So I fixed the bug
+fairly quickly, but there was quite a bit of latency before I could push
+the fix out. It should be fixed now.
+
+> I'm curious if this is expected behavior (i.e., notmuch does not work
+> with Maildir) or if something else is going on.
+
+Notmuch works just fine with maildir---it's one of the things that it
+likes the best.
+
+Happy hacking,
+
+-Carl
+
diff --git a/test/notmuch-test b/test/notmuch-test
index 113ea7cf..53ce355c 100755
--- a/test/notmuch-test
+++ b/test/notmuch-test
@@ -62,10 +62,13 @@ else
TEST_TIMEOUT_CMD=""
fi
+trap 'e=$?; kill $!; exit $e' HUP INT TERM
# Run the tests
for test in $TESTS; do
- $TEST_TIMEOUT_CMD ./$test "$@"
+ $TEST_TIMEOUT_CMD ./$test "$@" &
+ wait $!
done
+trap - HUP INT TERM
# Report results
./aggregate-results.sh test-results/*
diff --git a/test/raw b/test/raw
index b7e265a8..99d3a3bf 100755
--- a/test/raw
+++ b/test/raw
@@ -1,4 +1,4 @@
-#!/usr//bin/env bash
+#!/usr/bin/env bash
test_description='notmuch show --format=raw'
. ./test-lib.sh
diff --git a/test/symbol-hiding b/test/symbol-hiding
index d0b31aec..f67b653e 100755
--- a/test/symbol-hiding
+++ b/test/symbol-hiding
@@ -12,13 +12,12 @@ test_description='exception symbol hiding'
. ./test-lib.sh
run_test(){
- result=$(LD_LIBRARY_PATH=$TEST_DIRECTORY/../lib ./symbol-test 2>&1)
+ result=$(LD_LIBRARY_PATH=$TEST_DIRECTORY/../lib $TEST_DIRECTORY/symbol-test 2>&1)
}
output="A Xapian exception occurred opening database: Couldn't stat 'fakedb/.notmuch/xapian'
caught No chert database found at path \`./nonexistant'"
-g++ -o symbol-test -I$TEST_DIRECTORY/../lib $TEST_DIRECTORY/symbol-test.cc -L$TEST_DIRECTORY/../lib -lnotmuch -lxapian
mkdir -p fakedb/.notmuch
test_expect_success 'running test' run_test
test_begin_subtest 'checking output'
diff --git a/test/symbol-test.cc b/test/symbol-test.cc
index 1de06eae..1548ca40 100644
--- a/test/symbol-test.cc
+++ b/test/symbol-test.cc
@@ -1,17 +1,17 @@
#include <stdio.h>
#include <xapian.h>
#include <notmuch.h>
-main (int argc, char **argv){
- notmuch_database_t *notmuch
- = notmuch_database_open ("fakedb",
- NOTMUCH_DATABASE_MODE_READ_ONLY);
- try{
- (void)new Xapian::WritableDatabase ("./nonexistant", Xapian::DB_OPEN);
+int main() {
+ (void) notmuch_database_open("fakedb", NOTMUCH_DATABASE_MODE_READ_ONLY);
+
+ try {
+ (void) new Xapian::WritableDatabase("./nonexistant", Xapian::DB_OPEN);
} catch (const Xapian::Error &error) {
- printf("caught %s\n",error.get_msg().c_str());
+ printf("caught %s\n", error.get_msg().c_str());
return 0;
}
+
return 1;
}
diff --git a/test/test-lib.sh b/test/test-lib.sh
index cf309f94..a9759570 100755..100644
--- a/test/test-lib.sh
+++ b/test/test-lib.sh
@@ -398,6 +398,8 @@ emacs_deliver_message ()
(insert \"${body}\")
$@
(message-send-and-exit))"
+ # opportunistically quit smtp-dummy in case above fails.
+ { echo QUIT > /dev/tcp/localhost/25025; } 2>/dev/null
wait ${smtp_dummy_pid}
notmuch new >/dev/null
}
@@ -427,7 +429,7 @@ test_begin_subtest ()
error "bug in test script: Missing test_expect_equal in ${BASH_SOURCE[1]}:${BASH_LINENO[0]}"
fi
test_subtest_name="$1"
- test_subtest_known_broken_=
+ test_reset_state_
# Remember stdout and stderr file descriptors and redirect test
# output to the previously prepared file descriptors 3 and 4 (see
# below)
@@ -546,6 +548,33 @@ test_have_prereq () {
esac
}
+# declare prerequisite for the given external binary
+test_declare_external_prereq () {
+ binary="$1"
+ test "$#" = 2 && name=$2 || name="$binary(1)"
+
+ hash $binary 2>/dev/null || eval "
+ test_missing_external_prereq_${binary}_=t
+$binary () {
+ echo -n \"\$test_subtest_missing_external_prereqs_ \" | grep -qe \" $name \" ||
+ test_subtest_missing_external_prereqs_=\"\$test_subtest_missing_external_prereqs_ $name\"
+ false
+}"
+}
+
+# Explicitly require external prerequisite. Useful when binary is
+# called indirectly (e.g. from emacs).
+# Returns success if dependency is available, failure otherwise.
+test_require_external_prereq () {
+ binary="$1"
+ if [ "$(eval echo -n \$test_missing_external_prereq_${binary}_)" = t ]; then
+ # dependency is missing, call the replacement function to note it
+ eval "$binary"
+ else
+ true
+ fi
+}
+
# You are not expected to call test_ok_ and test_failure_ directly, use
# the text_expect_* functions instead.
@@ -579,14 +608,14 @@ test_failure_message_ () {
}
test_known_broken_ok_ () {
- test_subtest_known_broken_=
+ test_reset_state_
test_fixed=$(($test_fixed+1))
say_color pass "%-6s" "FIXED"
echo " $@"
}
test_known_broken_failure_ () {
- test_subtest_known_broken_=
+ test_reset_state_
test_broken=$(($test_broken+1))
test_failure_message_ "BROKEN" "$@"
return 1
@@ -622,18 +651,32 @@ test_skip () {
fi
case "$to_skip" in
t)
- test_subtest_known_broken_=
- say_color skip >&3 "skipping test: $@"
- say_color skip "%-6s" "SKIP"
- echo " $1"
- : true
+ test_report_skip_ "$@"
;;
*)
- false
+ test_check_missing_external_prereqs_ "$@"
;;
esac
}
+test_check_missing_external_prereqs_ () {
+ if test -n "$test_subtest_missing_external_prereqs_"; then
+ say_color skip >&3 "missing prerequisites:"
+ echo "$test_subtest_missing_external_prereqs_" >&3
+ test_report_skip_ "$@"
+ else
+ false
+ fi
+}
+
+test_report_skip_ () {
+ test_reset_state_
+ say_color skip >&3 "skipping test:"
+ echo " $@" >&3
+ say_color skip "%-6s" "SKIP"
+ echo " $1"
+}
+
test_subtest_known_broken () {
test_subtest_known_broken_=t
}
@@ -642,10 +685,14 @@ test_expect_success () {
test "$#" = 3 && { prereq=$1; shift; } || prereq=
test "$#" = 2 ||
error "bug in the test script: not 2 or 3 parameters to test-expect-success"
+ test_reset_state_
if ! test_skip "$@"
then
test_run_ "$2"
- if [ "$?" = 0 -a "$eval_ret" = 0 ]
+ run_ret="$?"
+ # test_run_ may update missing external prerequisites
+ test_check_missing_external_prereqs_ "$@" ||
+ if [ "$run_ret" = 0 -a "$eval_ret" = 0 ]
then
test_ok_ "$1"
else
@@ -658,10 +705,14 @@ test_expect_code () {
test "$#" = 4 && { prereq=$1; shift; } || prereq=
test "$#" = 3 ||
error "bug in the test script: not 3 or 4 parameters to test-expect-code"
+ test_reset_state_
if ! test_skip "$@"
then
test_run_ "$3"
- if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+ run_ret="$?"
+ # test_run_ may update missing external prerequisites,
+ test_check_missing_external_prereqs_ "$@" ||
+ if [ "$run_ret" = 0 -a "$eval_ret" = "$1" ]
then
test_ok_ "$2"
else
@@ -684,6 +735,7 @@ test_external () {
error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
descr="$1"
shift
+ test_reset_state_
if ! test_skip "$descr" "$@"
then
# Announce the script to reduce confusion about the
@@ -842,17 +894,22 @@ EOF
}
test_emacs () {
+ # test dependencies beforehand to avoid the waiting loop below
+ test_require_external_prereq emacs || return
+ test_require_external_prereq emacsclient || return
+
if [ -z "$EMACS_SERVER" ]; then
- EMACS_SERVER="notmuch-test-suite-$$"
+ server_name="notmuch-test-suite-$$"
# start a detached session with an emacs server
# user's TERM is given to dtach which assumes a minimally
# VT100-compatible terminal -- and emacs inherits that
TERM=$ORIGINAL_TERM dtach -n "$TEST_TMPDIR/emacs-dtach-socket.$$" \
sh -c "stty rows 24 cols 80; exec '$TMP_DIRECTORY/run_emacs' \
--no-window-system \
- --eval '(setq server-name \"$EMACS_SERVER\")' \
+ --eval '(setq server-name \"$server_name\")' \
--eval '(server-start)' \
--eval '(orphan-watchdog $$)'" || return
+ EMACS_SERVER="$server_name"
# wait until the emacs server is up
until test_emacs '()' 2>/dev/null; do
sleep 1
@@ -862,6 +919,21 @@ test_emacs () {
emacsclient --socket-name="$EMACS_SERVER" --eval "(progn $@)"
}
+test_reset_state_ () {
+ test -z "$test_init_done_" && test_init_
+
+ test_subtest_known_broken_=
+ test_subtest_missing_external_prereqs_=
+}
+
+# called once before the first subtest
+test_init_ () {
+ test_init_done_=t
+
+ # skip all tests if there were external prerequisites missing during init
+ test_check_missing_external_prereqs_ "all tests in $this_test" && test_done
+}
+
find_notmuch_path ()
{
@@ -1069,3 +1141,10 @@ test -z "$NO_PYTHON" && test_set_prereq PYTHON
# test whether the filesystem supports symbolic links
ln -s x y 2>/dev/null && test -h y 2>/dev/null && test_set_prereq SYMLINKS
rm -f y
+
+# declare prerequisites for external binaries used in tests
+test_declare_external_prereq dtach
+test_declare_external_prereq emacs
+test_declare_external_prereq emacsclient
+test_declare_external_prereq gdb
+test_declare_external_prereq gpg
diff --git a/util/Makefile.local b/util/Makefile.local
index 2ff42b3d..03408995 100644
--- a/util/Makefile.local
+++ b/util/Makefile.local
@@ -9,3 +9,5 @@ libutil_modules := $(libutil_c_srcs:.c=.o)
$(dir)/libutil.a: $(libutil_modules)
$(call quiet,AR) rcs $@ $^
+
+CLEAN := $(CLEAN) $(dir)/xutil.o $(dir)/error_util.o $(dir)/libutil.a