]> git.notmuchmail.org Git - notmuch/commitdiff
Merge branch 'release'
authorDavid Bremner <bremner@debian.org>
Tue, 6 Dec 2011 23:39:33 +0000 (19:39 -0400)
committerDavid Bremner <bremner@debian.org>
Tue, 6 Dec 2011 23:39:33 +0000 (19:39 -0400)
Conflicts:
NEWS

Conflicts resolved by inserting the 0.10.2 stanza before 0.11

63 files changed:
Makefile.local
NEWS
RELEASING
bindings/python/docs/source/index.rst
bindings/python/notmuch/__init__.py
bindings/python/notmuch/database.py
bindings/python/notmuch/filename.py
bindings/python/notmuch/globals.py
bindings/python/notmuch/message.py
bindings/python/notmuch/tag.py
bindings/python/notmuch/thread.py
contrib/.gitattributes [deleted file]
contrib/notmuch-deliver/COPYING [deleted file]
contrib/notmuch-deliver/README.mkd
contrib/notmuch-deliver/maildrop/maildir/Makefile.am
contrib/notmuch-deliver/maildrop/maildir/configure.in
contrib/notmuch-deliver/maildrop/maildir/maildircreate.c
contrib/notmuch-deliver/maildrop/maildir/maildircreate.h
contrib/notmuch-deliver/maildrop/maildir/maildirmisc.h
contrib/notmuch-deliver/maildrop/maildir/maildirmkdir.c
contrib/notmuch-deliver/maildrop/maildir/maildiropen.c
contrib/notmuch-deliver/maildrop/numlib/Makefile.am
contrib/notmuch-deliver/maildrop/numlib/atotimet.c
contrib/notmuch-deliver/maildrop/numlib/atouidt.c
contrib/notmuch-deliver/maildrop/numlib/changeuidgid.c
contrib/notmuch-deliver/maildrop/numlib/configure.in
contrib/notmuch-deliver/maildrop/numlib/numlib.h
contrib/notmuch-deliver/maildrop/numlib/strdevt.c
contrib/notmuch-deliver/maildrop/numlib/strgidt.c
contrib/notmuch-deliver/maildrop/numlib/strhdevt.c
contrib/notmuch-deliver/maildrop/numlib/strhinot.c
contrib/notmuch-deliver/maildrop/numlib/strhpidt.c
contrib/notmuch-deliver/maildrop/numlib/strhtimet.c
contrib/notmuch-deliver/maildrop/numlib/strinot.c
contrib/notmuch-deliver/maildrop/numlib/strofft.c
contrib/notmuch-deliver/maildrop/numlib/strpidt.c
contrib/notmuch-deliver/maildrop/numlib/strsize.c
contrib/notmuch-deliver/maildrop/numlib/strsizet.c
contrib/notmuch-deliver/maildrop/numlib/strtimet.c
contrib/notmuch-deliver/maildrop/numlib/struidt.c
contrib/notmuch-deliver/src/main.c
debian/control
emacs/notmuch-hello.el
emacs/notmuch-show.el
emacs/notmuch.el
lib/Makefile.local
lib/message.cc
notmuch-dump.c
notmuch-tag.c
test/.gitignore
test/Makefile.local
test/README
test/basic
test/emacs
test/emacs.expected-output/emacs-stashing [deleted file]
test/emacs.expected-output/notmuch-show-thread-maildir-storage-with-fourfold-indentation [new file with mode: 0644]
test/emacs.expected-output/notmuch-show-thread-maildir-storage-without-indentation [new file with mode: 0644]
test/notmuch-test
test/raw
test/symbol-hiding
test/symbol-test.cc
test/test-lib.sh [changed mode: 0755->0644]
util/Makefile.local

index d97fa6183d45f179294f29b2cf853cbb5898d5b0..15e6d8827661c9d736bb754dbe9422a6942e9c31 100644 (file)
@@ -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 3f577e4240c2dc09333bf254ccc1bab7e6fa8d08..bb5e4d55f6415a34c1e4a5231799bd62a3a14c7b 100644 (file)
--- 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)
 ===========================
 
index e3e0cefe69e3dc3f640c1d3471d90f802209d553..88dab04eefd5dcc9ca78f9b76ee71ecf96215d7e 100644 (file)
--- 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.
 
index 73d2a3b015454ed97fcd93468b3872648dab8b5f..f7d3d6058e3b058a171da31a365d57b2b02abd0f 100644 (file)
@@ -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__
 
index 539afedfe305bdfa2000592b166647eba9700142..f3ff98745c07a9a69927702f13cbd7f261e59b22 100644 (file)
@@ -69,7 +69,7 @@ from notmuch.globals import (
     TagTooLongError,
     UnbalancedFreezeThawError,
     UnbalancedAtomicError,
-    NotInitializedError
+    NotInitializedError,
 )
 from notmuch.version import __VERSION__
 __LICENSE__ = "GPL v3+"
index f4bc53e02c6d17574595e25972979af99ad6aa9d..7923f768b1f78caec831c3018b5280367feb9712 100644 (file)
@@ -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)
index de4d785ad6085ddfd3fbf42ebe54ae33d5ca76c7..a7cd7e63d44bf03076b5e21087e77ae425761a7a 100644 (file)
@@ -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)
index de1db1617ae26a705c62caa457e7eea3b02943f1..54a49b2d3f16895a8a8ce0b9ffc459b58a7cadf2 100644 (file)
@@ -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)
index 4bf90c22f9bb62fbfe3c4bef1c69b7a2cb72da36..ce8e7181b2743f162843c8f2d387baf3515d3f9c 100644 (file)
@@ -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)
index 50e3686b6580d65369ac3178d988d90c1837d932..2fb7d3287a8413e295b1e9b5b2840d9d06bdb476 100644 (file)
@@ -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)
index 5e08eb316901b70a3642c5e0b7f4a1d2571b4977..5058846d7ce89d4bbd0cbef5fb6bab06079e3047 100644 (file)
@@ -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 (file)
index 3d57a08..0000000
+++ /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 (file)
index 3912109..0000000
+++ /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.
-\f
-                   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.)
-\f
-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.
-\f
-  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.
-\f
-  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
-\f
-           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.
index f1f2144ffedd38e12e8c4ad5094eaad26406f3fa..06268bd8400e24abc8065dcd4282f66f018c1234 100644 (file)
@@ -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 : -->
index 2495a83b6d0f9d0d3e7e504ebb701dea38337397..6fd0ae49cc9fe7e7d0cbb74f772114a2fd530995 100644 (file)
@@ -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.
index 1e7244431dee340e90bac94135b0db2a2ad55a9f..dfdd22a379e253380fb0725691623972c016f2a2 100644 (file)
@@ -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, :, :)
index 74030f413c86b178412cba13eb4b05c06b28647c..5efc0afbc158e4147a7933ef8d2ab87bb685cd2a 100644 (file)
@@ -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)
 {
index ea1c71ac33af00e83ec304e4658f86603e8f489e..853914547c8bdca83cfb6f1cd0f78333f91f0e2e 100644 (file)
@@ -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 */
 
index 545d11e19b48d74b8b992d76fb01a37f7aafa2a2..e1e7c19acaedc2a011e615065111196141850d43 100644 (file)
@@ -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 $";
 
 /*
 **
index 754b2c70d97d845b82d3864e18df9825c48fb1eb..28a3ac21dae326089c9f8c3acfc8a0bf2d57cfdb 100644 (file)
@@ -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)
 {
index 5071df7681ea6c052700337e3ad2ded7acb3ddc2..254287370f74deccbb74c96986721c6da355ee34 100644 (file)
@@ -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)
 {
index c0f129fae7f15bf7830c6ea337b63f3664bc92e4..0a5f103d8d3e64d863fb71195ee4fad9dd1dc6ef 100644 (file)
@@ -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.
index d494fd2215dfd4b3f08822bb6b09d26fd03dc29a..0360b5b2c99a05947be6e6d64299c71de9dd8eb2 100644 (file)
@@ -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)
index 3c01ecf55e317c485f4f520b352330f51dd56a25..6f869d54300de68f83d20a1c8107efb6ace8cb60 100644 (file)
@@ -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)
index 5679392705b0437e4bac8e28f134d830f453253c..adaee406d4fa4a05b59e02904f750b3fa41906c7 100644 (file)
@@ -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)
 {
index fc977aa9de54a37135349a36a1d05060f1877363..7479725b1dd11a02eee680185e1b2596ae0b596e 100644 (file)
@@ -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.
 
index 7928aa7e075b089fe54c86182d91c6665e634f9d..c31d9b1a103aa5c19bccaa3772c810920d61eaa8 100644 (file)
@@ -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 */
index 2e542d5478c7f30823656177aad15e02db82beb6..aa8b02822f1aedf036c99fc42e44761f6b496382 100644 (file)
@@ -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)
 {
index 89472e3bdfe79a5f580fa7311b8d979b80dc2f30..828a4550d4d3865de89da732a7d5dc54b2c749e7 100644 (file)
@@ -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)
 {
index 98e25e5346ec6e5e947fe62205423e08a1bf11e2..9ff45f283302eca2f7296fec1e18056c4b908b59 100644 (file)
@@ -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";
 
index fa64091598301bc43efa7f965f55493211c7e32b..c3cdc10e7964e120b9d1f3d28a71a54b4de44efc 100644 (file)
@@ -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";
 
index 2723af0b26dfd1896eb000bf374da2b3863967ff..5a440f5df6cacf325eb1777e92bb6d84e891f67c 100644 (file)
@@ -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";
 
index b86b05a28f8df66795e367f53167760bfdf87bfc..cb9e6a9fcc79a74ffa9cd49241ff19247f77cd47 100644 (file)
@@ -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";
 
index eb544c3ce7e29e0998d2efc6d7011cfa9c1bdf91..314b2f38c2d6b974bc93d19ef52481f5f897864e 100644 (file)
@@ -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)
 {
index 3435148e7ccd33b602947971818c4e5dd9b42bd3..567f912bb87db0c995522063a5c28a851fe2e93f 100644 (file)
@@ -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)
 {
index 12ee9ce14088833e8d9a26b35ac860e8416a6c8f..0c29b07e4f047f0ee65485f590357e9f73c98714 100644 (file)
@@ -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)
 {
index 0a7dcaa8d78fef0a45717e491ace63f967353fed..1c903122f6153bf509aa1d1572799deed8386d8c 100644 (file)
@@ -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)
 {
index d4ec92d3f6cd156bae1d8ea16c82e0c98b848cb4..fd9d1d1a0314c999ee5b6860a9b36266821740b0 100644 (file)
@@ -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)
 {
index be7e051b0baac7f85d7ed27ecb77c885bb811a8f..63307f2214a42e18824caf3e7f9a1481f73c7c3d 100644 (file)
@@ -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)
 {
index 50f3f74226606b0df1a849cdc7067f05fbb0e2de..5843ed241dcea2835484bfe0c01dc7e6d0326055 100644 (file)
@@ -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)
 {
index f7a4eaa6576bd1ec544d54c54b0e3d6e158388d0..6f32f73d68b97decbe382b3dc84cffc3e99f259d 100644 (file)
@@ -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
index 3252f32cf4c00bc0b6a29fb377a0c2c459ba2ba1..f6f415edf46b850e4b8da2e868471c125cfd4940 100644 (file)
@@ -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
index 1a76c30abed9d79eafe1da8c30acc9ad5bf93561..0582cae76f492fc1ab349ab4458e889b2ea576ae 100644 (file)
   :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)
index d5c95d802b701144149c9cc61fa11be0e0791a02..d7fbbca9471b374b8ce26b22bd15084a2a8a690d 100644 (file)
@@ -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]).
index c1827cc2691f66cb0f566668fede9116bfb62776..89361498f8e27b38ad769667c17794485c051f31 100644 (file)
@@ -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)
index 57dca702f116a0bdfdb5e171fb008f6503db7ec7..54c4dea4560b422f39a47cc5304b6de0b1a0484d 100644 (file)
@@ -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
index ca7fbf21f19262d6dd69c0f8cc1429685817fb71..00754254b87bc387420529a66082a3887ec69a4e 100644 (file)
@@ -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;
index 126593d177d50f5b84f0d84636ae015853fe7298..a490917f9fa59d5a9a042d0c85030bc7aad42c8e 100644 (file)
@@ -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);
index dded39ea5df645799eced2657fa4db1ee20bbe52..537d5a4d1625d0747b1fe74f2045a150ee4b67d9 100644 (file)
@@ -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;
index 9e97052d4c500c4c1d67b95022237e6a30d91dc5..7e30e8d5bc870fecce053659bde0a72d1727093d 100644 (file)
@@ -1,4 +1,5 @@
 test-results
 corpus.mail
 smtp-dummy
+symbol-test
 tmp.*
index 8eb0433098b20958e42d6831e5bcc4e3e6e5af6d..bffbbdbdc0e2aa797b8895baef4049fab9a59003 100644 (file)
@@ -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
index 2481f16d1ebefac322f855ef2c8b043a17988185..2e757e0eeca8d1c7641848bbb744ee681008fb9c 100644 (file)
@@ -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:
 
index 38db2baf74d97f0c27878a03fe5ecf207bcb1052..4edf8315be8aaab92d648eddb8f1da70b1cef3ef 100755 (executable)
@@ -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
index 75a0a7442f4b46921afa698f1bde9732e8482895..3f8c72d34e1afa16220bf8932dea7d09c74c7d0a 100755 (executable)
@@ -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 (file)
index 4923594..0000000
+++ /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 (file)
index 0000000..41e2aaa
--- /dev/null
@@ -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 (file)
index 0000000..fa2108e
--- /dev/null
@@ -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
+
index 113ea7cf3330ee8b51fdf9fde3c2ec5fd3ec524c..53ce355cae1dfeed8bd09ef9ef5df46c82e3ebad 100755 (executable)
@@ -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/*
index b7e265a80da79fd66a69498e95b8cea4168a0071..99d3a3bf0460ee55009c72dcba1814cc59995ff8 100755 (executable)
--- 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
index d0b31aec115ce83b92f37b4fc3e161d9601cadb0..f67b653eb747a3d64a7d4b1ae518ec0c80ed4184 100755 (executable)
@@ -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'
index 1de06eae41032f0c0d5b05c840f3942cfff043da..1548ca400a544f3ddb325ce02826de1531cf602d 100644 (file)
@@ -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;
 }
old mode 100755 (executable)
new mode 100644 (file)
index cf309f9..a975957
@@ -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
index 2ff42b3d85f2e644b45fd8f51c0ada496395027c..03408995b4fd35c44d98f46313f6629e31ae7184 100644 (file)
@@ -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