]> git.cworth.org Git - notmuch/commitdiff
Merge in ruby bindings.
authorCarl Worth <cworth@cworth.org>
Mon, 8 Nov 2010 18:08:27 +0000 (10:08 -0800)
committerCarl Worth <cworth@cworth.org>
Mon, 8 Nov 2010 18:08:34 +0000 (10:08 -0800)
Thanks to Ali Polatel for these bindings. This code was fetched from
the ruby branch of:

git://github.com/alip/notmuch.git

129 files changed:
Makefile
Makefile.local
NEWS
RELEASING
TODO
bindings/python/notmuch/__init__.py
bindings/python/notmuch/message.py
configure
debian/changelog
debian/control
debian/libnotmuch1.symbols [new file with mode: 0644]
debian/notmuch.emacsen-startup
emacs/notmuch-hello.el
emacs/notmuch-lib.el
emacs/notmuch-maildir-fcc.el
emacs/notmuch-mua.el
emacs/notmuch-query.el
emacs/notmuch-show.el
emacs/notmuch.el
lib/Makefile.local
lib/database-private.h
lib/database.cc
lib/directory.cc
lib/index.cc
lib/libsha1.h
lib/message.cc
lib/notmuch-private.h
lib/notmuch.h
lib/query.cc
lib/thread.cc
lib/xutil.h
notmuch-client.h
notmuch-config.c
notmuch-reply.c
notmuch-search.c
notmuch-setup.c
notmuch-show.c
notmuch.1
notmuch.c
test/.gitignore [new file with mode: 0644]
test/Makefile [new file with mode: 0644]
test/Makefile.local [new file with mode: 0644]
test/README [new file with mode: 0644]
test/aggregate-results.sh [new file with mode: 0755]
test/author-order [new file with mode: 0755]
test/basic [new file with mode: 0755]
test/corpus/01 [new file with mode: 0644]
test/corpus/02 [new file with mode: 0644]
test/corpus/03 [new file with mode: 0644]
test/corpus/04 [new file with mode: 0644]
test/corpus/05 [new file with mode: 0644]
test/corpus/06 [new file with mode: 0644]
test/corpus/07 [new file with mode: 0644]
test/corpus/08 [new file with mode: 0644]
test/corpus/09 [new file with mode: 0644]
test/corpus/10 [new file with mode: 0644]
test/corpus/11 [new file with mode: 0644]
test/corpus/12 [new file with mode: 0644]
test/corpus/13 [new file with mode: 0644]
test/corpus/14 [new file with mode: 0644]
test/corpus/15 [new file with mode: 0644]
test/corpus/16 [new file with mode: 0644]
test/corpus/17 [new file with mode: 0644]
test/corpus/18 [new file with mode: 0644]
test/corpus/19 [new file with mode: 0644]
test/corpus/20 [new file with mode: 0644]
test/corpus/21 [new file with mode: 0644]
test/corpus/22 [new file with mode: 0644]
test/corpus/23 [new file with mode: 0644]
test/corpus/24 [new file with mode: 0644]
test/corpus/25 [new file with mode: 0644]
test/corpus/26 [new file with mode: 0644]
test/corpus/27 [new file with mode: 0644]
test/corpus/28 [new file with mode: 0644]
test/corpus/29 [new file with mode: 0644]
test/corpus/30 [new file with mode: 0644]
test/corpus/31 [new file with mode: 0644]
test/corpus/32 [new file with mode: 0644]
test/corpus/33 [new file with mode: 0644]
test/corpus/34 [new file with mode: 0644]
test/corpus/35 [new file with mode: 0644]
test/corpus/36 [new file with mode: 0644]
test/corpus/37 [new file with mode: 0644]
test/corpus/38 [new file with mode: 0644]
test/corpus/39 [new file with mode: 0644]
test/corpus/40 [new file with mode: 0644]
test/corpus/41 [new file with mode: 0644]
test/corpus/42 [new file with mode: 0644]
test/corpus/43 [new file with mode: 0644]
test/corpus/44 [new file with mode: 0644]
test/corpus/45 [new file with mode: 0644]
test/corpus/46 [new file with mode: 0644]
test/corpus/47 [new file with mode: 0644]
test/corpus/48 [new file with mode: 0644]
test/corpus/49 [new file with mode: 0644]
test/corpus/50 [new file with mode: 0644]
test/dump-restore [new file with mode: 0755]
test/emacs [new file with mode: 0755]
test/emacs.expected-output/attachment [new file with mode: 0644]
test/emacs.expected-output/notmuch-hello [new file with mode: 0644]
test/emacs.expected-output/notmuch-hello-no-saved-searches [new file with mode: 0644]
test/emacs.expected-output/notmuch-hello-view-inbox [new file with mode: 0644]
test/emacs.expected-output/notmuch-hello-with-empty [new file with mode: 0644]
test/emacs.expected-output/notmuch-search-tag-inbox [new file with mode: 0644]
test/emacs.expected-output/notmuch-show-thread-maildir-storage [new file with mode: 0644]
test/emacs.expected-output/raw-message-cf0c4d-52ad0a [new file with mode: 0644]
test/encoding [new file with mode: 0755]
test/from-guessing [new file with mode: 0755]
test/json [new file with mode: 0755]
test/long-id [new file with mode: 0755]
test/new [new file with mode: 0755]
test/notmuch-test
test/raw [new file with mode: 0755]
test/reply [new file with mode: 0755]
test/search [new file with mode: 0755]
test/smtp-dummy.c [new file with mode: 0644]
test/test-lib.sh [new file with mode: 0644]
test/thread-naming [new file with mode: 0755]
test/thread-order [new file with mode: 0755]
test/uuencode [new file with mode: 0755]
test/valgrind/suppressions [new file with mode: 0644]
test/valgrind/valgrind.sh [new file with mode: 0755]
version
vim/README
vim/plugin/notmuch.vim
vim/syntax/notmuch-git-diff.vim [new file with mode: 0644]
vim/syntax/notmuch-search.vim
vim/syntax/notmuch-show.vim
xutil.c [new file with mode: 0644]

index 619392d39562aa1f6553e762b91b01106f689061..7549b40d171fdde519697e5fe3d67de307ddbf20 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -3,7 +3,7 @@
 all:
 
 # List all subdirectories here. Each contains its own Makefile.local
-subdirs = compat completion emacs lib
+subdirs = compat completion emacs lib test
 
 # We make all targets depend on the Makefiles themselves.
 global_deps = Makefile Makefile.config Makefile.local \
index bc61a3c108f0db972987a6e5aa0ab6dc0262f1a6..f9b5a9b3417c7bec2c159df3e656a71434c6676c 100644 (file)
@@ -31,18 +31,16 @@ GPG_FILE=$(SHA1_FILE).asc
 # Smash together user's values with our extra values
 FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CFLAGS) $(WARN_CFLAGS) $(CONFIGURE_CFLAGS) $(extra_cflags)
 FINAL_CXXFLAGS = $(CXXFLAGS) $(WARN_CXXFLAGS) $(CONFIGURE_CXXFLAGS) $(extra_cflags) $(extra_cxxflags)
-FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch
+FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
 FINAL_NOTMUCH_LINKER = CC
 ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
 FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
 FINAL_NOTMUCH_LINKER = CXX
 endif
-ifeq ($(PLATFORM),LINUX)
 ifeq ($(LIBDIR_IN_LDCONFIG),0)
-FINAL_NOTMUCH_LDFLAGS += -Wl,--enable-new-dtags -Wl,-rpath,$(libdir)
+FINAL_NOTMUCH_LDFLAGS += $(RPATH_LDFLAGS)
 endif
-endif
-FINAL_LIBNOTMUCH_LDFLAGS = $(LDFLAGS) $(CONFIGURE_LDFLAGS)
+FINAL_LIBNOTMUCH_LDFLAGS = $(LDFLAGS) $(AS_NEEDED_LDFLAGS) $(CONFIGURE_LDFLAGS)
 
 .PHONY: all
 all: notmuch notmuch-shared notmuch.1.gz
@@ -61,10 +59,6 @@ ifeq ($(shell cat .first-build-message 2>/dev/null),)
 endif
 endif
 
-.PHONY: test
-test: all
-       @./test/notmuch-test
-
 $(TAR_FILE):
        git archive --format=tar --prefix=$(PACKAGE)-$(VERSION)/ HEAD > $(TAR_FILE).tmp
        echo $(VERSION) > version.tmp
@@ -255,7 +249,8 @@ notmuch_client_srcs =               \
        notmuch-time.c          \
        query-string.c          \
        show-message.c          \
-       json.c
+       json.c                  \
+       xutil.c
 
 notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
 
diff --git a/NEWS b/NEWS
index 2d5e398f24285efe8a8f11d94abded6c559d4399..bf98f4620ac7c6e162875411b0fd0b74b415e378 100644 (file)
--- a/NEWS
+++ b/NEWS
+New command-line features
+-------------------------
+New "notmuch show --format=raw" for getting at original email contents
+
+  This new feature allows for a fully-functional email client to be
+  built on top of the notmuch command-line without needing any direct
+  access to the mail store itself.
+
+  For example, it's now possible to run "emacs -f notmuch" on a local
+  machine with only ssh access to the mail store/notmuch database. To
+  do this, simply set the notmuch-command variable in emacs to the
+  name of a script containing:
+
+       ssh user@host notmuch "$@"
+
+  If the ssh client has enabled connection sharing (ControlMaster
+  option in OpenSSH), the emacs interface can be quite responsive this
+  way.
+
+General bug fixes
+-----------------
+Fix "notmuch search" to print nothing when nothing matches
+
+  The 0.4 release had a bug in which:
+
+       notmuch search <expression-with-no-matches>
+
+  would produce a single blank line of output, (where previous
+  versions would produce no output. This fix also causes a change in
+  the --format=json output, (which would previously produce "[]" and
+  now produces nothing).
+
+Notmuch 0.4 (2010-11-01)
+========================
+New command-line features
+-------------------------
+notmuch search --output=(summary|threads|messages|tags|files)
+
+  This new option allows for particular items to be returned from
+  notmuch searches. The "summary" option is the default and behaves
+  just as "notmuch search" has historically behaved.
+
+  The new option values allow for thread IDs, message IDs, lists of
+  tags, and lists of filenames to be returned from searches. It is
+  expected that this new option will be very useful in shell
+  scripts. For example:
+
+       for file in $(notmuch search --output=files <search-terms>); do
+               <operations-on> "$file"
+       done
+
+notmuch show --format=mbox <search-specification>
+
+  This new option allows for the messages matching a search
+  specification to be presented as an mbox. Specifically the "mboxrd"
+  format is used which allows for reversible quoting of lines
+  beginning with "From ". A reader should remove a single '>' from the
+  beginning of all lines beginning with one or more '>' characters
+  followed by the 5 characters "From ".
+
+notmuch config [get|set] <section>.<item> [value ...]
+
+  The new top-level "config" command allows for any value in the
+  notmuch configuration file to be queried or set to a new value. Both
+  single-valued and multi-valued items are supported, as our any
+  custom items stored in the configuration file.
+
+Avoid setting Bcc header in "notmuch reply"
+
+  We decided that this was a bit heavy-handed as the actual mail
+  user-agent should be responsible for setting any Bcc option. Also,
+  see below for the notmuch/emacs user-agent now setting an Fcc by
+  default rather than Bcc.
+
+New library features
+--------------------
+Add notmuch_query_get_query_string and notmuch_query_get_sort
+
+  These are simply functions for querying properties of a
+  notmuch_query_t object.
+
 New emacs features
 ------------------
-Add a new, optional hook for detecting inline patches
+Enable Fcc of all sent messages by default (to "sent" directory)
+
+  All messages sent from the emacs interface will now be saved to the
+  notmuch mail store where they will be incorporated to the database
+  by the next "notmuch new". By default, messages are saved to the
+  "sent" directory at the top-level of the mail store. This directory
+  can be customized by means of the "Notmuch Fcc Dirs" option in the
+  notmuch customize interface.
+
+Ability to all open messages in a thread to a pipe
+
+  Historically, the '|' keybinding allows for piping a single message
+  to an external command. Now, by prefixing this key with a prefix
+  argument, (for example, by pressing "Control-U |"), all open
+  messages in the current thread will be sent to the external command.
+
+Optional support for detecting inline patches
 
   This hook is disabled by default but can be enabled with a checkbox
-  under ""Notmuch Show Insert Text/Plain Hook" in the notmuch
-  customize interface. It allows for inline patches to be detected and
-  treated as if they were attachments, (with context-sensitive
-  highlighting).
+  under "Notmuch Show Insert Text/Plain Hook" in the notmuch customize
+  interface. It allows for inline patches to be detected and treated
+  as if they were attachments, (with context-sensitive highlighting).
 
 Automatically tag messages as "replied" when sending a reply
 
-  This feature adds a "replied" tag by default, but can easily be
-  customized to add or remove other tags as well. For example, a user
-  might use a tag of "needs-reply" and can configure this feature to
-  automatically remove that tag when replying. See "Notmuch Message
-  Mark Replied" in the notmuch customize interface.
+  Messages replied to within the emacs interface will now be tagged as
+  "replied". This feature can easily be customized to add or remove
+  other tags as well. For example, a user might use a tag of
+  "needs-reply" and can configure this feature to automatically remove
+  that tag when replying. See "Notmuch Message Mark Replied" in the
+  notmuch customize interface.
+
+Allow search-result color specifications to overlay each other
+
+  For example, one tag can specify the background color of matching
+  lines, while another can specify the foreground. With this change,
+  both settings will now be visible simultaneously, (which was not the
+  case in previous releases). See "Notmuch Search Line Faces" in the
+  notmuch customize interface.
+
+Make hidden author names still available for incremental search.
+
+  When there is insufficient space to display all authors of a thread
+  in search results, the names of hidden authors are now still made
+  available to emacs' incremental search commands. As the user
+  searches, matching lines will temporarily expand to show the hidden
+  names.
 
-Emacs mail improvements
+New binding of Control-TAB (works like TAB in reverse)
+
+  Many notmuch nodes already use TAB to navigate forward through
+  various items allowing actions, (message headers, email attachments,
+  etc.). The new Control-TAB binding operates similarly but in the
+  opposite direction.
+
+New build-system features
+-------------------------
+Various portability fixes have been applied
+
+  These include fixes for build failures on at least Solaris, FreeBSD,
+  and Fedora systems. We're hopeful that the notmuch code base is now
+  more portable than ever before.
+
+Arrange for libnotmuch to be found automatically after make install
+
+  The notmuch build system is now careful to help the user avoid
+  errors of the form "libnotmuch.so could not be found" immediately
+  after installing. This support takes two forms:
+
+       1. If the library is installed to a system directory,
+          (configured in /etc/ld.so.conf), then "make install" will
+          automatically run ldconfig.
+
+       2. If the library is installed to a non-system directory, the
+          build system adds a DR_RUNPATH entry to the final binary
+          pointing to the directory to which the library is installed.
+
+  When this support works, the user should be able to run notmuch
+  immediately after "make install", without any errors trying to find
+  the notmuch library, and without having to manually set environment
+  variables such as LD_LIBRARY_PATH.
+
+Check compiler/linker options before using them
+
+  The configure script now carefully checks that any desired
+  compilation options, (whether for enabling compiler warnings, or for
+  embedding rpath, etc.), are supported. Only supported options are
+  used in the resulting Makefile.
+
+New test-suite features
 -----------------------
+New modularization of test suite.
+
+  Thanks to a gracious relicensing of the test-suite infrastructure
+  from the git project, notmuch now has a modular test suite. This
+  provides the ability to run individual sections of the test suite
+  rather than the whole things. It also provides better summary of
+  test results, with support for tests that are expected to fail
+  (BROKEN and FIXED) in addition to PASS and FAIL. Finally, it makes
+  it easy to run the test suite within valgrind (pass --valgrind to
+  notmuch-test or to any sub-script) which has been very useful.
+
+New testing of emacs interface.
+
+  The test suite has been augmented to allow automated testing of the
+  emacs interfaces. So far, this includes basic searches, display of
+  threads, and tag manipulation. This also includes a test that a new
+  message can successfully be sent out through a (dummy) SMTP server
+  and that said message is successfully integrated into the notmuch
+  database via the FCC setting.
+
+General bug fixes
+-----------------
+Fix potential corruption of database when "notmuch new " is interrupted.
+
+  Previously, an interruption of "notmuch new" would (rarely) result
+  in a corrupt database. The corruption would manifest itself by a
+  persistent error of the form:
+
+       document ID of 1234 has no thread ID
+
+  The message-adding code has been carefully audited and reworked to
+  avoid this sort of corruption regardless of when it is interrupted.
+
+Fix failure with extremely long message ID headers.
+
+  Previously, a message with an extremely long message ID, (say, more
+  than 300 characters), would fail to be added to notmuch, (triggering
+  Xapian exceptions). This has now been fixed.
+
+Fix for messages with "charset=unknown-8bit"
+
+  Previously, messages with this charset would cause notmuch to emit a
+  GMime warning, (which would then trip up emacs or other interfaces
+  parsing the notmuch results).
+
+Fix notmuch_query_search_threads function to return NULL on any exception
+
+Fix "notmuch search" to return non-zero if notmuch_query_search_threads fails
+
+  Previously, this command could confusingly report a Xapian
+  exception, yet still return an error code of 0. It now correctly
+  returns a failing error code of 1 in this case.
+
+Emacs bug fixes
+---------------
+Fix to handle a message with a subject containing, for example "[1234]"
+
+  Previously, a message subject containing a sequence of digits within
+  square brackets would cause the emacs interface to mis-parse the
+  output of "notmuch search". This would result in the message being
+  mis-displayed and prevent the user from manipulating the message in
+  the emacs interface.
+
+Fix to correctly handle message IDs containing ".."
+
+  The emacs interface now properly quotes message IDs to avoid a
+  Xapian bug in which the ".." within a message ID would be
+  misinterpreted as a numeric range specification.
+
+Python-binding fixes
+--------------------
+The python bindings for notmuch have been updated to work with python3.
+
+Debian-specific fixes
+---------------------
+Fix emacs initialization so "M-x notmuch" works for users by default.
+
+  Now, a new Debian user can immediately run "emacs -f notmuch" after
+  "apt-get install notmuch". Previously, the user would have had to
+  edit the ~/.emacs file to add "(require 'notmuch)" before this would
+  work.
 
-Easier way to define a fcc directory
-    
-  In the common case that a user only has one FCC (save outgoing mail
-  in the Mail directory, it is now possible to simply configure a
-  string such as "Sent" in the notmuch-fcc-dirs variable. More complex
-  options, depending on a users email address, are possible and
-  described in the variable customization help text.
-    
 Notmuch 0.3.1 (2010-04-27)
 ==========================
 General bug fixes
index e9cb3d0bfbe2040dcf46a4edcee1f6086ebb6ef2..6c714f8e48a691ab8ae32fd7ecd22c3e71f52439 100644 (file)
--- a/RELEASING
+++ b/RELEASING
@@ -48,10 +48,10 @@ repository. From here, there are just a few steps to release:
        as "1.1" or "1.2").
 
        Finally, releases that do not change "features" but are merely
-       bug fixes either add increase the micro number or add it
-       (starting at ".1" if not present). So a bug-fix release from
-       "1.0" would be "1.0.1" and a subsequent bug-fix release would
-       be "1.0.2" etc.
+       bug fixes either increase the micro number or add it (starting
+       at ".1" if not present). So a bug-fix release from "1.0" would
+       be "1.0.1" and a subsequent bug-fix release would be "1.0.2"
+       etc.
 
        Commit this change.
 
diff --git a/TODO b/TODO
index f65e59ab54d7d90b39e73157a59937af182ce4b1..9834d73a7476e9d5ddab1e4de189a4d6ff37ccc0 100644 (file)
--- a/TODO
+++ b/TODO
@@ -6,8 +6,15 @@ Fix the things that are causing the most pain to new users
 
 Emacs interface (notmuch.el)
 ----------------------------
-Enhance '+' and '-' in the search view to operate on an entire region
-if set.
+Add notmuch-bcc and notmuch-cc for setting default Bcc and Cc values,
+(should affect the message-setup-hook).
+
+Switch the notmuch-search view to use "notmuch search --format=json"
+to fix large classes of bugs regarding poorly-escaped output and lame
+regular expressions. (The most recently found, unfixed example is the
+sender's name containing ';' which causes emacs to drop a search
+result.) This may require removing the outer array from the current
+"notmuch search --format=json" results.
 
 Fix '*' to work by simply calling '+' or '-' on a region consisting of
 the entire buffer.
@@ -31,8 +38,6 @@ Add support to "mute" a thread (add a "muted" tag and then don't
 display threads in searches by default where any message of the thread
 has the "muted" tag).
 
-Fix i-search to open up invisible citations as necessary.
-
 Make '=' count from the end rather than from the beginning if more
 than half-way through the buffer.
 
@@ -41,10 +46,6 @@ sending. This should probably just be fixed in message-mode itself,
 (but perhaps we can have a notmuch-message-mode that layers this on
 top).
 
-Implement Fcc and use it for all messages, (whether a new composition,
-a reply, or a forward). This again may require a notmuch-message-mode
-that extends message-mode.
-
 Stop hiding the headers so much in the thread-view mode.
 
 Allow opening a message in thread-view mode by clicking on either
@@ -56,65 +57,9 @@ Change 'a' command in thread-view mode to only archive open messages.
 
 Add a binding to open all closed messages.
 
-Make all authors and subjects available to isearch, (hidden by default
-but with magic expansion while isearching).
-
-Fix notmuch-hello as follows:
-
-       1. Change the "notmuch" and message count in the welcome sentence to
-          not be buttons.
-
-       2. Put the saved searches (if any) before the search bar.
-
-       3. When `notmuch-hello' (or even `notmuch' when it gets its new name)
-          is invoked directly, move to the first button, (go to point-min and
-          then call widget-forward). That is, if the user has any saved
-          searches, then point will be on the first one. If the user has no
-          saved searches, then point will be on the search bar.
-
-       4. Fix refresh of notmuch-hello to leave point in the same logical
-          place, (the same saved-search widget at least).
-
 Change the 'a'rchive command in the thread view to only archive open
 messages.
 
-Emacs saved-search interface
-----------------------------
-Here's a proposal Carl wrote (id:87einafy4u.fsf@yoom.home.cworth.org):
-
-  So what I'm imagining for the default notmuch view is something like
-  this:
-
-          Welcome to notmuch.
-
-              Notmuch search: _________________________________________
-
-          Saved searches:
-
-              55,342      All messages
-                  22      Inbox
-
-          Recent searches:
-
-                   1      from:"someone special" and tag:unread
-                  34      tag:notmuch and tag:todo
-
-          Click (or press Enter) on any search to see the results.
-          Right-click (or press Space) on any recent search to save it.
-
-  So the "saved searches" portion of the view is basically just what
-  notmuch-folder displays now. Above that there's an obvious place to
-  start a new search, (in a slightly more "web-browser-like" way than the
-  typical mini-buffer approach).
-
-  All recent searches appear in the list at the bottom automatically, and
-  there's the documented mechanism for saving a search, (giving it a name
-  and having it appear above).
-
-Portability
------------
-Fix configure script to test each compiler warning we want to use.
-
 Completion
 ----------
 Fix bash completion to complete multiple search options (both --first
@@ -123,6 +68,13 @@ and *then* --max-threads), and also complete value for --sort=
 
 notmuch command-line tool
 -------------------------
+Replace "notmuch reply" with "notmuch compose --reply <search-terms>".
+This would enable a plain "notmuch compose" to be used to construct an
+initial message, (which would then have the properly configured name
+and email address in the From: line. We could also then easily support
+"notmuch compose --from <something>" to support getting at alternate
+email addresses.
+
 Fix the --format=json option to not imply --entire-thread.
 
 Implement "notmuch search --exclude-threads=<search-terms>" to allow
@@ -136,21 +88,9 @@ option (or similar) to "notmuch show".) For now, this is being worked
 around in the emacs interface by noticing that "notmuch show" returns
 nothing and re-rerunning the command without the extra arguments.
 
-Teach "notmuch search" to return many different kinds of results. Some
-ideas:
-
-       notmuch search --output=threads # Default if no --output is given
-       notmuch search --output=messages
-       notmuch search --output=tags
-       notmuch search --output=addresses
-       notmuch search --output=terms
-
 Add a "--format" option to "notmuch search", (something printf-like
 for selecting what gets printed).
 
-Add a "--count-only" (or so?) option to "notmuch search" for returning
-the count of search results.
-
 Give "notmuch restore" some progress indicator.
 
 Fix "notmuch restore" to operate in a single pass much like "notmuch
@@ -202,8 +142,6 @@ Make failure to read a file (such as a permissions problem) a warning
 rather than an error (should be similar to the existing warning for a
 non-mail file).
 
-Actually compile and install a libnotmuch shared library.
-
 Fix to use the *last* Message-ID header if multiple such headers are
 encountered, (I noticed this is one thing that kept me from seeing the
 same message-ID values as sup).
@@ -288,7 +226,7 @@ fix old messages to be consistent.
 Start indexing the List-Id header, (and re-index this header for
 existing messages at the next database upgrade).
 
-Start indexing the message file's directory ana make it available for
+Start indexing the message file's directory and make it available for
 search as "folder:" (and re-index this value for existing messages at
 the next database upgrade).
 
@@ -297,24 +235,14 @@ re-index these for existing messages at the next database upgrade).
 
 Test suite
 ----------
-Start testing --format=json.
-
 Achieve 100% test coverage with the test suite.
 
-Modularize test suite (to be able to run individual tests).
-
-Summarize test results at the end.
-
-Fix the insane quoting nightmare of the test suite, (and once we do
-that we can actually test the implicit-phrase search feature such as
-"notmuch search 'body search (phrase)'"
-
-Test "notmuch reply" choosing the correct email address from the
-Received header when no configured email address appears in To or Cc.
-
 General
 -------
 Audit everything for dealing with out-of-memory (and drop xutil.c).
 
 Investigate why the notmuch database is slightly larger than the sup
 database for the same corpus of email.
+
+Makefile should print message teaching user about LD_LIBRARY_PATH (or
+similar) if libdir is not set to a directory examined by ldconfig.
index 56a4f2a4ef143fc0f7db473282bf027299cce50e..5ba1337f2ab43b3c36e682686febc2361fe35440 100644 (file)
@@ -51,10 +51,10 @@ along with notmuch.  If not, see <http://www.gnu.org/licenses/>.
 
 Copyright 2010 Sebastian Spaeth <Sebastian@SSpaeth.de>'
 """
-from database import Database, Query
-from message import Messages, Message
-from thread import Threads, Thread
-from tag import Tags
+from notmuch.database import Database, Query
+from notmuch.message import Messages, Message
+from notmuch.thread import Threads, Thread
+from notmuch.tag import Tags
 from notmuch.globals import nmlib, STATUS, NotmuchError
 __LICENSE__="GPL v3+"
 __VERSION__='0.2.2'
index 613cc4abc2be7f89c1501cbabaf008288e50204e..ac85cbb453b83aa474298f43cd0066b40b23b28f 100644 (file)
@@ -748,7 +748,7 @@ class Message(object):
         output += "\n\fbody{"
 
         parts = format["body"]
-        parts.sort(key=lambda(p): p["id"])
+        parts.sort(key=lambda x: x['id'])
         for p in parts:
             if not p.has_key("filename"):
                 output += "\n\fpart{ "
index c86a227cd7268fe622303efeb86ebf5063285574..bab25016a1162ecadf3e965a083d37594ac179d5 100755 (executable)
--- a/configure
+++ b/configure
@@ -15,7 +15,7 @@ CXX=${CXX:-g++}
 CFLAGS=${CFLAGS:--O2}
 CXXFLAGS=${CXXFLAGS:-\$(CFLAGS)}
 LDFLAGS=${LDFLAGS:-}
-XAPIAN_CONFIG=${XAPIAN_CONFIG:-xapian-config-1.1${tab}xapian-config}
+XAPIAN_CONFIG=${XAPIAN_CONFIG:-xapian-config}
 
 # We don't allow the EMACS or GZIP Makefile variables inherit values
 # from the environment as we do with CC and CXX above. The reason is
@@ -275,21 +275,21 @@ fi
 libdir_in_ldconfig=0
 
 printf "Checking which platform we are on... "
-if [ `uname` = "Darwin" ] ; then
+uname=`uname`
+if [ $uname = "Darwin" ] ; then
     printf "Mac OS X.\n"
     platform=MACOSX
     linker_resolves_library_dependencies=0
-elif [ `uname` = "SunOS" ] ; then
+elif [ $uname = "SunOS" ] ; then
     printf "Solaris.\n"
     platform=SOLARIS
     linker_resolves_library_dependencies=0
-elif [ `uname` = "Linux" ] ; then
+elif [ $uname = "Linux" ] ; then
     printf "Linux\n"
     platform=LINUX
     linker_resolves_library_dependencies=1
     ldconfig_paths=$(/sbin/ldconfig -N -X -v 2>/dev/null | sed -n -e 's,^\(/.*\):\( (.*)\)\?$,\1,p')
     for path in $ldconfig_paths; do
-       echo "Checking $path compared to $libdir_expanded"
        if [ "$path" = "$libdir_expanded" ]; then
            libdir_in_ldconfig=1
        fi
@@ -390,6 +390,50 @@ else
 fi
 rm -f compat/have_strcasestr
 
+printf "int main(void){return 0;}\n" > minimal.c
+
+printf "Checking for rpath support... "
+if ${CC} -Wl,--enable-new-dtags -Wl,-rpath,/tmp/ -o minimal minimal.c >/dev/null 2>&1
+then
+    printf "Yes.\n"
+    rpath_ldflags="-Wl,--enable-new-dtags -Wl,-rpath,\$(libdir)"
+else
+    printf "No (nothing to worry about).\n"
+    rpath_ldflags=""
+fi
+
+printf "Checking for -Wl,--as-needed... "
+if ${CC} -Wl,--as-needed -o minimal minimal.c >/dev/null 2>&1
+then
+    printf "Yes.\n"
+    as_needed_ldflags="-Wl,--as-needed"
+else
+    printf "No (nothing to worry about).\n"
+    as_needed_ldflags=""
+fi
+
+WARN_CXXFLAGS=""
+printf "Checking for available C++ compiler warning flags... "
+for flag in -Wall -Wextra -Wwrite-strings -Wswitch-enum; do
+    if ${CC} $flag -o minimal minimal.c > /dev/null 2>&1
+    then
+       WARN_CXXFLAGS="${WARN_CXXFLAGS}${WARN_CXXFLAGS:+ }${flag}"
+    fi
+done
+printf "\n\t${WARN_CXXFLAGS}\n"
+
+WARN_CFLAGS="${WARN_CXXFLAGS}"
+printf "Checking for available C compiler warning flags... "
+for flag in -Wmissing-declarations; do
+    if ${CC} $flag -o minimal minimal.c > /dev/null 2>&1
+    then
+       WARN_CFLAGS="${WARN_CFLAGS}${WARN_CFLAGS:+ }${flag}"
+    fi
+done
+printf "\n\t${WARN_CFLAGS}\n"
+
+rm -f minimal minimal.c
+       
 cat <<EOF
 
 All required packages were found. You may now run the following
@@ -428,10 +472,10 @@ CXXFLAGS = ${CXXFLAGS}
 LDFLAGS = ${LDFLAGS}
 
 # Flags to enable warnings when using the C++ compiler
-WARN_CXXFLAGS=-Wall -Wextra -Wwrite-strings -Wswitch-enum
+WARN_CXXFLAGS=${WARN_CXXFLAGS}
 
 # Flags to enable warnings when using the C compiler
-WARN_CFLAGS=\$(WARN_CXXFLAGS) -Wmissing-declarations
+WARN_CFLAGS=${WARN_CFLAGS}
 
 # The prefix to which notmuch should be installed
 # Note: If you change this value here, be sure to ensure that the
@@ -498,6 +542,12 @@ GMIME_LDFLAGS = ${gmime_ldflags}
 TALLOC_CFLAGS = ${talloc_cflags}
 TALLOC_LDFLAGS = ${talloc_ldflags}
 
+# Flags needed to have linker set rpath attribute
+RPATH_LDFLAGS = ${rpath_ldflags}
+
+# Flags needed to have linker link only to necessary libraries
+AS_NEEDED_LDFLAGS = ${as_needed_ldflags}
+
 # Whether valgrind header files are available
 HAVE_VALGRIND = ${have_valgrind}
 
index 66589c18f64747798d479994382e5d8c70dfd7a1..7266641afcd2bbf1e7cdb3136a6e3e552b20ecd5 100644 (file)
@@ -1,3 +1,30 @@
+notmuch (0.4) unstable; urgency=low
+
+  * new: notmuch search --output=(summary|threads|messages|tags|files)
+  * new: notmuch show --format=mbox <search-specification>
+  * new: notmuch config [get|set] <section>.<item> [value ...]
+  * lib: Add notmuch_query_get_query_string and notmuch_query_get_sort
+  * emacs: Enable Fcc of all sent messages by default (to "sent" directory)
+  * emacs: Ability to all open messages in a thread to a pipe
+  * emacs: Optional support for detecting inline patches
+  * emacs: Automatically tag messages as "replied" when sending a reply
+  * emacs: Allow search-result color specifications to overlay each other
+  * emacs: Make hidden author names still available for incremental search.
+  * emacs: New binding of Control-TAB (works like TAB in reverse)
+  * test: New modularization of test suite.
+  * test: New testing of emacs interface.
+  * bugfix: Avoid setting Bcc header in "notmuch reply"
+  * bugfix: Avoid corruption of database when "notmuch new " is interrupted.
+  * bugfix: Fix failure with extremely long message ID headers.
+  * bugfix: Fix for messages with "charset=unknown-8bit"
+  * bugfix: Fix notmuch_query_search_threads to return NULL on any exception
+  * bugfix: Fix "notmuch search" to return non-zero on any exception
+  * emacs bugfix: Fix for message with a subject containing, "[1234]"
+  * emacs bugfix: Fix to correctly handle message IDs containing ".."
+  * emacs bugfix: Fix initialization so "M-x notmuch" works by default.
+
+ -- Carl Worth <cworth@debian.org>  Mon, 01 Nov 2010 16:23:47 -0700
+
 notmuch (0.3.1) unstable; urgency=low
 
   * Fix an infinite loop in "notmuch reply"
index e4c61ab377a74d1a305bcb60f5ef4f40f65a0b14..3491e9a48874b1869c52daa8b664ee45395edd7a 100644 (file)
@@ -4,7 +4,7 @@ Priority: extra
 Maintainer: Carl Worth <cworth@debian.org>
 Uploaders: Jameson Graef Rollins <jrollins@finestructure.net>, martin f. krafft <madduck@debian.org>
 Build-Depends: debhelper (>= 7.0.50~), pkg-config, libxapian-dev, libgmime-2.4-dev, libtalloc-dev, libz-dev, emacs (>= 23~)
-Standards-Version: 3.8.4
+Standards-Version: 3.9.1.0
 Homepage: http://notmuchmail.org/
 Vcs-Git: git://notmuchmail.org/git/notmuch
 Vcs-Browser: http://git.notmuchmail.org/git/notmuch
diff --git a/debian/libnotmuch1.symbols b/debian/libnotmuch1.symbols
new file mode 100644 (file)
index 0000000..88ed761
--- /dev/null
@@ -0,0 +1,79 @@
+libnotmuch.so.1 libnotmuch1 #MINVER#
+ _ZTIN6Xapian10LogicErrorE@Base 0.3
+ _ZTIN6Xapian12RuntimeErrorE@Base 0.3
+ _ZTIN6Xapian16DocNotFoundErrorE@Base 0.3
+ _ZTIN6Xapian20InvalidArgumentErrorE@Base 0.3
+ _ZTIN6Xapian5ErrorE@Base 0.3
+ _ZTSN6Xapian10LogicErrorE@Base 0.3
+ _ZTSN6Xapian12RuntimeErrorE@Base 0.3
+ _ZTSN6Xapian16DocNotFoundErrorE@Base 0.3
+ _ZTSN6Xapian20InvalidArgumentErrorE@Base 0.3
+ _ZTSN6Xapian5ErrorE@Base 0.3
+ notmuch_database_add_message@Base 0.3
+ notmuch_database_close@Base 0.3
+ notmuch_database_create@Base 0.3
+ notmuch_database_find_message@Base 0.3
+ notmuch_database_get_all_tags@Base 0.3
+ notmuch_database_get_directory@Base 0.3
+ notmuch_database_get_path@Base 0.3
+ notmuch_database_get_version@Base 0.3
+ notmuch_database_needs_upgrade@Base 0.3
+ notmuch_database_open@Base 0.3
+ notmuch_database_remove_message@Base 0.3
+ notmuch_database_upgrade@Base 0.3
+ notmuch_directory_destroy@Base 0.3
+ notmuch_directory_get_child_directories@Base 0.3
+ notmuch_directory_get_child_files@Base 0.3
+ notmuch_directory_get_mtime@Base 0.3
+ notmuch_directory_set_mtime@Base 0.3
+ notmuch_filenames_destroy@Base 0.3
+ notmuch_filenames_get@Base 0.3
+ notmuch_filenames_move_to_next@Base 0.3
+ notmuch_filenames_valid@Base 0.3
+ notmuch_message_add_tag@Base 0.3
+ notmuch_message_destroy@Base 0.3
+ notmuch_message_freeze@Base 0.3
+ notmuch_message_get_date@Base 0.3
+ notmuch_message_get_filename@Base 0.3
+ notmuch_message_get_flag@Base 0.3
+ notmuch_message_get_header@Base 0.3
+ notmuch_message_get_message_id@Base 0.3
+ notmuch_message_get_replies@Base 0.3
+ notmuch_message_get_tags@Base 0.3
+ notmuch_message_get_thread_id@Base 0.3
+ notmuch_message_remove_all_tags@Base 0.3
+ notmuch_message_remove_tag@Base 0.3
+ notmuch_message_set_flag@Base 0.3
+ notmuch_message_thaw@Base 0.3
+ notmuch_messages_collect_tags@Base 0.3
+ notmuch_messages_destroy@Base 0.3
+ notmuch_messages_get@Base 0.3
+ notmuch_messages_move_to_next@Base 0.3
+ notmuch_messages_valid@Base 0.3
+ notmuch_query_count_messages@Base 0.3
+ notmuch_query_create@Base 0.3
+ notmuch_query_destroy@Base 0.3
+ notmuch_query_get_query_string@Base 0.4
+ notmuch_query_get_sort@Base 0.4
+ notmuch_query_search_messages@Base 0.3
+ notmuch_query_search_threads@Base 0.3
+ notmuch_query_set_sort@Base 0.3
+ notmuch_status_to_string@Base 0.3
+ notmuch_tags_destroy@Base 0.3
+ notmuch_tags_get@Base 0.3
+ notmuch_tags_move_to_next@Base 0.3
+ notmuch_tags_valid@Base 0.3
+ notmuch_thread_destroy@Base 0.3
+ notmuch_thread_get_authors@Base 0.3
+ notmuch_thread_get_matched_messages@Base 0.3
+ notmuch_thread_get_newest_date@Base 0.3
+ notmuch_thread_get_oldest_date@Base 0.3
+ notmuch_thread_get_subject@Base 0.3
+ notmuch_thread_get_tags@Base 0.3
+ notmuch_thread_get_thread_id@Base 0.3
+ notmuch_thread_get_toplevel_messages@Base 0.3
+ notmuch_thread_get_total_messages@Base 0.3
+ notmuch_threads_destroy@Base 0.3
+ notmuch_threads_get@Base 0.3
+ notmuch_threads_move_to_next@Base 0.3
+ notmuch_threads_valid@Base 0.3
index 25a527f5fc8d99244329a1530feafee881861569..60bf996820126f3377d68fbeeaf88cfbef32b79e 100644 (file)
@@ -20,4 +20,9 @@
    (concat "/usr/share/"
           (symbol-name debian-emacs-flavor)
           "/site-lisp/notmuch"))
+  (autoload 'notmuch "notmuch" "Run notmuch and display saved searches, known tags, etc." t)
+  (autoload 'notmuch-hello "notmuch" "Run notmuch and display saved searches, known tags, etc." t)
+  (autoload 'notmuch-search "notmuch" "Run \"notmuch search\" with the given query string and display results." t)
+  (autoload 'notmuch-show "notmuch" "Run \"notmuch show\" with the given thread ID and display results." t)
+
   ))
index f8ae332c440ed3b4d9fed18ceb05cd41db6a6e3f..e58dd24e3460ae445d9e218ca97d201e10f7ebcd 100644 (file)
@@ -19,9 +19,9 @@
 ;;
 ;; Authors: David Edmondson <dme@dme.org>
 
+(eval-when-compile (require 'cl))
 (require 'widget)
 (require 'wid-edit) ; For `widget-forward'.
-(require 'cl)
 
 (require 'notmuch-lib)
 (require 'notmuch-mua)
@@ -115,6 +115,7 @@ Typically \",\" in the US and UK and \".\" in Europe."
     (while (> n 0)
       (push (% n 1000) result)
       (setq n (/ n 1000)))
+    (setq result (or result '(0)))
     (apply #'concat
      (number-to-string (car result))
      (mapcar (lambda (elem)
@@ -209,11 +210,12 @@ should be. Returns a cons cell `(tags-per-line width)'."
                   ;; after the name.
                   (+ 9 1 widest)))))))
 
-    (cons tags-per-line (/ (- (window-width) notmuch-hello-indent
-                             ;; Count is 9 wide (8 digits plus
-                             ;; space), 1 for the space after the
-                             ;; name.
-                             (* tags-per-line (+ 9 1)))
+    (cons tags-per-line (/ (max 1
+                               (- (window-width) notmuch-hello-indent
+                                  ;; Count is 9 wide (8 digits plus
+                                  ;; space), 1 for the space after the
+                                  ;; name.
+                                  (* tags-per-line (+ 9 1))))
                           tags-per-line))))
 
 (defun notmuch-hello-insert-tags (tag-alist widest target)
@@ -249,7 +251,9 @@ should be. Returns a cons cell `(tags-per-line width)'."
                ;; can just insert `(- widest (length name))' spaces -
                ;; the column separator is included in the button if
                ;; `(equal widest (length name)'.
-               (widget-insert (make-string (- widest (length name)) ? ))))
+               (widget-insert (make-string (max 1
+                                                (- widest (length name)))
+                                           ? ))))
            (setq count (1+ count))
            (if (eq (% count tags-per-line) 0)
                (widget-insert "\n")))
@@ -290,7 +294,7 @@ should be. Returns a cons cell `(tags-per-line width)'."
     (define-key map "v" '(lambda () "Display the notmuch version" (interactive)
                            (message "notmuch version %s" (notmuch-version))))
     (define-key map "?" 'notmuch-help)
-    (define-key map "q" 'kill-this-buffer)
+    (define-key map "q" 'notmuch-kill-this-buffer)
     (define-key map "=" 'notmuch-hello-update)
     (define-key map "G" 'notmuch-hello-poll-and-update)
     (define-key map (kbd "<C-tab>") 'widget-backward)
@@ -314,7 +318,9 @@ Complete list of currently available key bindings:
  ;;(setq buffer-read-only t)
 )
 
+;;;###autoload
 (defun notmuch-hello (&optional no-display)
+  "Run notmuch and display saved searches, known tags, etc."
   (interactive)
 
   ; Jump through a hoop to get this value from the deprecated variable
@@ -378,7 +384,7 @@ Complete list of currently available key bindings:
                     :help-echo "Refresh"
                     (notmuch-hello-nice-number
                      (string-to-number (car (process-lines notmuch-command "count")))))
-      (widget-insert " messages (that's not much mail).\n"))
+      (widget-insert " messages.\n"))
 
     (let ((found-target-pos nil)
          (final-target-pos nil))
@@ -510,7 +516,6 @@ Complete list of currently available key bindings:
       (unless (widget-at)
        (notmuch-hello-goto-search)))))
 
-;;;###autoload
 (defun notmuch-folder ()
   "Deprecated function for invoking notmuch---calling `notmuch' is preferred now."
   (interactive)
index f30bcb429cc2a775a98b0242b3f3f63b5a0c8ecf..dfdcd0520b021e101acc2c105616c720b46c2f1a 100644 (file)
@@ -68,6 +68,30 @@ the user hasn't set this variable with the old or new value."
        (match-string 2 long-string)
       "unknown")))
 
+(defun notmuch-config-get (item)
+  "Return a value from the notmuch configuration."
+  ;; Trim off the trailing newline
+  (substring (shell-command-to-string
+             (concat notmuch-command " config get " item))
+             0 -1))
+
+(defun notmuch-database-path ()
+  "Return the database.path value from the notmuch configuration."
+  (notmuch-config-get "database.path"))
+
+(defun notmuch-user-name ()
+  "Return the user.name value from the notmuch configuration."
+  (notmuch-config-get "user.name"))
+
+(defun notmuch-user-primary-email ()
+  "Return the user.primary_email value from the notmuch configuration."
+  (notmuch-config-get "user.primary_email"))
+
+(defun notmuch-kill-this-buffer ()
+  "Kill the current buffer."
+  (interactive)
+  (kill-buffer (current-buffer)))
+
 ;;
 
 ;; XXX: This should be a generic function in emacs somewhere, not
index 8bb41a89a848a598d3adee37e059dd55e49cdd5d..693d8d42bc19bfcd49f2212c646723fd396d96f4 100644 (file)
 
 (require 'message)
 
+(require 'notmuch-lib)
+
 (defvar notmuch-maildir-fcc-count 0)
 
-(defcustom notmuch-fcc-dirs nil
+(defcustom notmuch-fcc-dirs "sent"
  "Determines the maildir directory to save outgoing mails in.
 
  If set to non-nil, this will cause message mode to file your
  used. The first entry is used as a default fallback when nothing
  else matches.
 
- In all cases, the complete FCC directory will be constructed by 
- concatenating the content  of the variable 'message-directory' 
- ('~/Mail/' by default and customizable via M-x
- customize-variable<RET>message-directory<RET>) and this value.
+ In all cases, a relative FCC directory will be understood to
+ specify a directory within the notmuch mail store, (as set by
+ the database.path option in the notmuch configuration file).
 
  You will be prompted to create the directory if it does not exist yet when 
  sending a mail.
@@ -67,7 +68,7 @@
           '(lambda (destdir)
              (notmuch-maildir-fcc-write-buffer-to-maildir destdir t)))
     ;;add a hook to actually insert the Fcc header when sending
-    (add-hook 'message-send-hook 'notmuch-fcc-header-setup))
+    (add-hook 'message-header-setup-hook 'notmuch-fcc-header-setup))
 
 (defun notmuch-fcc-header-setup ()
   "Adds an appropriate fcc header to the current mail buffer
@@ -90,8 +91,9 @@
   ;; if there is no fcc header yet, add ours
   (unless (message-fetch-field "fcc")
     (message-add-header (concat "Fcc: "
-                                (file-name-as-directory message-directory)
-                                subdir)))
+                               (if (= (elt subdir 0) ?/)
+                                   subdir
+                                 (concat (notmuch-database-path) "/" subdir)))))
 
   ;; finally test if fcc points to a valid maildir
   (let ((fcc-header (message-fetch-field "fcc")))
index 0ad079ffaea88a38a2a1cc9ed594341ec3bc0265..dc7b386f23c32abbba3a8e8ad8966764ce9d0c8c 100644 (file)
@@ -126,6 +126,10 @@ list."
       (when (not (string= "" user-agent))
        (push (cons "User-Agent" user-agent) other-headers))))
 
+  (unless (mail-header 'from other-headers)
+    (push (cons "From" (concat
+                       (notmuch-user-name) " <" (notmuch-user-primary-email) ">")) other-headers))
+
   (message-mail to subject other-headers continue
                switch-function yank-action send-actions)
   (message-sort-headers)
index 0d6e7759a6b57593597dbcdb70afe5128ee1b3bf..26f954474227f15052692ff6ba46479baf4350b7 100644 (file)
@@ -47,7 +47,7 @@ is a possibly empty forest of replies.
   (apply 'append
         (mapcar
           (lambda (tree)
-            (funcall mapper fn tree))
+            (funcall mapper function tree))
           seq)))
 
 (defun notmuch-query-map-threads (fn threads)
index f872cdfe00d44c1a498ad1ad46abe6b4ee84d1c4..701212544b51d354e3cb639b8c4e625358714aa2 100644 (file)
@@ -21,7 +21,7 @@
 ;; Authors: Carl Worth <cworth@cworth.org>
 ;;          David Edmondson <dme@dme.org>
 
-(require 'cl)
+(eval-when-compile (require 'cl))
 (require 'mm-view)
 (require 'message)
 (require 'mm-decode)
@@ -85,10 +85,10 @@ any given message."
 (defmacro with-current-notmuch-show-message (&rest body)
   "Evaluate body with current buffer set to the text of current message"
   `(save-excursion
-     (let ((filename (notmuch-show-get-filename)))
-       (let ((buf (generate-new-buffer (concat "*notmuch-msg-" filename "*"))))
+     (let ((id (notmuch-show-get-message-id)))
+       (let ((buf (generate-new-buffer (concat "*notmuch-msg-" id "*"))))
          (with-current-buffer buf
-           (insert-file-contents filename nil nil nil t)
+           (call-process notmuch-command nil t nil "show" "--format=raw" id)
            ,@body)
         (kill-buffer buf)))))
 
@@ -555,7 +555,7 @@ function is used. "
 (defvar notmuch-show-mode-map
       (let ((map (make-sparse-keymap)))
        (define-key map "?" 'notmuch-help)
-       (define-key map "q" 'kill-this-buffer)
+       (define-key map "q" 'notmuch-kill-this-buffer)
        (define-key map (kbd "<C-tab>") 'widget-backward)
        (define-key map (kbd "M-TAB") 'notmuch-show-previous-button)
        (define-key map (kbd "<backtab>") 'notmuch-show-previous-button)
@@ -586,7 +586,6 @@ function is used. "
       "Keymap for \"notmuch show\" buffers.")
 (fset 'notmuch-show-mode-map notmuch-show-mode-map)
 
-;;;###autoload
 (defun notmuch-show-mode ()
   "Major mode for viewing a thread with notmuch.
 
@@ -597,21 +596,17 @@ By default, various components of email messages, (citations,
 signatures, already-read messages), are hidden. You can make
 these parts visible by clicking with the mouse button or by
 pressing RET after positioning the cursor on a hidden part, (for
-which \\[notmuch-show-next-button] and
-\\[notmuch-show-previous-button] are helpful).
+which \\[notmuch-show-next-button] and \\[notmuch-show-previous-button] are helpful).
 
 Reading the thread sequentially is well-supported by pressing
-\\[notmuch-show-advance-and-archive]. This will scroll the
-current message (if necessary), advance to the next message, or
-advance to the next thread (if already on the last message of a
-thread).
+\\[notmuch-show-advance-and-archive]. This will scroll the current message (if necessary), advance
+to the next message, or advance to the next thread (if already on
+the last message of a thread).
 
 Other commands are available to read or manipulate the thread
-more selectively, (such as '\\[notmuch-show-next-message]' and
-'\\[notmuch-show-previous-message]' to advance to messages
-without removing any tags, and '\\[notmuch-show-archive-thread]'
-to archive an entire thread without scrolling through with
-\\[notmuch-show-advance-and-archive]).
+more selectively, (such as '\\[notmuch-show-next-message]' and '\\[notmuch-show-previous-message]' to advance to messages
+without removing any tags, and '\\[notmuch-show-archive-thread]' to archive an entire thread
+without scrolling through with \\[notmuch-show-advance-and-archive]).
 
 You can add or remove arbitary tags from the current message with
 '\\[notmuch-show-add-tag]' or '\\[notmuch-show-remove-tag]'.
@@ -730,7 +725,7 @@ All currently available key bindings:
 
 (defun notmuch-show-get-message-id ()
   "Return the message id of the current message."
-  (concat "id:" (notmuch-show-get-prop :id)))
+  (concat "id:\"" (notmuch-show-get-prop :id) "\""))
 
 ;; dme: Would it make sense to use a macro for many of these?
 
@@ -778,6 +773,22 @@ All currently available key bindings:
   "Mark the current message as read."
   (notmuch-show-remove-tag "unread"))
 
+;; Functions for getting attributes of several messages in the current
+;; thread.
+
+(defun notmuch-show-get-message-ids-for-open-messages ()
+  "Return a list of all message IDs for open messages in the current thread."
+  (save-excursion
+    (let (message-ids done)
+      (goto-char (point-min))
+      (while (not done)
+       (if (notmuch-show-message-visible-p)
+           (setq message-ids (append message-ids (list (notmuch-show-get-message-id)))))
+       (setq done (not (notmuch-show-goto-message-next)))
+       )
+      message-ids
+      )))
+
 ;; Commands typically bound to keys.
 
 (defun notmuch-show-advance-and-archive ()
@@ -906,42 +917,84 @@ any effects from previous calls to
 (defun notmuch-show-view-raw-message ()
   "View the file holding the current message."
   (interactive)
-  (view-file (notmuch-show-get-filename)))
+  (let ((id (notmuch-show-get-message-id)))
+    (let ((buf (get-buffer-create (concat "*notmuch-raw-" id "*"))))
+      (switch-to-buffer buf)
+      (save-excursion
+       (call-process notmuch-command nil t nil "show" "--format=raw" id)))))
 
-(defun notmuch-show-pipe-message (command)
-  "Pipe the contents of the current message to the given command.
+(defun notmuch-show-pipe-message (entire-thread command)
+  "Pipe the contents of the current message (or thread) to the given command.
 
 The given command will be executed with the raw contents of the
 current email message as stdin. Anything printed by the command
-to stdout or stderr will appear in the *Messages* buffer."
-  (interactive "sPipe message to command: ")
-  (apply 'start-process-shell-command "notmuch-pipe-command" "*notmuch-pipe*"
-        (list command " < "
-              (shell-quote-argument (notmuch-show-get-filename)))))
+to stdout or stderr will appear in the *Messages* buffer.
+
+When invoked with a prefix argument, the command will receive all
+open messages in the current thread (formatted as an mbox) rather
+than only the current message."
+  (interactive "P\nsPipe message to command: ")
+  (let (shell-command)
+    (if entire-thread
+       (setq shell-command 
+             (concat notmuch-command " show --format=mbox "
+                     (shell-quote-argument
+                      (mapconcat 'identity (notmuch-show-get-message-ids-for-open-messages) " OR "))
+                     " | " command))
+      (setq shell-command
+           (concat notmuch-command " show --format=raw "
+                   (shell-quote-argument (notmuch-show-get-message-id)) " | " command)))
+    (start-process-shell-command "notmuch-pipe-command" "*notmuch-pipe*" shell-command)))
+
+(defun notmuch-show-add-tags-worker (current-tags add-tags)
+  "Add to `current-tags' with any tags from `add-tags' not
+currently present and return the result."
+  (let ((result-tags (copy-sequence current-tags)))
+    (mapc (lambda (add-tag)
+           (unless (member add-tag current-tags)
+             (setq result-tags (push add-tag result-tags))))
+           add-tags)
+    (sort result-tags 'string<)))
+
+(defun notmuch-show-del-tags-worker (current-tags del-tags)
+  "Remove any tags in `del-tags' from `current-tags' and return
+the result."
+  (let ((result-tags (copy-sequence current-tags)))
+    (mapc (lambda (del-tag)
+           (setq result-tags (delete del-tag result-tags)))
+         del-tags)
+    result-tags))
 
 (defun notmuch-show-add-tag (&rest toadd)
   "Add a tag to the current message."
   (interactive
    (list (notmuch-select-tag-with-completion "Tag to add: ")))
-  (apply 'notmuch-call-notmuch-process
-        (append (cons "tag"
-                      (mapcar (lambda (s) (concat "+" s)) toadd))
-                (cons (notmuch-show-get-message-id) nil)))
-  (notmuch-show-set-tags (sort (union toadd (notmuch-show-get-tags) :test 'string=) 'string<)))
+
+  (let* ((current-tags (notmuch-show-get-tags))
+        (new-tags (notmuch-show-add-tags-worker current-tags toadd)))
+
+    (unless (equal current-tags new-tags)
+      (apply 'notmuch-call-notmuch-process
+            (append (cons "tag"
+                          (mapcar (lambda (s) (concat "+" s)) toadd))
+                    (cons (notmuch-show-get-message-id) nil)))
+      (notmuch-show-set-tags new-tags))))
 
 (defun notmuch-show-remove-tag (&rest toremove)
   "Remove a tag from the current message."
   (interactive
    (list (notmuch-select-tag-with-completion
          "Tag to remove: " (notmuch-show-get-message-id))))
-  (let ((tags (notmuch-show-get-tags)))
-    (if (intersection tags toremove :test 'string=)
-       (progn
-         (apply 'notmuch-call-notmuch-process
-                (append (cons "tag"
-                              (mapcar (lambda (s) (concat "-" s)) toremove))
-                        (cons (notmuch-show-get-message-id) nil)))
-         (notmuch-show-set-tags (sort (set-difference tags toremove :test 'string=) 'string<))))))
+
+  (let* ((current-tags (notmuch-show-get-tags))
+        (new-tags (notmuch-show-del-tags-worker current-tags toremove)))
+
+    (unless (equal current-tags new-tags)
+      (apply 'notmuch-call-notmuch-process
+            (append (cons "tag"
+                          (mapcar (lambda (s) (concat "-" s)) toremove))
+                    (cons (notmuch-show-get-message-id) nil)))
+      (notmuch-show-set-tags new-tags))))
 
 (defun notmuch-show-toggle-headers ()
   "Toggle the visibility of the current message headers."
@@ -990,7 +1043,7 @@ argument, hide all of the messages."
        until (not (notmuch-show-goto-message-next)))
   ;; Move to the next item in the search results, if any.
   (let ((parent-buffer notmuch-show-parent-buffer))
-    (kill-this-buffer)
+    (notmuch-kill-this-buffer)
     (if parent-buffer
        (progn
          (switch-to-buffer parent-buffer)
index fe1041f013f864f56d0b49276e5b64eb079dddd6..4a9223e4dfa8f1f0e95c76e10219a8f1a1863581 100644 (file)
@@ -47,7 +47,7 @@
 ; kudos: Notmuch list <notmuch@notmuchmail.org> (subscription is not
 ; required, but is available from http://notmuchmail.org).
 
-(require 'cl)
+(eval-when-compile (require 'cl))
 (require 'mm-view)
 (require 'message)
 
@@ -232,7 +232,7 @@ For a mouse binding, return nil."
   "Exit the search buffer, calling any defined continuation function."
   (interactive)
   (let ((continuation notmuch-search-continuation))
-    (kill-this-buffer)
+    (notmuch-kill-this-buffer)
     (when continuation
       (funcall continuation))))
 
@@ -329,7 +329,6 @@ For a mouse binding, return nil."
   "Face used in search mode face for tags."
   :group 'notmuch)
 
-;;;###autoload
 (defun notmuch-search-mode ()
   "Major mode displaying results of a notmuch search.
 
@@ -575,9 +574,10 @@ This function advances the next thread when finished."
                        (if (and atbob
                                 (not (string= notmuch-search-target-thread "found")))
                            (set 'never-found-target-thread t))))))
-             (if (and never-found-target-thread
+             (when (and never-found-target-thread
                       notmuch-search-target-line)
-                 (goto-line notmuch-search-target-line)))))))
+                 (goto-char (point-min))
+                 (forward-line (1- notmuch-search-target-line))))))))
 
 (defcustom notmuch-search-line-faces nil
   "Tag/face mapping for line highlighting in notmuch-search.
@@ -585,28 +585,33 @@ This function advances the next thread when finished."
 Here is an example of how to color search results based on tags.
  (the following text would be placed in your ~/.emacs file):
 
- (setq notmuch-search-line-faces '((\"delete\" . '(:foreground \"red\"))
+ (setq notmuch-search-line-faces '((\"delete\" . '(:foreground \"red\"
+                                                  :background \"blue\"))
                                    (\"unread\" . '(:foreground \"green\"))))
 
-Order matters: for lines with multiple tags, the the first
-matching will be applied."
+The attributes defined for matching tags are merged, with later
+attributes overriding earlier. A message having both \"delete\"
+and \"unread\" tags with the above settings would have a green
+foreground and blue background."
   :type '(alist :key-type (string) :value-type (list))
   :group 'notmuch)
 
 (defun notmuch-search-color-line (start end line-tag-list)
-  "Colorize lines in notmuch-show based on tags"
-  (if notmuch-search-line-faces
-      (let ((overlay (make-overlay start end))
-           (tags-faces (copy-alist notmuch-search-line-faces)))
-       (while tags-faces
-         (let* ((tag-face (car tags-faces))
-                (tag (car tag-face))
-                (face (cdr tag-face)))
-           (cond ((member tag line-tag-list)
-                  (overlay-put overlay 'face face)
-                  (setq tags-faces nil))
-                 (t
-                  (setq tags-faces (cdr tags-faces)))))))))
+  "Colorize lines in `notmuch-show' based on tags."
+  ;; Create the overlay only if the message has tags which match one
+  ;; of those specified in `notmuch-search-line-faces'.
+  (let (overlay)
+    (mapc '(lambda (elem)
+            (let ((tag (car elem))
+                  (attributes (cdr elem)))
+              (when (member tag line-tag-list)
+                (when (not overlay)
+                  (setq overlay (make-overlay start end)))
+                ;; Merge the specified properties with any already
+                ;; applied from an earlier match.
+                (overlay-put overlay 'face
+                             (append (overlay-get overlay 'face) attributes)))))
+         notmuch-search-line-faces)))
 
 (defun notmuch-search-isearch-authors-show (overlay)
   (remove-from-invisibility-spec (cons (overlay-get overlay 'invisible) t)))
@@ -691,7 +696,7 @@ matching will be applied."
                  (more t)
                  (inhibit-read-only t))
              (while more
-               (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\(.*\\) \\(\\[[0-9/]*\\]\\) \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)
+               (if (string-match "^\\(thread:[0-9A-Fa-f]*\\) \\([^][]*\\) \\(\\[[0-9/]*\\]\\) \\([^;]*\\); \\(.*\\) (\\([^()]*\\))$" string line)
                    (let* ((thread-id (match-string 1 string))
                           (date (match-string 2 string))
                           (count (match-string 3 string))
@@ -741,10 +746,16 @@ characters as well as `_.+-'.
 
 (defun notmuch-search-buffer-title (query)
   "Returns the title for a buffer with notmuch search results."
-  (let* ((saved-search (rassoc-if (lambda (key)
-                                   (string-match (concat "^" (regexp-quote key))
-                                                 query))
-                                 (reverse (notmuch-saved-searches))))
+  (let* ((saved-search
+         (let (longest
+               (longest-length 0))
+           (loop for tuple in notmuch-saved-searches
+                 if (let ((quoted-query (regexp-quote (cdr tuple))))
+                      (and (string-match (concat "^" quoted-query) query)
+                           (> (length (match-string 0 query))
+                              longest-length)))
+                 do (setq longest tuple))
+           longest))
         (saved-search-name (car saved-search))
         (saved-search-query (cdr saved-search)))
     (cond ((and saved-search (equal saved-search-query query))
@@ -788,10 +799,13 @@ The optional parameters are used as follows:
       (erase-buffer)
       (goto-char (point-min))
       (save-excursion
-       (let ((proc (start-process-shell-command
-                    "notmuch-search" buffer notmuch-command "search"
-                    (if oldest-first "--sort=oldest-first" "--sort=newest-first")
-                    (shell-quote-argument query))))
+       (let ((proc (start-process
+                    "notmuch-search" buffer
+                    notmuch-command "search"
+                    (if oldest-first
+                        "--sort=oldest-first"
+                      "--sort=newest-first")
+                    query)))
          (set-process-sentinel proc 'notmuch-search-process-sentinel)
          (set-process-filter proc 'notmuch-search-process-filter))))
     (run-hooks 'notmuch-search-hook)))
@@ -810,7 +824,7 @@ same relative position within the new buffer."
        (target-thread (notmuch-search-find-thread-id))
        (query notmuch-search-query-string)
        (continuation notmuch-search-continuation))
-    (kill-this-buffer)
+    (notmuch-kill-this-buffer)
     (notmuch-search query oldest-first target-thread target-line continuation)
     (goto-char (point-min))))
 
index 9c0facce2b54ddce48fa619f8e983f2e27124da6..3d7de5caffa6516332ded9f46203e354c67371cf 100644 (file)
@@ -15,7 +15,7 @@ LIBNOTMUCH_VERSION_MAJOR = 1
 
 # The minor version of the library interface. This should be incremented at
 # the time of release for any additions to the library interface.
-LIBNOTMUCH_VERSION_MINOR = 1
+LIBNOTMUCH_VERSION_MINOR = 2
 
 # The release version the library interface. This should be incremented at
 # the time of release if there have been no changes to the interface, (but
@@ -36,9 +36,11 @@ SONAME = $(LINKER_NAME).$(LIBNOTMUCH_VERSION_MAJOR)
 LIBNAME = $(SONAME).$(LIBNOTMUCH_VERSION_MINOR).$(LIBNOTMUCH_VERSION_RELEASE)
 LIBRARY_LINK_FLAG = -shared -Wl,-soname=$(SONAME)
 ifeq ($(LIBDIR_IN_LDCONFIG),1)
+ifeq ($(DESTDIR),)
 LIBRARY_INSTALL_POST_COMMAND=ldconfig
 endif
 endif
+endif
 
 dir := lib
 extra_cflags += -I$(dir) -fPIC
@@ -76,13 +78,17 @@ $(dir)/$(LINKER_NAME): $(dir)/$(SONAME)
 
 install: install-$(dir)
 
+# The (often-reused) $dir works fine within targets/pre-requisites,
+# but cannot be used reliably within commands, so copy its value to a
+# variable that is not reused.
+lib := $(dir)
 install-$(dir):
        mkdir -p $(DESTDIR)$(libdir)/
-       install -m0644 $(dir)/$(LIBNAME) $(DESTDIR)$(libdir)/
+       install -m0644 $(lib)/$(LIBNAME) $(DESTDIR)$(libdir)/
        ln -sf $(LIBNAME) $(DESTDIR)$(libdir)/$(SONAME)
        ln -sf $(LIBNAME) $(DESTDIR)$(libdir)/$(LINKER_NAME)
        mkdir -p $(DESTDIR)$(includedir)
-       install -m0644 $(dir)/notmuch.h $(DESTDIR)$(includedir)/
+       install -m0644 $(lib)/notmuch.h $(DESTDIR)$(includedir)/
        $(LIBRARY_INSTALL_POST_COMMAND)
 
 SRCS  := $(SRCS) $(libnotmuch_c_srcs) $(libnotmuch_cxx_srcs)
index bd72f670092b0846501e425de44eb69f91a13869..e42b8bb8d3283d52fba367643142c734b89ca027 100644 (file)
@@ -34,6 +34,8 @@
 
 #include <xapian.h>
 
+#pragma GCC visibility push(hidden)
+
 struct _notmuch_database {
     notmuch_bool_t exception_reported;
 
@@ -65,4 +67,6 @@ notmuch_tags_t *
 _notmuch_convert_tags (void *ctx, Xapian::TermIterator &i,
                       Xapian::TermIterator &end);
 
+#pragma GCC visibility pop
+
 #endif
index 6affc205e4e81b894443722b30f54457ff7669e3..82c078867a52dee07a61dbaf6bea77044d578398 100644 (file)
@@ -24,7 +24,6 @@
 
 #include <sys/time.h>
 #include <signal.h>
-#include <xapian.h>
 
 #include <glib.h> /* g_free, GPtrArray, GHashTable */
 
@@ -185,7 +184,7 @@ typedef struct {
  * nearly universal to all mail messages).
  */
 
-prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
+static prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
     { "type",                  "T" },
     { "reference",             "XREFERENCE" },
     { "replyto",               "XREPLYTO" },
@@ -194,14 +193,14 @@ prefix_t BOOLEAN_PREFIX_INTERNAL[] = {
     { "directory-direntry",    "XDDIRENTRY" },
 };
 
-prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
+static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
     { "thread",                        "G" },
     { "tag",                   "K" },
     { "is",                    "K" },
     { "id",                    "Q" }
 };
 
-prefix_t PROBABILISTIC_PREFIX[]= {
+static prefix_t PROBABILISTIC_PREFIX[]= {
     { "from",                  "XFROM" },
     { "to",                    "XTO" },
     { "attachment",            "XATTACHMENT" },
@@ -1300,7 +1299,7 @@ _resolve_message_id_to_thread_id (notmuch_database_t *notmuch,
 
     talloc_free (metadata_key);
 
-    return thread_id;
+    return talloc_strdup (ctx, thread_id);
 }
 
 static notmuch_status_t
index 5d673e2134d058b8010d96d9783d187e765b96a0..2540ca7647fa99e976593d052fed6b55437c9380 100644 (file)
@@ -21,8 +21,6 @@
 #include "notmuch-private.h"
 #include "database-private.h"
 
-#include <xapian.h>
-
 struct _notmuch_filenames {
     Xapian::TermIterator iterator;
     Xapian::TermIterator end;
@@ -52,7 +50,7 @@ _notmuch_filenames_destructor (notmuch_filenames_t *filenames)
  * iterating over the non-prefixed portion of terms sharing a common
  * prefix.
  */
-notmuch_filenames_t *
+static notmuch_filenames_t *
 _notmuch_filenames_create (void *ctx,
                           notmuch_database_t *notmuch,
                           const char *prefix)
index 0d6640bce57e59a657f6d7287d68aac535bd5534..00478f8db53f054d7917705f9b18c073c8bfa098 100644 (file)
@@ -63,7 +63,7 @@ struct _NotmuchFilterDiscardUuencodeClass {
     GMimeFilterClass parent_class;
 };
 
-GMimeFilter *notmuch_filter_discard_uuencode_new (void);
+static GMimeFilter *notmuch_filter_discard_uuencode_new (void);
 
 static void notmuch_filter_discard_uuencode_finalize (GObject *object);
 
@@ -195,7 +195,7 @@ filter_reset (GMimeFilter *gmime_filter)
  *
  * Returns: a new #NotmuchFilterDiscardUuencode filter.
  **/
-GMimeFilter *
+static GMimeFilter *
 notmuch_filter_discard_uuencode_new (void)
 {
     static GType type = 0;
index b4dca93bab9026af9f75e69fbcc11838b1c12a49..c1c848fc4431d4e4161f81c1ed17590a91912313 100644 (file)
@@ -43,6 +43,8 @@ extern "C"
 
 #include <stdint.h>
 
+#pragma GCC visibility push(hidden)
+
 /* Size of SHA1 digest */
 
 #define SHA1_DIGEST_SIZE 20
@@ -60,6 +62,8 @@ void sha1_hash(const unsigned char data[], unsigned long len, sha1_ctx ctx[1]);
 void sha1_end(unsigned char hval[], sha1_ctx ctx[1]);
 void sha1(unsigned char hval[], const unsigned char data[], unsigned long len);
 
+#pragma GCC visibility pop
+
 #if defined(__cplusplus)
 }
 #endif
index 71f5619fe0c04435cc5a5996ff48d995288fe5a0..bf9f1edb84b6404f1c06b35d8c76607eca4d2b73 100644 (file)
@@ -25,8 +25,6 @@
 
 #include <gmime/gmime.h>
 
-#include <xapian.h>
-
 struct _notmuch_message {
     notmuch_database_t *notmuch;
     Xapian::docid doc_id;
index 5a0cf92540619ff5fa50951c5326c84e191051b8..5b32f84a75ff4c316fa64e7b278dc973a4d4c851 100644 (file)
@@ -48,6 +48,8 @@ NOTMUCH_BEGIN_DECLS
 
 #include "xutil.h"
 
+#pragma GCC visibility push(hidden)
+
 #ifdef DEBUG
 # define DEBUG_DATABASE_SANITY 1
 # define DEBUG_QUERY 1
@@ -442,6 +444,8 @@ _notmuch_tags_add_tag (notmuch_tags_t *tags, const char *tag);
 void
 _notmuch_tags_prepare_iterator (notmuch_tags_t *tags);
 
+#pragma GCC visibility pop
+
 NOTMUCH_END_DECLS
 
 #endif
index 505ad19fe90470c958fa9d59805af929b588af8e..bd0880f3a232df162e90db66daac6b70e0f47394 100644 (file)
@@ -361,10 +361,18 @@ typedef enum {
     NOTMUCH_SORT_UNSORTED
 } notmuch_sort_t;
 
+/* Return the query_string of this query. See notmuch_query_create. */
+const char *
+notmuch_query_get_query_string (notmuch_query_t *query);
+
 /* Specify the sorting desired for this query. */
 void
 notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort);
 
+/* Return the sort specified for this query. See notmuch_query_set_sort. */
+notmuch_sort_t
+notmuch_query_get_sort (notmuch_query_t *query);
+
 /* Execute a query for threads, returning a notmuch_threads_t object
  * which can be used to iterate over the results. The returned threads
  * object is owned by the query and as such, will only be valid until
@@ -400,6 +408,8 @@ notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort);
  * notmuch_threads_t object. (For consistency, we do provide a
  * notmuch_threads_destroy function, but there's no good reason
  * to call it if the query is about to be destroyed).
+ *
+ * If a Xapian exception occurs this function will return NULL.
  */
 notmuch_threads_t *
 notmuch_query_search_threads (notmuch_query_t *query);
index d241dc1dc819a6246e2bfc791330c5448dc9548e..7916421e4a0068847a0fc13b71514c1258893a42 100644 (file)
@@ -23,8 +23,6 @@
 
 #include <glib.h> /* GHashTable, GPtrArray */
 
-#include <xapian.h>
-
 struct _notmuch_query {
     notmuch_database_t *notmuch;
     const char *query_string;
@@ -70,12 +68,24 @@ notmuch_query_create (notmuch_database_t *notmuch,
     return query;
 }
 
+const char *
+notmuch_query_get_query_string (notmuch_query_t *query)
+{
+    return query->query_string;
+}
+
 void
 notmuch_query_set_sort (notmuch_query_t *query, notmuch_sort_t sort)
 {
     query->sort = sort;
 }
 
+notmuch_sort_t
+notmuch_query_get_sort (notmuch_query_t *query)
+{
+    return query->sort;
+}
+
 /* We end up having to call the destructors explicitly because we had
  * to use "placement new" in order to initialize C++ objects within a
  * block that we allocated with talloc. So C++ is making talloc
@@ -249,6 +259,10 @@ notmuch_query_search_threads (notmuch_query_t *query)
                                              free, NULL);
 
     threads->messages = notmuch_query_search_messages (query);
+    if (threads->messages == NULL) {
+           talloc_free (threads);
+           return NULL;
+    }
 
     threads->thread_id = NULL;
 
index 13872d46c781900865e8ca118a7027ff0665643e..7f15586286d9441e1199c6f63c1867944b4b645b 100644 (file)
@@ -21,8 +21,6 @@
 #include "notmuch-private.h"
 #include "database-private.h"
 
-#include <xapian.h>
-
 #include <gmime/gmime.h>
 #include <glib.h> /* GHashTable */
 
@@ -140,14 +138,14 @@ _complete_thread_authors (notmuch_thread_t *thread)
     thread->authors_array = NULL;
 }
 
-/* clean up the uggly "Lastname, Firstname" format that some mail systems
+/* clean up the ugly "Lastname, Firstname" format that some mail systems
  * (most notably, Exchange) are creating to be "Firstname Lastname"
  * To make sure that we don't change other potential situations where a
  * comma is in the name, we check that we match one of these patterns
  * "Last, First" <first.last@company.com>
  * "Last, First MI" <first.mi.last@company.com>
  */
-char *
+static char *
 _thread_cleanup_author (notmuch_thread_t *thread,
                        const char *author, const char *from)
 {
index b973f7dcbf752f4cca74d69ef86bcf027cde3aaa..fd77f733a5b33a35dfb32bcb3055c0c1147a5303 100644 (file)
@@ -25,6 +25,8 @@
 #include <sys/types.h>
 #include <regex.h>
 
+#pragma GCC visibility push(hidden)
+
 /* xutil.c */
 void *
 xcalloc (size_t nmemb, size_t size);
@@ -48,4 +50,6 @@ int
 xregexec (const regex_t *preg, const char *string,
          size_t nmatch, regmatch_t pmatch[], int eflags);
 
+#pragma GCC visibility pop
+
 #endif
index 20be43b9660ccb60779167e32d87325dac255f40..fdfb94ad59879a4269c0cb3b7e4c2cd382e0cdf6 100644 (file)
@@ -110,9 +110,15 @@ notmuch_tag_command (void *ctx, int argc, char *argv[]);
 int
 notmuch_search_tags_command (void *ctx, int argc, char *argv[]);
 
+int
+notmuch_cat_command (void *ctx, int argc, char *argv[]);
+
 int
 notmuch_part_command (void *ctx, int argc, char *argv[]);
 
+int
+notmuch_config_command (void *ctx, int argc, char *argv[]);
+
 const char *
 notmuch_time_relative_date (const void *ctx, time_t then);
 
@@ -174,7 +180,7 @@ void
 notmuch_config_set_user_primary_email (notmuch_config_t *config,
                                       const char *primary_email);
 
-char **
+const char **
 notmuch_config_get_user_other_email (notmuch_config_t *config,
                                     size_t *length);
 
index 58f83b0d299c8d4b6213758448bf1db69302b6b3..dcdb0369c917cc562c28c5088c3443df0a616f6a 100644 (file)
@@ -68,7 +68,7 @@ struct _notmuch_config {
     char *database_path;
     char *user_name;
     char *user_primary_email;
-    char **user_other_email;
+    const char **user_other_email;
     size_t user_other_email_length;
     const char **new_tags;
     size_t new_tags_length;
@@ -151,26 +151,33 @@ get_username_from_passwd_file (void *ctx)
  * etc.), this function will print a message to stderr and return
  * NULL.
  *
- * Note: It is *not* an error if the specified configuration file does
- * not exist. In this case, a default configuration will be created
- * and returned. Subsequently calling notmuch_config_save will cause
- * the configuration to be written to the filename specified at the
- * time of notmuch_config_open.
+ * FILE NOT FOUND: When the specified configuration file (whether from
+ * 'filename' or the $NOTMUCH_CONFIG environment variable) does not
+ * exist, the behavior of this function depends on the 'is_new_ret'
+ * variable.
  *
- * The default configuration settings are determined as follows:
+ *     If is_new_ret is NULL, then a "file not found" message will be
+ *     printed to stderr and NULL will be returned.
+
+ *     If is_new_ret is non-NULL then a default configuration will be
+ *     returned and *is_new_ret will be set to 1 on return so that
+ *     the caller can recognize this case.
+ *
+ *     These default configuration settings are determined as
+ *     follows:
  *
- *     database_path:          $HOME/mail
+ *             database_path:          $HOME/mail
  *
- *     user_name:              From /etc/passwd
+ *             user_name:              From /etc/passwd
  *
- *     user_primary_mail:      $EMAIL variable if set, otherwise
- *                             constructed from the username and
- *                             hostname of the current machine.
+ *             user_primary_mail:      $EMAIL variable if set, otherwise
+ *                                     constructed from the username and
+ *                                     hostname of the current machine.
  *
- *     user_other_email:       Not set.
+ *             user_other_email:       Not set.
  *
- * The default configuration also contains comments to guide the user
- * in editing the file directly.
+ *     The default configuration also contains comments to guide the
+ *     user in editing the file directly.
  */
 notmuch_config_t *
 notmuch_config_open (void *ctx,
@@ -220,14 +227,19 @@ notmuch_config_open (void *ctx,
                                     G_KEY_FILE_KEEP_COMMENTS,
                                     &error))
     {
-       /* We are capable of dealing with a non-existent configuration
-        * file, so be silent about that (unless the user had set a
-        * non-default configuration file with the NOTMUCH_CONFIG
-        * variable)
+       /* If the caller passed a non-NULL value for is_new_ret, then
+        * the caller is prepared for a default configuration file in
+        * the case of FILE NOT FOUND. Otherwise, any read failure is
+        * an error.
         */
-       if (notmuch_config_env ||
-           !(error->domain == G_FILE_ERROR &&
-             error->code == G_FILE_ERROR_NOENT))
+       if (is_new_ret &&
+           error->domain == G_FILE_ERROR &&
+           error->code == G_FILE_ERROR_NOENT)
+       {
+           g_error_free (error);
+           is_new = 1;
+       }
+       else
        {
            fprintf (stderr, "Error reading configuration file %s: %s\n",
                     config->filename, error->message);
@@ -235,9 +247,6 @@ notmuch_config_open (void *ctx,
            g_error_free (error);
            return NULL;
        }
-
-       g_error_free (error);
-       is_new = 1;
     }
 
     /* Whenever we know of configuration sections that don't appear in
@@ -465,7 +474,7 @@ notmuch_config_set_user_primary_email (notmuch_config_t *config,
     config->user_primary_email = NULL;
 }
 
-char **
+const char **
 notmuch_config_get_user_other_email (notmuch_config_t *config,
                                     size_t *length)
 {
@@ -553,3 +562,144 @@ notmuch_config_set_new_tags (notmuch_config_t *config,
     config->new_tags = NULL;
 }
 
+/* Given a configuration item of the form <group>.<key> return the
+ * component group and key. If any error occurs, print a message on
+ * stderr and return 1. Otherwise, return 0.
+ *
+ * Note: This function modifies the original 'item' string.
+ */
+static int
+_item_split (char *item, char **group, char **key)
+{
+    char *period;
+
+    *group = item;
+
+    period = index (item, '.');
+    if (period == NULL || *(period+1) == '\0') {
+       fprintf (stderr,
+                "Invalid configuration name: %s\n"
+                "(Should be of the form <section>.<item>)\n", item);
+       return 1;
+    }
+
+    *period = '\0';
+    *key = period + 1;
+
+    return 0;
+}
+
+static int
+notmuch_config_command_get (void *ctx, char *item)
+{
+    notmuch_config_t *config;
+
+    config = notmuch_config_open (ctx, NULL, NULL);
+    if (config == NULL)
+       return 1;
+
+    if (strcmp(item, "database.path") == 0) {
+       printf ("%s\n", notmuch_config_get_database_path (config));
+    } else if (strcmp(item, "user.name") == 0) {
+       printf ("%s\n", notmuch_config_get_user_name (config));
+    } else if (strcmp(item, "user.primary_email") == 0) {
+       printf ("%s\n", notmuch_config_get_user_primary_email (config));
+    } else if (strcmp(item, "user.other_email") == 0) {
+       const char **other_email;
+       size_t i, length;
+       
+       other_email = notmuch_config_get_user_other_email (config, &length);
+       for (i = 0; i < length; i++)
+           printf ("%s\n", other_email[i]);
+    } else if (strcmp(item, "new.tags") == 0) {
+       const char **tags;
+       size_t i, length;
+
+       tags = notmuch_config_get_new_tags (config, &length);
+       for (i = 0; i < length; i++)
+           printf ("%s\n", tags[i]);
+    } else {
+       char **value;
+       size_t i, length;
+       char *group, *key;
+
+       if (_item_split (item, &group, &key))
+           return 1;
+
+       value = g_key_file_get_string_list (config->key_file,
+                                           group, key,
+                                           &length, NULL);
+       if (value == NULL) {
+           fprintf (stderr, "Unknown configuration item: %s.%s\n",
+                    group, key);
+           return 1;
+       }
+
+       for (i = 0; i < length; i++)
+           printf ("%s\n", value[i]);
+
+       free (value);
+    }
+
+    notmuch_config_close (config);
+
+    return 0;
+}
+
+static int
+notmuch_config_command_set (void *ctx, char *item, int argc, char *argv[])
+{
+    notmuch_config_t *config;
+    char *group, *key;
+    int ret;
+
+    if (_item_split (item, &group, &key))
+       return 1;
+
+    config = notmuch_config_open (ctx, NULL, NULL);
+    if (config == NULL)
+       return 1;
+
+    /* With only the name of an item, we clear it from the
+     * configuration file.
+     *
+     * With a single value, we set it as a string.
+     *
+     * With multiple values, we set them as a string list.
+     */
+    switch (argc) {
+    case 0:
+       g_key_file_remove_key (config->key_file, group, key, NULL);
+       break;
+    case 1:
+       g_key_file_set_string (config->key_file, group, key, argv[0]);
+       break;
+    default:
+       g_key_file_set_string_list (config->key_file, group, key,
+                                   (const gchar **) argv, argc);
+       break;
+    }
+
+    ret = notmuch_config_save (config);
+    notmuch_config_close (config);
+
+    return ret;
+}
+
+int
+notmuch_config_command (void *ctx, int argc, char *argv[])
+{
+    if (argc < 2) {
+       fprintf (stderr, "Error: notmuch config requires at least two arguments.\n");
+       return 1;
+    }
+
+    if (strcmp (argv[0], "get") == 0)
+       return notmuch_config_command_get (ctx, argv[1]);
+    else if (strcmp (argv[0], "set") == 0)
+       return notmuch_config_command_set (ctx, argv[1], argc - 2, argv + 2);
+
+    fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
+            argv[0]);
+    return 1;
+}
index fd1de3b957389b3cf39c04727a7d2595646143bb..23d04b8b1ea01083de530204bc4b95eeaaf130c5 100644 (file)
@@ -119,7 +119,7 @@ static int
 address_is_users (const char *address, notmuch_config_t *config)
 {
     const char *primary;
-    char **other;
+    const char **other;
     size_t i, other_len;
 
     primary = notmuch_config_get_user_primary_email (config);
@@ -312,7 +312,8 @@ static const char *
 guess_from_received_header (notmuch_config_t *config, notmuch_message_t *message)
 {
     const char *received,*primary,*by;
-    char **other,*tohdr;
+    const char **other;
+    char *tohdr;
     char *mta,*ptr,*token;
     char *domain=NULL;
     char *tld=NULL;
@@ -481,9 +482,6 @@ notmuch_reply_format_default(void *ctx, notmuch_config_t *config, notmuch_query_
        g_mime_object_set_header (GMIME_OBJECT (reply),
                                  "From", from_addr);
 
-       g_mime_object_set_header (GMIME_OBJECT (reply), "Bcc",
-                          notmuch_config_get_user_primary_email (config));
-
        in_reply_to = talloc_asprintf (ctx, "<%s>",
                             notmuch_message_get_message_id (message));
 
@@ -558,9 +556,6 @@ notmuch_reply_format_headers_only(void *ctx, notmuch_config_t *config, notmuch_q
 
        (void)add_recipients_from_message (reply, config, message);
 
-       g_mime_object_set_header (GMIME_OBJECT (reply), "Bcc",
-                          notmuch_config_get_user_primary_email (config));
-
        reply_headers = g_mime_object_to_string (GMIME_OBJECT (reply));
        printf ("%s", reply_headers);
        free (reply_headers);
index 8a1cdca3f1dcce5feea71a580c250e4bfabe9084..bb989dac854bc7f12f4207c5a4eaa237d1401309 100644 (file)
 
 #include "notmuch-client.h"
 
+typedef enum {
+    OUTPUT_SUMMARY,
+    OUTPUT_THREADS,
+    OUTPUT_MESSAGES,
+    OUTPUT_FILES,
+    OUTPUT_TAGS
+} output_t;
+
 typedef struct search_format {
     const char *results_start;
-    const char *thread_start;
-    void (*thread) (const void *ctx,
-                   const char *thread_id,
-                   const time_t date,
-                   const int matched,
-                   const int total,
-                   const char *authors,
-                   const char *subject);
+    const char *item_start;
+    void (*item_id) (const void *ctx,
+                    const char *item_type,
+                    const char *item_id);
+    void (*thread_summary) (const void *ctx,
+                           const char *thread_id,
+                           const time_t date,
+                           const int matched,
+                           const int total,
+                           const char *authors,
+                           const char *subject);
     const char *tag_start;
     const char *tag;
     const char *tag_sep;
     const char *tag_end;
-    const char *thread_sep;
-    const char *thread_end;
+    const char *item_sep;
+    const char *item_end;
     const char *results_end;
 } search_format_t;
 
+static void
+format_item_id_text (const void *ctx,
+                    const char *item_type,
+                    const char *item_id);
+
 static void
 format_thread_text (const void *ctx,
                    const char *thread_id,
@@ -50,14 +66,20 @@ format_thread_text (const void *ctx,
 static const search_format_t format_text = {
     "",
        "",
+           format_item_id_text,
            format_thread_text,
            " (",
                "%s", " ",
-           ")", "",
-       "\n",
-    "",
+           ")", "\n",
+       "",
+    "\n",
 };
 
+static void
+format_item_id_json (const void *ctx,
+                    const char *item_type,
+                    const char *item_id);
+
 static void
 format_thread_json (const void *ctx,
                    const char *thread_id,
@@ -69,6 +91,7 @@ format_thread_json (const void *ctx,
 static const search_format_t format_json = {
     "[",
        "{",
+           format_item_id_json,
            format_thread_json,
            "\"tags\": [",
                "\"%s\"", ", ",
@@ -77,6 +100,14 @@ static const search_format_t format_json = {
     "]\n",
 };
 
+static void
+format_item_id_text (unused (const void *ctx),
+                    const char *item_type,
+                    const char *item_id)
+{
+    printf ("%s%s", item_type, item_id);
+}
+
 static void
 format_thread_text (const void *ctx,
                    const char *thread_id,
@@ -95,6 +126,19 @@ format_thread_text (const void *ctx,
            subject);
 }
 
+static void
+format_item_id_json (const void *ctx,
+                    unused (const char *item_type),
+                    const char *item_id)
+{
+    void *ctx_quote = talloc_new (ctx);
+
+    printf ("%s", json_quote_str (ctx_quote, item_id));
+
+    talloc_free (ctx_quote);
+    
+}
+
 static void
 format_thread_json (const void *ctx,
                    const char *thread_id,
@@ -122,11 +166,12 @@ format_thread_json (const void *ctx,
     talloc_free (ctx_quote);
 }
 
-static void
+static int
 do_search_threads (const void *ctx,
                   const search_format_t *format,
                   notmuch_query_t *query,
-                  notmuch_sort_t sort)
+                  notmuch_sort_t sort,
+                  output_t output)
 {
     notmuch_thread_t *thread;
     notmuch_threads_t *threads;
@@ -134,55 +179,160 @@ do_search_threads (const void *ctx,
     time_t date;
     int first_thread = 1;
 
-    fputs (format->results_start, stdout);
+    threads = notmuch_query_search_threads (query);
+    if (threads == NULL)
+       return 1;
 
-    for (threads = notmuch_query_search_threads (query);
+    for (;
         notmuch_threads_valid (threads);
         notmuch_threads_move_to_next (threads))
     {
        int first_tag = 1;
 
-       if (! first_thread)
-           fputs (format->thread_sep, stdout);
+       if (first_thread)
+           fputs (format->results_start, stdout);
+       else
+           fputs (format->item_sep, stdout);
 
        thread = notmuch_threads_get (threads);
 
-       if (sort == NOTMUCH_SORT_OLDEST_FIRST)
-           date = notmuch_thread_get_oldest_date (thread);
-       else
-           date = notmuch_thread_get_newest_date (thread);
-
-       fputs (format->thread_start, stdout);
-
-       format->thread (ctx,
-                       notmuch_thread_get_thread_id (thread),
-                       date,
-                       notmuch_thread_get_matched_messages (thread),
-                       notmuch_thread_get_total_messages (thread),
-                       notmuch_thread_get_authors (thread),
-                       notmuch_thread_get_subject (thread));
-
-       fputs (format->tag_start, stdout);
-
-       for (tags = notmuch_thread_get_tags (thread);
-            notmuch_tags_valid (tags);
-            notmuch_tags_move_to_next (tags))
-       {
-           if (! first_tag)
-               fputs (format->tag_sep, stdout);
-           printf (format->tag, notmuch_tags_get (tags));
-           first_tag = 0;
-       }
+       if (output == OUTPUT_THREADS) {
+           format->item_id (ctx, "thread:",
+                            notmuch_thread_get_thread_id (thread));
+       } else { /* output == OUTPUT_SUMMARY */
+           fputs (format->item_start, stdout);
+
+           if (sort == NOTMUCH_SORT_OLDEST_FIRST)
+               date = notmuch_thread_get_oldest_date (thread);
+           else
+               date = notmuch_thread_get_newest_date (thread);
+
+           format->thread_summary (ctx,
+                                   notmuch_thread_get_thread_id (thread),
+                                   date,
+                                   notmuch_thread_get_matched_messages (thread),
+                                   notmuch_thread_get_total_messages (thread),
+                                   notmuch_thread_get_authors (thread),
+                                   notmuch_thread_get_subject (thread));
+
+           fputs (format->tag_start, stdout);
+
+           for (tags = notmuch_thread_get_tags (thread);
+                notmuch_tags_valid (tags);
+                notmuch_tags_move_to_next (tags))
+           {
+               if (! first_tag)
+                   fputs (format->tag_sep, stdout);
+               printf (format->tag, notmuch_tags_get (tags));
+               first_tag = 0;
+           }
 
-       fputs (format->tag_end, stdout);
-       fputs (format->thread_end, stdout);
+           fputs (format->tag_end, stdout);
+
+           fputs (format->item_end, stdout);
+       }
 
        first_thread = 0;
 
        notmuch_thread_destroy (thread);
     }
 
-    fputs (format->results_end, stdout);
+    if (! first_thread)
+       fputs (format->results_end, stdout);
+
+    return 0;
+}
+
+static int
+do_search_messages (const void *ctx,
+                   const search_format_t *format,
+                   notmuch_query_t *query,
+                   output_t output)
+{
+    notmuch_message_t *message;
+    notmuch_messages_t *messages;
+    int first_message = 1;
+
+    messages = notmuch_query_search_messages (query);
+    if (messages == NULL)
+       return 1;
+
+    for (;
+        notmuch_messages_valid (messages);
+        notmuch_messages_move_to_next (messages))
+    {
+       message = notmuch_messages_get (messages);
+
+       if (first_message)
+           fputs (format->results_start, stdout);
+       else
+           fputs (format->item_sep, stdout);
+
+       if (output == OUTPUT_FILES) {
+           format->item_id (ctx, "",
+                            notmuch_message_get_filename (message));
+       } else { /* output == OUTPUT_MESSAGES */
+           format->item_id (ctx, "id:",
+                            notmuch_message_get_message_id (message));
+       }
+
+       first_message = 0;
+
+       notmuch_message_destroy (message);
+    }
+
+    notmuch_messages_destroy (messages);
+
+    if (! first_message)
+       fputs (format->results_end, stdout);
+
+    return 0;
+}
+
+static int
+do_search_tags (const void *ctx,
+               notmuch_database_t *notmuch,
+               const search_format_t *format,
+               notmuch_query_t *query)
+{
+    notmuch_messages_t *messages = NULL;
+    notmuch_tags_t *tags;
+    const char *tag;
+    int first_tag = 1;
+
+    /* Special-case query of "*" for better performance. */
+    if (strcmp (notmuch_query_get_query_string (query), "*") == 0) {
+       tags = notmuch_database_get_all_tags (notmuch);
+    } else {
+       messages = notmuch_query_search_messages (query);
+       if (messages == NULL)
+           return 1;
+
+       tags = notmuch_messages_collect_tags (messages);
+    }
+    if (tags == NULL)
+       return 1;
+
+    for (;
+        notmuch_tags_valid (tags);
+        notmuch_tags_move_to_next (tags))
+    {
+       tag = notmuch_tags_get (tags);
+
+       if (! first_tag)
+           fputs (format->item_sep, stdout);
+
+       format->item_id (ctx, "", tag);
+
+       first_tag = 0;
+    }
+
+    notmuch_tags_destroy (tags);
+
+    if (messages)
+       notmuch_messages_destroy (messages);
+
+    return 0;
 }
 
 int
@@ -195,7 +345,8 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
     char *opt;
     notmuch_sort_t sort = NOTMUCH_SORT_NEWEST_FIRST;
     const search_format_t *format = &format_text;
-    int i;
+    int i, ret;
+    output_t output = OUTPUT_SUMMARY;
 
     for (i = 0; i < argc && argv[i][0] == '-'; i++) {
        if (strcmp (argv[i], "--") == 0) {
@@ -222,6 +373,22 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
                fprintf (stderr, "Invalid value for --format: %s\n", opt);
                return 1;
            }
+       } else if (STRNCMP_LITERAL (argv[i], "--output=") == 0) {
+           opt = argv[i] + sizeof ("--output=") - 1;
+           if (strcmp (opt, "summary") == 0) {
+               output = OUTPUT_SUMMARY;
+           } else if (strcmp (opt, "threads") == 0) {
+               output = OUTPUT_THREADS;
+           } else if (strcmp (opt, "messages") == 0) {
+               output = OUTPUT_MESSAGES;
+           } else if (strcmp (opt, "files") == 0) {
+               output = OUTPUT_FILES;
+           } else if (strcmp (opt, "tags") == 0) {
+               output = OUTPUT_TAGS;
+           } else {
+               fprintf (stderr, "Invalid value for --output: %s\n", opt);
+               return 1;
+           }
        } else {
            fprintf (stderr, "Unrecognized option: %s\n", argv[i]);
            return 1;
@@ -258,10 +425,23 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
 
     notmuch_query_set_sort (query, sort);
 
-    do_search_threads (ctx, format, query, sort);
+    switch (output) {
+    default:
+    case OUTPUT_SUMMARY:
+    case OUTPUT_THREADS:
+       ret = do_search_threads (ctx, format, query, sort, output);
+       break;
+    case OUTPUT_MESSAGES:
+    case OUTPUT_FILES:
+       ret = do_search_messages (ctx, format, query, output);
+       break;
+    case OUTPUT_TAGS:
+       ret = do_search_tags (ctx, notmuch, format, query);
+       break;
+    }
 
     notmuch_query_destroy (query);
     notmuch_database_close (notmuch);
 
-    return 0;
+    return ret;
 }
index 955deb7e51330c633d1a1017de47e35742cc8d4c..c3ea9371c13cbc4058922fbdc920a5755e4084f0 100644 (file)
@@ -92,9 +92,9 @@ notmuch_setup_command (unused (void *ctx),
                       unused (int argc), unused (char *argv[]))
 {
     char *response = NULL;
-    size_t response_size;
+    size_t response_size = 0;
     notmuch_config_t *config;
-    char **old_other_emails;
+    const char **old_other_emails;
     size_t old_other_emails_len;
     GPtrArray *other_emails;
     unsigned int i;
index af7854d814b929c9be1b43bb5b3d33cfa8a0e340..ef421ec7cc382ea6e3dbe188c50d1140423b6f30 100644 (file)
@@ -77,6 +77,20 @@ static const show_format_t format_json = {
     "]"
 };
 
+static void
+format_message_mbox (const void *ctx,
+                    notmuch_message_t *message,
+                    unused (int indent));
+
+static const show_format_t format_mbox = {
+    "",
+        "", format_message_mbox,
+            "", NULL, "",
+            "", NULL, "",
+        "", "",
+    ""
+};
+
 static const char *
 _get_tags_as_string (const void *ctx, notmuch_message_t *message)
 {
@@ -163,6 +177,112 @@ format_message_json (const void *ctx, notmuch_message_t *message, unused (int in
     talloc_free (ctx_quote);
 }
 
+/* Extract just the email address from the contents of a From:
+ * header. */
+static const char *
+_extract_email_address (const void *ctx, const char *from)
+{
+    InternetAddressList *addresses;
+    InternetAddress *address;
+    InternetAddressMailbox *mailbox;
+    const char *email = "MAILER-DAEMON";
+
+    addresses = internet_address_list_parse_string (from);
+
+    /* Bail if there is no address here. */
+    if (addresses == NULL || internet_address_list_length (addresses) < 1)
+       goto DONE;
+
+    /* Otherwise, just use the first address. */
+    address = internet_address_list_get_address (addresses, 0);
+
+    /* The From header should never contain an address group rather
+     * than a mailbox. So bail if it does. */
+    if (! INTERNET_ADDRESS_IS_MAILBOX (address))
+       goto DONE;
+
+    mailbox = INTERNET_ADDRESS_MAILBOX (address);
+    email = internet_address_mailbox_get_addr (mailbox);
+    email = talloc_strdup (ctx, email);
+
+  DONE:
+    /* XXX: How to free addresses here? */
+    return email;
+   }
+
+/* Return 1 if 'line' is an mbox From_ line---that is, a line
+ * beginning with zero or more '>' characters followed by the
+ * characters 'F', 'r', 'o', 'm', and space.
+ *
+ * Any characters at all may appear after that in the line.
+ */
+static int
+_is_from_line (const char *line)
+{
+    const char *s = line;
+
+    if (line == NULL)
+       return 0;
+
+    while (*s == '>')
+       s++;
+
+    if (STRNCMP_LITERAL (s, "From ") == 0)
+       return 1;
+    else
+       return 0;
+}
+
+/* Print a message in "mboxrd" format as documented, for example,
+ * here:
+ *
+ * http://qmail.org/qmail-manual-html/man5/mbox.html
+ */
+static void
+format_message_mbox (const void *ctx,
+                    notmuch_message_t *message,
+                    unused (int indent))
+{
+    const char *filename;
+    FILE *file;
+    const char *from;
+
+    time_t date;
+    struct tm date_gmtime;
+    char date_asctime[26];
+
+    char *line = NULL;
+    size_t line_size;
+    ssize_t line_len;
+
+    filename = notmuch_message_get_filename (message);
+    file = fopen (filename, "r");
+    if (file == NULL) {
+       fprintf (stderr, "Failed to open %s: %s\n",
+                filename, strerror (errno));
+       return;
+    }
+
+    from = notmuch_message_get_header (message, "from");
+    from = _extract_email_address (ctx, from);
+
+    date = notmuch_message_get_date (message);
+    gmtime_r (&date, &date_gmtime);
+    asctime_r (&date_gmtime, date_asctime);
+
+    printf ("From %s %s", from, date_asctime);
+
+    while ((line_len = getline (&line, &line_size, file)) != -1 ) {
+       if (_is_from_line (line))
+           putchar ('>');
+       printf ("%s", line);
+    }
+
+    printf ("\n");
+
+    fclose (file);
+}
+
 static void
 format_headers_text (const void *ctx, notmuch_message_t *message)
 {
@@ -408,21 +528,105 @@ show_messages (void *ctx, const show_format_t *format, notmuch_messages_t *messa
     fputs (format->message_set_end, stdout);
 }
 
+/* Support for --format=raw */
+static int
+do_show_raw (unused(void *ctx), notmuch_query_t *query)
+{
+    notmuch_messages_t *messages;
+    notmuch_message_t *message;
+    const char *filename;
+    FILE *file;
+    size_t size;
+    char buf[4096];
+
+    if (notmuch_query_count_messages (query) != 1) {
+       fprintf (stderr, "Error: search term did not match precisely one message.\n");
+       return 1;
+    }
+
+    messages = notmuch_query_search_messages (query);
+    message = notmuch_messages_get (messages);
+
+    if (message == NULL) {
+       fprintf (stderr, "Error: Cannot find matching message.\n");
+       return 1;
+    }
+
+    filename = notmuch_message_get_filename (message);
+    if (filename == NULL) {
+       fprintf (stderr, "Error: Cannot message filename.\n");
+       return 1;
+    }
+
+    file = fopen (filename, "r");
+    if (file == NULL) {
+       fprintf (stderr, "Error: Cannot open file %s: %s\n", filename, strerror (errno));
+       return 1;
+    }
+
+    while (!feof (file)) {
+       size = fread (buf, 1, sizeof (buf), file);
+       fwrite (buf, size, 1, stdout);
+    }
+
+    fclose (file);
+
+    return 0;
+}
+
+/* Support for --format=text|json|mbox */
+static int
+do_show (void *ctx,
+        notmuch_query_t *query,
+        const show_format_t *format,
+        int entire_thread)
+{
+    notmuch_threads_t *threads;
+    notmuch_thread_t *thread;
+    notmuch_messages_t *messages;
+    int first_toplevel = 1;
+
+    fputs (format->message_set_start, stdout);
+
+    for (threads = notmuch_query_search_threads (query);
+        notmuch_threads_valid (threads);
+        notmuch_threads_move_to_next (threads))
+    {
+       thread = notmuch_threads_get (threads);
+
+       messages = notmuch_thread_get_toplevel_messages (thread);
+
+       if (messages == NULL)
+           INTERNAL_ERROR ("Thread %s has no toplevel messages.\n",
+                           notmuch_thread_get_thread_id (thread));
+
+       if (!first_toplevel)
+           fputs (format->message_set_sep, stdout);
+       first_toplevel = 0;
+
+       show_messages (ctx, format, messages, 0, entire_thread);
+
+       notmuch_thread_destroy (thread);
+
+    }
+
+    fputs (format->message_set_end, stdout);
+
+    return 0;
+}
+
 int
 notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
 {
     notmuch_config_t *config;
     notmuch_database_t *notmuch;
     notmuch_query_t *query;
-    notmuch_threads_t *threads;
-    notmuch_thread_t *thread;
-    notmuch_messages_t *messages;
     char *query_string;
     char *opt;
     const show_format_t *format = &format_text;
     int entire_thread = 0;
     int i;
-    int first_toplevel = 1;
+    int raw = 0;
 
     for (i = 0; i < argc && argv[i][0] == '-'; i++) {
        if (strcmp (argv[i], "--") == 0) {
@@ -436,6 +640,10 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
            } else if (strcmp (opt, "json") == 0) {
                format = &format_json;
                entire_thread = 1;
+           } else if (strcmp (opt, "mbox") == 0) {
+               format = &format_mbox;
+           } else if (strcmp (opt, "raw") == 0) {
+               raw = 1;
            } else {
                fprintf (stderr, "Invalid value for --format: %s\n", opt);
                return 1;
@@ -477,31 +685,10 @@ notmuch_show_command (void *ctx, unused (int argc), unused (char *argv[]))
        return 1;
     }
 
-    fputs (format->message_set_start, stdout);
-
-    for (threads = notmuch_query_search_threads (query);
-        notmuch_threads_valid (threads);
-        notmuch_threads_move_to_next (threads))
-    {
-       thread = notmuch_threads_get (threads);
-
-       messages = notmuch_thread_get_toplevel_messages (thread);
-
-       if (messages == NULL)
-           INTERNAL_ERROR ("Thread %s has no toplevel messages.\n",
-                           notmuch_thread_get_thread_id (thread));
-
-       if (!first_toplevel)
-           fputs (format->message_set_sep, stdout);
-       first_toplevel = 0;
-
-       show_messages (ctx, format, messages, 0, entire_thread);
-
-       notmuch_thread_destroy (thread);
-
-    }
-
-    fputs (format->message_set_end, stdout);
+    if (raw)
+       return do_show_raw (ctx, query);
+    else
+       return do_show (ctx, query, format, entire_thread);
 
     notmuch_query_destroy (query);
     notmuch_database_close (notmuch);
index 86830f4b403fd0fccbbf4928f5c01565abe1d825..3878a040cbcf1e10f1bdf3b568b0cb8affeb2a2e 100644 (file)
--- a/notmuch.1
+++ b/notmuch.1
@@ -150,6 +150,53 @@ include
 
 Presents the results in either JSON or plain-text (default).
 .RE
+
+.RS 4
+.TP 4
+.B \-\-output=(summary|threads|messages|files|tags)
+
+.RS 4
+.TP 4
+.B summary
+
+Output a summary of each thread with any message matching the search
+terms. The summary includes the thread ID, date, the number of
+messages in the thread (both the number matched and the total number),
+the authors of the thread and the subject.
+.RE
+.RS 4
+.TP 4
+.B threads
+
+Output the thread IDs of all threads with any message matching the
+search terms, either one per line (\-\-format=text) or as a JSON array
+(\-\-format=json).
+.RE
+.RS 4
+.TP 4
+.B messages
+
+Output the message IDs of all messages matching the search terms,
+either one per line (\-\-format=text) or as a JSON array
+(\-\-format=json).
+.RE
+.RS 4
+.TP 4
+.B files
+
+Output the filenames of all messages matching the search terms, either
+one per line (\-\-format=text) or as a JSON array (\-\-format=json).
+.RE
+.RS 4
+.TP 4
+.B tags
+
+Output all tags that appear on any message matching the search terms,
+either one per line (\-\-format=text) or as a JSON array
+(\-\-format=json).
+.RE
+.RE
+
 .RS 4
 .TP 4
 .BR \-\-sort= ( newest\-first | oldest\-first )
@@ -201,31 +248,54 @@ matched message will be displayed.
 
 .RS 4
 .TP 4
-.B \-\-format=(json|text)
+.B \-\-format=(text|json|mbox|raw)
 
 .RS 4
 .TP 4
 .B text
 
-The default plain-text format  has  text-content  MIME parts
+The default plain-text format has all text-content MIME parts
 decoded. Various components in the output,
 .RB ( message ", " header ", " body ", " attachment ", and MIME " part ),
 will be delimited by easily-parsed markers. Each marker consists of a
 Control-L character (ASCII decimal 12), the name of the marker, and
 then either an opening or closing brace, ('{' or '}'), to either open
 or close the component.
-
 .RE
 .RS 4
 .TP 4
 .B json
 
-Format output as Javascript Object Notation (JSON). JSON output always
-includes all messages in a matching thread; in effect
+The output is formatted with Javascript Object Notation (JSON). This
+format is more robust than the text format for automated
+processing. JSON output always includes all messages in a matching
+thread; in effect
 .B \-\-format=json
 implies
 .B \-\-entire\-thread
 
+.RE
+.RS 4
+.TP 4
+.B mbox
+
+All matching messages are output in the traditional, Unix mbox format
+with each message being prefixed by a line beginning with "From " and
+a blank line separating each message. Lines in the message content
+beginning with "From " (preceded by zero or more '>' characters) have
+an additional '>' character added. This reversible escaping
+is termed "mboxrd" format and described in detail here:
+http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html
+.RE
+.RS 4
+.TP 4
+.B raw
+
+The original, raw content of the email message is displayed.
+Consumers of this format should expect to implement MIME decoding and
+similar functions. This format must only be used with search terms
+matching a single message.
+
 .RE
 A common use of
 .B notmuch show
@@ -389,6 +459,44 @@ See the
 section below for details of the supported syntax for <search-terms>.
 .RE
 
+The
+.B config
+command can be used to get or set settings int the notmuch
+configuration file.
+
+.RS 4
+.TP 4
+.BR "config get " <section> . <item>
+
+The value of the specified configuration item is printed to stdout. If
+the item has multiple values, each value is separated by a newline
+character.
+
+Available configuration items include at least
+
+       database.path
+
+       user.name
+
+       user.primary_email
+
+       user.other_email
+
+       new.tags
+.RE
+
+.RS 4
+.TP 4
+.BR "config set " <section> . "<item> [values ...]"
+
+The specified configuration item is set to the given value.  To
+specify a multiple-value item, provide each value as a separate
+command-line argument.
+
+If no values are provided, the specified configuration item will be
+removed from the configuration file.
+.RE
+
 .SH SEARCH SYNTAX
 Several notmuch commands accept a common syntax for search terms.
 
index 13e2612d4fad63caa4368e6c7e128b3640e90701..9ba0ec0308ae2b172b8410405a653e6f643e4840 100644 (file)
--- a/notmuch.c
+++ b/notmuch.c
@@ -165,6 +165,39 @@ command_t commands[] = {
       "\t\tPresents the results in either JSON or\n"
       "\t\tplain-text (default)\n"
       "\n"
+      "\t--output=(summary|threads|messages|files|tags)\n"
+      "\n"
+      "\t\tsummary (default)\n"
+      "\n"
+      "\t\tOutput a summary of each thread with any message matching the\n"
+      "\t\tsearch terms. The summary includes the thread ID, date, the\n"
+      "\t\tnumber of messages in the thread (both the number matched and\n"
+      "\t\tthe total number), the authors of the thread and the subject.\n"
+      "\n"
+      "\t\tthreads\n"
+      "\n"
+      "\t\tOutput the thread IDs of all threads with any message matching\n"
+      "\t\tthe search terms, either one per line (--format=text) or as a\n"
+      "\t\tJSON array (--format=json).\n"
+      "\n"
+      "\t\tmessages\n"
+      "\n"
+      "\t\tOutput the message IDs of all messages matching the search\n"
+      "\t\tterms, either one per line (--format=text) or as a JSON array\n"
+      "\t\t(--format=json).\n"
+      "\n"
+      "\t\tfiles\n"
+      "\n"
+      "\t\tOutput the filenames of all messages matching the search\n"
+      "\t\tterms, either one per line (--format=text) or as a JSON array\n"
+      "\t\t(--format=json).\n"
+      "\n"
+      "\t\ttags\n"
+      "\n"
+      "\t\tOutput all tags that appear on any message matching the search\n"
+      "\t\tterms, either one per line (--format=text) or as a JSON array\n"
+      "\t\t(--format=json).\n"
+      "\n"
       "\t--sort=(newest-first|oldest-first)\n"
       "\n"
       "\t\tPresent results in either chronological order\n"
@@ -189,12 +222,12 @@ command_t commands[] = {
       "\t\tall messages in the same thread as any matched\n"
       "\t\tmessage will be displayed.\n"
       "\n"
-      "\t--format=(json|text)\n"
+      "\t--format=(text|json|mbox|raw)\n"
       "\n"
-      "\t\ttext\t(default)\n"
+      "\t\ttext (default)\n"
       "\n"
-      "\t\tThe plain-text has all text-content MIME parts decoded.\n"
-      "\t\tVarious components in the output, ('message', 'header',\n"
+      "\t\tThe default plain-text format has all text-content MIME parts\n"
+      "\t\tdecoded. Various components in the output, ('message', 'header',\n"
       "\t\t'body', 'attachment', and MIME 'part') are delimited by\n"
       "\t\teasily-parsed markers. Each marker consists of a Control-L\n"
       "\t\tcharacter (ASCII decimal 12), the name of the marker, and\n"
@@ -203,9 +236,30 @@ command_t commands[] = {
       "\n"
       "\t\tjson\n"
       "\n"
-      "\t\tFormat output as Javascript Object Notation (JSON).\n"
-      "\t\tJSON output always includes all messages in a matching,\n"
-      "\t\tthread i.e. '--format=json' implies '--entire-thread'\n"
+      "\t\tThe output is formatted with Javascript Object Notation\n"
+      "\t\t(JSON). This format is more robust than the text format\n"
+      "\t\tfor automated processing. JSON output always includes all\n"
+      "\t\tmessages in a matching thread; in effect '--format=json'\n"
+      "\t\timplies '--entire-thread'\n"
+      "\n"
+      "\t\tmbox\n"
+      "\n"
+      "\t\tAll matching messages are output in the traditional, Unix\n"
+      "\t\tmbox format with each message being prefixed by a line\n"
+      "\t\tbeginning with 'From ' and a blank line separating each\n"
+      "\t\tmessage. Lines in the message content beginning with 'From '\n"
+      "\t\t(preceded by zero or more '>' characters) have an additional\n"
+      "\t\t'>' character added. This reversible escaping is termed\n"
+      "\t\t\"mboxrd\" format and described in detail here:\n"
+      "\n"
+      "\t\thttp://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html\n"
+      "\n"
+      "\t\traw\n"
+      "\n"
+      "\t\tThe original, raw content of the email message is displayed.\n"
+      "\t\tConsumers of this format should expect to implement MIME\n"
+      "\t\tdecoding and similar functions. This format must only\n"
+      "\t\tbe used with search terms matching a single message.\n"
       "\n"
       "\tA common use of \"notmuch show\" is to display a single\n"
       "\tthread of email messages. For this, use a search term of\n"
@@ -307,6 +361,31 @@ command_t commands[] = {
       "\tby the \"--format=json\" option of \"notmuch show\". If the\n"
       "\tmessage specified by the search terms does not include a\n"
       "\tpart with the specified \"id\" there will be no output." },
+    { "config", notmuch_config_command,
+      "[get|set] <section>.<item> [value ...]",
+      "Get or set settings in the notmuch configuration file.",
+      "    config get <section>.<item>\n"
+      "\n"
+      "\tThe value of the specified configuration item is printed\n"
+      "\tto stdout. If the item has multiple values, each value\n"
+      "\tis separated by a newline character.\n"
+      "\n"
+      "\tAvailable configuration items include at least\n"
+      "\n"
+      "\t\tdatabase.path\n"
+      "\t\tuser.name\n"
+      "\t\tuser.primary_email\n"
+      "\t\tuser.other_email\n"
+      "\t\tnew.tags\n"
+      "\n"
+      "    config set <section>.<item> [value ...]\n"
+      "\n"
+      "\tThe specified configuration item is set to the given value.\n"
+      "\tTo specify a multiple-value item, provide each value as\n"
+      "\ta separate command-line argument.\n"
+      "\n"
+      "\tIf no values are provided, the specified configuration item\n"
+      "\twill be removed from the configuration file." },
     { "help", notmuch_help_command,
       "[<command>]",
       "This message, or more detailed help for the named command.",
diff --git a/test/.gitignore b/test/.gitignore
new file mode 100644 (file)
index 0000000..b34778f
--- /dev/null
@@ -0,0 +1,3 @@
+test-results
+corpus.mail
+smtp-dummy
diff --git a/test/Makefile b/test/Makefile
new file mode 100644 (file)
index 0000000..b6859ea
--- /dev/null
@@ -0,0 +1,7 @@
+# See Makfefile.local for the list of files to be compiled in this
+# directory.
+all:
+       $(MAKE) -C .. all
+
+.DEFAULT:
+       $(MAKE) -C .. $@
diff --git a/test/Makefile.local b/test/Makefile.local
new file mode 100644 (file)
index 0000000..7b602bc
--- /dev/null
@@ -0,0 +1,14 @@
+# -*- makefile -*-
+
+dir := test
+
+$(dir)/smtp-dummy: $(dir)/smtp-dummy.c
+       $(call quiet,CC) $^ -o $@
+
+.PHONY: test check
+test:  all $(dir)/smtp-dummy
+       @${dir}/notmuch-test $(OPTIONS)
+
+check: test
+
+CLEAN := $(CLEAN) $(dir)/smtp-dummy
diff --git a/test/README b/test/README
new file mode 100644 (file)
index 0000000..ebaa3cf
--- /dev/null
@@ -0,0 +1,202 @@
+Notmuch test suite
+==================
+This directory contains the test suite for notmuch.
+
+When fixing bugs or enhancing notmuch, you are strongly encouraged to
+add tests in this directory to cover what you are trying to fix or
+enhance.
+
+Running Tests
+-------------
+The easiest way to run tests is to say "make test", (or simply run the
+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.)
+
+The following command-line options are available when running tests:
+
+--debug::
+       This may help the person who is developing a new test.
+       It causes the command defined with test_debug to run.
+
+--immediate::
+       This causes the test to immediately exit upon the first
+       failed test.
+
+--valgrind::
+       Execute notmuch with valgrind and exit with status
+       126 on errors (just like regular tests, this will only stop
+       the test script when running under -i).  Valgrind errors
+       go to stderr, so you might want to pass the -v option, too.
+
+       Since it makes no sense to run the tests with --valgrind and
+       not see any output, this option implies --verbose.  For
+       convenience, it also implies --tee.
+
+--tee::
+       In addition to printing the test output to the terminal,
+       write it to files named 't/test-results/$TEST_NAME.out'.
+       As the names depend on the tests' file names, it is safe to
+       run the tests with this option in parallel.
+
+When invoking the test suite via "make test" any of the above options
+can be specified as follows:
+
+       make test OPTIONS="--verbose"
+
+Skipping Tests
+--------------
+If, for any reason, you need to skip one or more tests, you can do so
+by setting the NOTMUCH_SKIP_TESTS variable to the name of one or more
+sections of tests.
+
+For example:
+
+    $ NOTMUCH_SKIP_TESTS="search reply" make test
+
+Even more fine-grained skipping is possible by appending a test number
+(or glob pattern) after the section name. For example, the first
+search test and the second reply test could be skipped with:
+
+    $ NOTMUCH_SKIP_TESTS="search.1 reply.2" make test
+
+Note that some tests in the existing test suite rely on previous test
+items, so you cannot arbitrarily skip any test and expect the
+remaining tests to be unaffected.
+
+Writing Tests
+-------------
+The test script is written as a shell script.  It should start
+with the standard "#!/bin/bash" with copyright notices, and an
+assignment to variable 'test_description', like this:
+
+       #!/bin/bash
+       #
+       # Copyright (c) 2005 Junio C Hamano
+       #
+
+       test_description='xxx test (option --frotz)
+
+       This test exercises the "notmuch xxx" command when
+       given the option --frotz.'
+
+Source 'test-lib.sh'
+--------------------
+After assigning test_description, the test script should source
+test-lib.sh like this:
+
+       . ./test-lib.sh
+
+This test harness library does the following things:
+
+ - If the script is invoked with command line argument --help
+   (or -h), it shows the test_description and exits.
+
+ - Creates a temporary directory with default notmuch-config and a
+   mail store with a corpus of mail, (initially, 50 early messages
+   sent to the notmuch list). This directory is
+   test/tmp.<test-basename>. The path to notmuch-config is exported in
+   NOTMUCH_CONFIG environment variable and mail store path is stored
+   in MAIL_DIR variable.
+
+ - Defines standard test helper functions for your scripts to
+   use.  These functions are designed to make all scripts behave
+   consistently when command line arguments --verbose (or -v),
+   --debug (or -d), and --immediate (or -i) is given.
+
+End with test_done
+------------------
+Your script will be a sequence of tests, using helper functions
+from the test harness library.  At the end of the script, call
+'test_done'.
+
+Test harness library
+--------------------
+There are a handful helper functions defined in the test harness
+library for your script to use.
+
+ test_expect_success <message> <script>
+
+   This takes two strings as parameter, and evaluates the
+   <script>.  If it yields success, test is considered
+   successful.  <message> should state what it is testing.
+
+ test_expect_failure <message> <script>
+
+   This is NOT the opposite of test_expect_success, but is used
+   to mark a test that demonstrates a known breakage.  Unlike
+   the usual test_expect_success tests, which say "ok" on
+   success and "FAIL" on failure, this will say "FIXED" on
+   success and "still broken" on failure.  Failures from these
+   tests won't cause -i (immediate) to stop.
+
+ test_begin_subtest <message>
+
+   Set the test description message for a subsequent test_expect_equal
+   invocation (see below).
+
+ test_expect_equal <output> <expected>
+
+   This is an often-used convenience function built on top of
+   test_expect_success. It uses the message from the last
+   test_begin_subtest call, so call before calling
+   test_expect_equal. This function generates a successful test if
+   both the <output> and <expected> strings are identical. If not, it
+   will generate a failure and print the difference of the two
+   strings.
+
+ test_expect_equal_failure <output> <expected>
+
+   This works similar to test_expect_equal (see above) but is used to
+   mark a test that demonstrates a known breakage, (that is, the
+   author of the test expectes "output" and "expected" to differ until
+   the breakage is fixed). See test_expect_failure for details.
+
+ test_debug <script>
+
+   This takes a single argument, <script>, and evaluates it only
+   when the test script is started with --debug command line
+   argument.  This is primarily meant for use during the
+   development of a new test script.
+
+ test_emacs <emacs-lisp-expressions>
+
+   This function executes the provided emacs lisp script within
+   emacs. The script can be a sequence of emacs lisp expressions,
+   (that is, they will be evaluated within a progn form). The lisp
+   expressions can call `message' to generate output on stdout to be
+   examined by the calling test script.
+
+ test_done
+
+   Your test script must have test_done at the end.  Its purpose
+   is to summarize successes and failures in the test script and
+   exit with an appropriate error code.
+
+There are also a number of mail-specific functions which are useful in
+writing tests:
+
+  generate_message
+
+    Generates a message with an optional template. Most tests will
+    actually prefere to call add_message. See below.
+
+  add_message
+
+    Generate a message and add it to the database (by calling "notmuch
+    new"). It is sufficient to simply call add_message with no
+    arguments if you don't care about the content of the message. If
+    more control is needed, arguments can be provide to specify many
+    different header values for the new message. See the documentation
+    within test-lib.sh or refer to many example calls within existing
+    tests.
+
+  add_email_corpus
+
+    This function should be called at the beginning of a test file
+    when a test needs to operate on a non-empty body of messages. It
+    will intialize the mail database to a known state of 50 sample
+    messages, (culled from the early history of the notmuch mailing
+    list).
diff --git a/test/aggregate-results.sh b/test/aggregate-results.sh
new file mode 100755 (executable)
index 0000000..0f1ea33
--- /dev/null
@@ -0,0 +1,81 @@
+#!/bin/sh
+
+fixed=0
+success=0
+failed=0
+broken=0
+total=0
+
+for file
+do
+       while read type value
+       do
+               case $type in
+               '')
+                       continue ;;
+               fixed)
+                       fixed=$(($fixed + $value)) ;;
+               success)
+                       success=$(($success + $value)) ;;
+               failed)
+                       failed=$(($failed + $value)) ;;
+               broken)
+                       broken=$(($broken + $value)) ;;
+               total)
+                       total=$(($total + $value)) ;;
+               esac
+       done <"$file"
+done
+
+pluralize () {
+    case $2 in
+       1)
+           case $1 in
+               test)
+                   echo test ;;
+               failure)
+                   echo failure ;;
+           esac
+           ;;
+       *)
+           case $1 in
+               test)
+                   echo tests ;;
+               failure)
+                   echo failures ;;
+           esac
+           ;;
+    esac
+}
+
+echo "Notmuch test suite complete."
+if [ "$fixed" = "0" ] && [ "$failed" = "0" ]; then
+    tests=$(pluralize "test" $total)
+    printf "All $total $tests "
+    if [ "$broken" = "0" ]; then
+       echo "passed."
+    else
+       failures=$(pluralize "failure" $broken)
+       echo "behaved as expected ($broken expected $failures)."
+    fi;
+else
+    echo "$success/$total tests passed."
+    if [ "$broken" != "0" ]; then
+       tests=$(pluralize "test" $broken)
+       echo "$broken broken $tests failed as expected."
+    fi
+    if [ "$fixed" != "0" ]; then
+       tests=$(pluralize "test" $fixed)
+       echo "$fixed broken $tests now fixed."
+    fi
+    if [ "$failed" != "0" ]; then
+       tests=$(pluralize "test" $failed)
+       echo "$failed $tests failed."
+    fi
+fi
+
+skipped=$(($total - $fixed - $success - $failed - $broken))
+if [ "$skipped" != "0" ]; then
+    tests=$(pluralize "test" $skipped)
+    echo "$skipped $tests skipped."
+fi
diff --git a/test/author-order b/test/author-order
new file mode 100755 (executable)
index 0000000..9f0b931
--- /dev/null
@@ -0,0 +1,36 @@
+#!/bin/bash
+test_description="author reordering;"
+. ./test-lib.sh
+
+test_begin_subtest "Adding parent message"
+generate_message [body]=findme [id]=new-parent-id [subject]=author-reorder-threadtest '[from]="User <user@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Adding initial child message"
+generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User1 <user1@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Adding second child message"
+generate_message [body]=findme "[in-reply-to]=\<new-parent-id\>" [subject]=author-reorder-threadtest '[from]="User2 <user2@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching when all three messages match"
+output=$(notmuch search findme | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [3/3] User, User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching when two messages match"
+output=$(notmuch search User1 or User2 | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [2/3] User1, User2| User; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching when only one message matches"
+output=$(notmuch search User2 | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/3] User2| User, User1; author-reorder-threadtest (inbox unread)"
+
+test_begin_subtest "Searching when only first message matches"
+output=$(notmuch search User | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/3] User| User1, User2; author-reorder-threadtest (inbox unread)"
+
+test_done
diff --git a/test/basic b/test/basic
new file mode 100755 (executable)
index 0000000..725c753
--- /dev/null
@@ -0,0 +1,79 @@
+#!/bin/bash
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+
+test_description='the test framework itself.'
+
+################################################################
+# It appears that people try to run tests without building...
+
+if ! test -x ../notmuch
+then
+       echo >&2 'You do not seem to have built notmuch yet.'
+       exit 1
+fi
+
+. ./test-lib.sh
+
+################################################################
+# Test harness
+test_expect_success 'success is reported like this' '
+    :
+'
+test_set_prereq HAVEIT
+haveit=no
+test_expect_success HAVEIT 'test runs if prerequisite is satisfied' '
+    test_have_prereq HAVEIT &&
+    haveit=yes
+'
+
+clean=no
+test_expect_success 'tests clean up after themselves' '
+    test_when_finished clean=yes
+'
+
+cleaner=no
+test_expect_code 1 'tests clean up even after a failure' '
+    test_when_finished cleaner=yes &&
+    (exit 1)
+'
+
+if test $clean$cleaner != yesyes
+then
+       say "bug in test framework: cleanup commands do not work reliably"
+       exit 1
+fi
+
+test_expect_code 2 'failure to clean up causes the test to fail' '
+    test_when_finished "(exit 2)"
+'
+
+# Ensure that all tests are being run
+test_begin_subtest 'Ensure that all available tests will be run by notmuch-test'
+tests_in_suite=$(grep TESTS= ../notmuch-test | sed -e "s/TESTS=\"\(.*\)\"/\1/" | tr " " "\n" | sort)
+available=$(ls -1 ../ | grep -v -E "^(aggregate-results.sh|Makefile|Makefile.local|notmuch-test|README|test-lib.sh|test-results|tmp.*|valgrind|corpus*|emacs.expected-output|smtp-dummy|smtp-dummy.c)" | sort)
+test_expect_equal "$tests_in_suite" "$available"
+
+################################################################
+# Test mail store prepared in test-lib.sh
+
+test_expect_success \
+    'test that mail store was created' \
+    'test -d "${MAIL_DIR}"'
+
+
+find "${MAIL_DIR}" -type f -print >should-be-empty
+test_expect_success \
+    'mail store should be empty' \
+    'cmp -s /dev/null should-be-empty'
+
+test_expect_success \
+    'NOTMUCH_CONFIG is set and points to an existing file' \
+    'test -f "${NOTMUCH_CONFIG}"'
+
+test_expect_success \
+    'PATH is set to this repository' \
+    'test "`echo $PATH|cut -f1 -d: | sed -e 's,/test/valgrind/bin$,,'`" = "`dirname ${TEST_DIRECTORY}`"'
+
+test_done
diff --git a/test/corpus/01 b/test/corpus/01
new file mode 100644 (file)
index 0000000..7e9e349
--- /dev/null
@@ -0,0 +1,34 @@
+From: "Mikhail Gusarov" <dottedmag@dottedmag.net>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 21:28:37 +0600
+Subject: [notmuch] [PATCH 1/2] Close message file after parsing message
+       headers
+Message-ID: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+
+Keeping unused files open helps to see "Too many open files" often.
+
+Signed-off-by: Mikhail Gusarov <dottedmag at dottedmag.net>
+---
+ lib/message-file.c |    5 +++++
+ 1 files changed, 5 insertions(+), 0 deletions(-)
+
+diff --git a/lib/message-file.c b/lib/message-file.c
+index 8a3f8ee..197ab01 100644
+--- a/lib/message-file.c
++++ b/lib/message-file.c
+@@ -325,6 +325,11 @@ notmuch_message_file_get_header (notmuch_message_file_t *message,
+           return decoded_value;
+     }
++    if (message->parsing_finished) {
++        fclose (message->file);
++        message->file = NULL;
++    }
++
+     if (message->line)
+       free (message->line);
+     message->line = NULL;
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/02 b/test/corpus/02
new file mode 100644 (file)
index 0000000..dadcdaa
--- /dev/null
@@ -0,0 +1,32 @@
+From: "Mikhail Gusarov" <dottedmag@dottedmag.net>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 21:28:38 +0600
+Subject: [notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++
+       file with gcc 4.4
+In-Reply-To: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+References: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+Message-ID: <1258471718-6781-2-git-send-email-dottedmag@dottedmag.net>
+
+
+Signed-off-by: Mikhail Gusarov <dottedmag at dottedmag.net>
+---
+ lib/message.cc |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/lib/message.cc b/lib/message.cc
+index 72c350f..a4b090b 100644
+--- a/lib/message.cc
++++ b/lib/message.cc
+@@ -21,6 +21,8 @@
+ #include "notmuch-private.h"
+ #include "database-private.h"
++#include <stdint.h>
++
+ #include <gmime/gmime.h>
+ #include <xapian.h>
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/03 b/test/corpus/03
new file mode 100644 (file)
index 0000000..c154ac5
--- /dev/null
@@ -0,0 +1,93 @@
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+From: Lars Kellogg-Stedman <lars@seas.harvard.edu>
+To: notmuch@notmuchmail.org
+Message-ID: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+MIME-Version: 1.0
+User-Agent: Mutt/1.5.19 (2009-01-05)
+Subject: [notmuch] Working with Maildir storage?
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============1685355122=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+
+--===============1685355122==
+Content-Type: multipart/signed; micalg=pgp-sha256;
+       protocol="application/pgp-signature"; boundary="5Dr6Wqe9hdyl7LAI"
+Content-Disposition: inline
+
+
+--5Dr6Wqe9hdyl7LAI
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+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=
+=3D3026: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,
+
+--=20
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+
+--5Dr6Wqe9hdyl7LAI
+Content-Type: application/pgp-signature
+Content-Disposition: inline
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLAvLmAAoJENdGlQYxQazYRtcH/0usClQ1Z+EoTsA+URwIK6hD
+FsZUxFxRjMuOQRn2idZ/zhhg5jJj11ZaHjqxSkDvi2ywkTKUf1vX9LLzVy5hSR9M
+E6XQUd5QWAQXo1VsTeKkukIL0YqsPjdgrT8+Yt+OS2NvhEncql23oxnL2/pHkIFq
+r0NdTmVV5Jcar7w9J6X1Mi9m229a/9jV5FImsWISkIhIWznXU5SiU6zIw8xhP4E0
+xhvVSNJnFryjVHtva870aSQduhHfeLPzpYhqbkMPvlq+bcz6Q/Q2SwxJcGLNMPHa
+os9s9FGhCvFKUhVzezHWPgXNCcNT8qK89rcUldb5Oq4jaJb8RCZCYABplfoyaFs=
+=vO4s
+-----END PGP SIGNATURE-----
+
+--5Dr6Wqe9hdyl7LAI--
+
+--===============1685355122==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============1685355122==--
+
diff --git a/test/corpus/04 b/test/corpus/04
new file mode 100644 (file)
index 0000000..0ce678b
--- /dev/null
@@ -0,0 +1,84 @@
+From: Mikhail Gusarov <dottedmag@dottedmag.net>
+To: notmuch@notmuchmail.org
+References: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+Date: Wed, 18 Nov 2009 01:02:38 +0600
+In-Reply-To: <20091117190054.GU3165@dottiness.seas.harvard.edu> (Lars
+       Kellogg-Stedman's message of "Tue, 17 Nov 2009 14:00:54 -0500")
+Message-ID: <87iqd9rn3l.fsf@vertex.dottedmag>
+User-Agent: Gnus/5.13 (Gnus v5.13) Emacs/23.1 (gnu/linux)
+MIME-Version: 1.0
+Subject: Re: [notmuch] Working with Maildir storage?
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============1958295626=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+--===============1958295626==
+Content-Type: multipart/signed; boundary="=-=-=";
+       micalg=pgp-sha1; protocol="application/pgp-signature"
+
+--=-=-=
+Content-Transfer-Encoding: quoted-printable
+
+
+Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did g=
+yre 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.hostna=
+me,U=3D3026:2,S:
+ LK>   Too many open files
+
+See the patch just posted here.
+
+=2D-=20
+  http://fossarchy.blogspot.com/
+
+--=-=-=
+Content-Type: application/pgp-signature
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iQIcBAEBAgAGBQJLAvNOAAoJEJ0g9lA+M4iIjLYQAKp0PXEgl3JMOEBisH52AsIK
+CzzfP4Fzd41K9VH/c1EdQWDYR6FCAA4IUSNICnJhITsYUb0eC5AKJiey3JP0+rmd
+s4qEFBKH2iuphv8Llltcv2Q8DyPuJBkVa3mO9XCCeABZ6v4UvnTSWRVG12csSEih
+ScgienU8sMrM9LwvvVI1ZB2flm2TzsH2hWi30jIgmtBntIKJaTgbFXB50FYFwULa
+gGL/oH3u+YpumedWzPZdCJrw2q7nMvYx8aQ29EDCNLZibAZe+6oDTa6Fv6/0ldpQ
+U+DptR0nJGbJTWa26OTSvmyeIORjAfM+TEI68n7KO9VHYPmVh6awcf0MNKYh2xWk
+eRQNBcKyQNWxeKyCCpT/rrTlpxBWahpvg+V8lkDH2W09wjRp6CUKvifK3Sz3am9m
+5ZUMpvXbwkZD6Ci6l/QytbYK50e8UpvFSu5DBaxBz59ykoypuNg2ayO5Kdi6IF5d
+T+Sw6wo8UKn9a33+vheIc0fkhZXbeSotEmDm7huazm6CgM3dcWXUpTuJvik1cSWp
+4buv98gY6IKWKoUTXODWUr+7VR4gei8du8qOsKem+QDfNX7tmaIRjhrbB24B91Wy
+td3MTJD7GjMNid0INqRY1CRMLo8YlPaq6NBZfcYtYgwa6gpJijz1/MAn8+GMrfhF
+9LI8b9jopNP+pMYBohLA
+=/ksP
+-----END PGP SIGNATURE-----
+--=-=-=--
+
+--===============1958295626==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============1958295626==--
+
diff --git a/test/corpus/05 b/test/corpus/05
new file mode 100644 (file)
index 0000000..75b05fa
--- /dev/null
@@ -0,0 +1,104 @@
+MIME-Version: 1.0
+Date: Tue, 17 Nov 2009 11:36:14 -0800
+Message-ID: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
+From: Alex Botero-Lowry <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Content-Type: multipart/mixed; boundary=0016e687869333b1570478963d35
+Subject: [notmuch] preliminary FreeBSD support
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+--0016e687869333b1570478963d35
+Content-Type: multipart/alternative; boundary=0016e687869333b14e0478963d33
+
+--0016e687869333b14e0478963d33
+Content-Type: text/plain; charset=ISO-8859-1
+
+I saw the announcement this morning, and was very excited, as I had been
+hoping sup would be turned into a library,
+since I like the concept more than the UI (I'd rather an emacs interface).
+
+I did a preliminary compile which worked out fine, but
+sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on
+FreeBSD, so notmuch_config_open segfaulted.
+
+Attached is a patch that supplies a default buffer size of 64 in cases where
+-1 is returned.
+
+http://www.opengroup.org/austin/docs/austin_328.txt - seems to indicate this
+is acceptable behavior,
+and http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically
+uses 64 as the
+buffer size.
+
+--0016e687869333b14e0478963d33
+Content-Type: text/html; charset=ISO-8859-1
+Content-Transfer-Encoding: quoted-printable
+
+I saw the announcement this morning, and was very excited, as I had been ho=
+ping sup would be turned into a library,<br>since I like the concept more t=
+han the UI (I&#39;d rather an emacs interface).<br><br>I did a preliminary =
+compile which worked out fine, but sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns=
+ -1 on<br>
+FreeBSD, so notmuch_config_open segfaulted.<br><br>Attached is a patch that=
+ supplies a default buffer size of 64 in cases where -1 is returned.<br><br=
+><a href=3D"http://www.opengroup.org/austin/docs/austin_328.txt">http://www=
+.opengroup.org/austin/docs/austin_328.txt</a> - seems to indicate this is a=
+cceptable behavior,<br>
+and <a href=3D"http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg01680=
+8.html">http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.html<=
+/a> specifically uses 64 as the<br>buffer size.<br><br><br>
+
+--0016e687869333b14e0478963d33--
+--0016e687869333b1570478963d35
+Content-Type: application/octet-stream; 
+       name="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
+Content-Disposition: attachment; 
+       filename="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_g252e6gs0
+
+RnJvbSBlM2JjNGJiZDdiOWQwZDA4NjgxNmFiNWY4ZjJkNmZmZWExZGQzZWE0IE1vbiBTZXAgMTcg
+MDA6MDA6MDAgMjAwMQpGcm9tOiBBbGV4YW5kZXIgQm90ZXJvLUxvd3J5IDxhbGV4LmJvdGVyb2xv
+d3J5QGdtYWlsLmNvbT4KRGF0ZTogVHVlLCAxNyBOb3YgMjAwOSAxMTozMDozOSAtMDgwMApTdWJq
+ZWN0OiBbUEFUQ0hdIERlYWwgd2l0aCBzaXR1YXRpb24gd2hlcmUgc3lzY29uZihfU0NfR0VUUFdf
+Ul9TSVpFX01BWCkgcmV0dXJucyAtMQoKLS0tCiBub3RtdWNoLWNvbmZpZy5jIHwgICAgMiArKwog
+MSBmaWxlcyBjaGFuZ2VkLCAyIGluc2VydGlvbnMoKyksIDAgZGVsZXRpb25zKC0pCgpkaWZmIC0t
+Z2l0IGEvbm90bXVjaC1jb25maWcuYyBiL25vdG11Y2gtY29uZmlnLmMKaW5kZXggMjQ4MTQ5Yy4u
+ZTcyMjBkOCAxMDA2NDQKLS0tIGEvbm90bXVjaC1jb25maWcuYworKysgYi9ub3RtdWNoLWNvbmZp
+Zy5jCkBAIC03Nyw2ICs3Nyw3IEBAIHN0YXRpYyBjaGFyICoKIGdldF9uYW1lX2Zyb21fcGFzc3dk
+X2ZpbGUgKHZvaWQgKmN0eCkKIHsKICAgICBsb25nIHB3X2J1Zl9zaXplID0gc3lzY29uZihfU0Nf
+R0VUUFdfUl9TSVpFX01BWCk7CisgICAgaWYgKHB3X2J1Zl9zaXplID09IC0xKSBwd19idWZfc2l6
+ZSA9IDY0OwogICAgIGNoYXIgKnB3X2J1ZiA9IHRhbGxvY196ZXJvX3NpemUgKGN0eCwgcHdfYnVm
+X3NpemUpOwogICAgIHN0cnVjdCBwYXNzd2QgcGFzc3dkLCAqaWdub3JlZDsKICAgICBjaGFyICpu
+YW1lOwpAQCAtMTAxLDYgKzEwMiw3IEBAIHN0YXRpYyBjaGFyICoKIGdldF91c2VybmFtZV9mcm9t
+X3Bhc3N3ZF9maWxlICh2b2lkICpjdHgpCiB7CiAgICAgbG9uZyBwd19idWZfc2l6ZSA9IHN5c2Nv
+bmYoX1NDX0dFVFBXX1JfU0laRV9NQVgpOworICAgIGlmIChwd19idWZfc2l6ZSA9PSAtMSkgcHdf
+YnVmX3NpemUgPSA2NDsKICAgICBjaGFyICpwd19idWYgPSB0YWxsb2NfemVyb19zaXplIChjdHgs
+IHB3X2J1Zl9zaXplKTsKICAgICBzdHJ1Y3QgcGFzc3dkIHBhc3N3ZCwgKmlnbm9yZWQ7CiAgICAg
+Y2hhciAqbmFtZTsKLS0gCjEuNi41LjIKCg==
+--0016e687869333b1570478963d35
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--0016e687869333b1570478963d35--
+
diff --git a/test/corpus/06 b/test/corpus/06
new file mode 100644 (file)
index 0000000..3baad49
--- /dev/null
@@ -0,0 +1,36 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 12:19:24 -0800
+Subject: [notmuch] preliminary FreeBSD support
+In-Reply-To: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
+References: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
+Message-ID: <87lji4lx9v.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 11:36:14 -0800, Alex Botero-Lowry <alex.boterolowry at gmail.com> wrote:
+> I saw the announcement this morning, and was very excited, as I had been
+> hoping sup would be turned into a library,
+> since I like the concept more than the UI (I'd rather an emacs interface).
+
+Hi Alex,
+
+That's great! It's good to hear that there are like-minded people out
+there. I hope that Notmuch will be useful for you.
+
+> I did a preliminary compile which worked out fine, but
+> sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on
+> FreeBSD, so notmuch_config_open segfaulted.
+> 
+> Attached is a patch that supplies a default buffer size of 64 in cases where
+> -1 is returned.
+
+Thanks for the patch. As we discussed in IRC[*], we should probably
+do the correct thing and check for ERANGE and loop as necessary (even if
+sysconf returns a positive value). Example code here:
+
+http://www.opengroup.org/austin/docs/austin_328.txt
+
+-Carl
+
+[*] #notmuch on irc.freenode.net for those who didn't just guess that
+already, (and I'll add that to the website soon).
+
diff --git a/test/corpus/07 b/test/corpus/07
new file mode 100644 (file)
index 0000000..7b1e2bb
--- /dev/null
@@ -0,0 +1,57 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 09:13:27 -0800
+Subject: [notmuch] [PATCH 1/2] Close message file after parsing message
+ headers
+In-Reply-To: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+References: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+Message-ID: <87lji5cbwo.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 21:28:37 +0600, Mikhail Gusarov <dottedmag at dottedmag.net> wrote:
+> Keeping unused files open helps to see "Too many open files" often.
+> 
+> Signed-off-by: Mikhail Gusarov <dottedmag at dottedmag.net>
+...
+On Tue, 17 Nov 2009 21:28:38 +0600, Mikhail Gusarov <dottedmag at dottedmag.net> wrote:
+> 
+> Signed-off-by: Mikhail Gusarov <dottedmag at dottedmag.net>
+> ---
+>  lib/message.cc |    2 ++
+>  1 files changed, 2 insertions(+), 0 deletions(-)
+
+Hi Mikhail,
+
+Welcome to notmuch, and thanks for these patches! I've pushed both of
+them out now.
+
+Keith ran into the same problem of "too many open files" and wrote a
+more complex fix, (which included what you did here). His code can be
+seen at:
+
+       git://keithp.com/git/notmuch
+
+I didn't apply Keith's fix yet, because I think I'd rather just fix the
+indexer to store the In-Reply-To header in a separate term prefix from
+the term used for the References header[*]. That will then let us lookup
+the in-reply-to value later for thread constructions without having to
+open the original email file at all.
+
+-Carl
+
+[*] Yes, this is my first post to our new mailing list and I'm already
+spouting off about "terms" and "prefixes" without any definitions. I
+apologize for that. I hope that people will ask questions freely here on
+the list whenever anything is not clear, and I'll be glad to explain
+things as needed. (Then when can shove answers into a HACKING document.)
+
+PS. This reply is a great example of a feature that notmuch *almost*
+supports already---repling to multiple messages at once. The "notmuch
+reply" command line does everything necessary to make this work, but we
+haven't yet hooked up any keybindings for this in the emacs client yet.
+Obviously, 'r' from the search view could reply to the entire thread.
+But when viewing a thread, anyone have a good keybinding suggestion?
+(There's obviously 'R' as opposed to 'r', but I think we'll probably
+want to distinguish "reply to sender" from "reply to all" before trying
+to distinguish "reply to message" from "reply to thread" (which I
+imagine is more rare of an operation).
+
diff --git a/test/corpus/08 b/test/corpus/08
new file mode 100644 (file)
index 0000000..baf34d1
--- /dev/null
@@ -0,0 +1,87 @@
+Date: Tue, 17 Nov 2009 15:33:01 -0500
+From: Lars Kellogg-Stedman <lars@seas.harvard.edu>
+To: Mikhail Gusarov <dottedmag@dottedmag.net>
+Message-ID: <20091117203301.GV3165@dottiness.seas.harvard.edu>
+References: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+       <87iqd9rn3l.fsf@vertex.dottedmag>
+MIME-Version: 1.0
+In-Reply-To: <87iqd9rn3l.fsf@vertex.dottedmag>
+User-Agent: Mutt/1.5.19 (2009-01-05)
+Cc: notmuch@notmuchmail.org
+Subject: Re: [notmuch] Working with Maildir storage?
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============0063752545=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+
+--===============0063752545==
+Content-Type: multipart/signed; micalg=pgp-sha256;
+       protocol="application/pgp-signature"; boundary="GGxZz/e2pmGePzrA"
+Content-Disposition: inline
+
+
+--GGxZz/e2pmGePzrA
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+> 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
+
+--=20
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+
+--GGxZz/e2pmGePzrA
+Content-Type: application/pgp-signature
+Content-Disposition: inline
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLAwh9AAoJENdGlQYxQazYHJMIAI+XTPOyBTZIxEGTdgVKd2fR
+k27ucKs6lXozfMIIGchNUDXQho+KmiuTfX1XFJeBkqOlhrd9zlGjBGoBM0YBq/Gs
+aStPdonREzsHORjmyQCCpjg4AcqCRTXFbDXzAeXlxMPOrZ3P0XNPzTEM1mVksbmg
+mBBDLdHncy4sSCfFgXwRGGgLv9z5Acqm8xGYr68c9PIXY939ozIKV9LVUhxiNz9g
+We2a9rLDhfwxUqDlGdiNwZZimiKvD/fsYSrBZMDb5HgIYkeNZ4SD8Xu+OgB550wN
+OFfwGi3o8WFK2AyDe5QJDh9Ub+euPNlVzePoGpkltZEHuCcLFJqCHv5XYpbxcjA=
+=GO2Q
+-----END PGP SIGNATURE-----
+
+--GGxZz/e2pmGePzrA--
+
+--===============0063752545==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============0063752545==--
+
diff --git a/test/corpus/09 b/test/corpus/09
new file mode 100644 (file)
index 0000000..26b51b1
--- /dev/null
@@ -0,0 +1,33 @@
+From: "Mikhail Gusarov" <dottedmag@dottedmag.net>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:50:48 +0600
+Subject: [notmuch] Working with Maildir storage?
+In-Reply-To: <20091117203301.GV3165@dottiness.seas.harvard.edu> (Lars
+       Kellogg-Stedman's message of "Tue, 17 Nov 2009 15:33:01 -0500")
+References: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+       <87iqd9rn3l.fsf@vertex.dottedmag>
+       <20091117203301.GV3165@dottiness.seas.harvard.edu>
+Message-ID: <87fx8can9z.fsf@vertex.dottedmag>
+
+
+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
+
+-- 
+  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>
+
diff --git a/test/corpus/10 b/test/corpus/10
new file mode 100644 (file)
index 0000000..4211d73
--- /dev/null
@@ -0,0 +1,54 @@
+From: "Mikhail Gusarov" <dottedmag@dottedmag.net>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:51:18 +0600
+Subject: [notmuch] [PATCH] Handle rename of message file
+Message-ID: <1258491078-29658-1-git-send-email-dottedmag@dottedmag.net>
+
+If message file has been renamed, just update filename in the DB.
+
+Signed-off-by: Mikhail Gusarov <dottedmag at dottedmag.net>
+---
+ lib/database.cc |   21 ++++++++++++---------
+ 1 files changed, 12 insertions(+), 9 deletions(-)
+
+diff --git a/lib/database.cc b/lib/database.cc
+index 3c8d626..c4eb8b6 100644
+--- a/lib/database.cc
++++ b/lib/database.cc
+@@ -925,20 +925,23 @@ notmuch_database_add_message (notmuch_database_t *notmuch,
+       if (private_status == NOTMUCH_PRIVATE_STATUS_NO_DOCUMENT_FOUND) {
+           _notmuch_message_set_filename (message, filename);
+           _notmuch_message_add_term (message, "type", "mail");
++
++          ret = _notmuch_database_link_message (notmuch, message, message_file);
++          if (ret)
++              goto DONE;
++
++          date = notmuch_message_file_get_header (message_file, "date");
++          _notmuch_message_set_date (message, date);
++
++          _notmuch_message_index_file (message, filename);
++      } else if (strcmp(notmuch_message_get_filename(message), filename)) {
++          /* Message file has been moved/renamed */
++          _notmuch_message_set_filename (message, filename);
+       } else {
+           ret = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
+           goto DONE;
+       }
+-      ret = _notmuch_database_link_message (notmuch, message, message_file);
+-      if (ret)
+-          goto DONE;
+-
+-      date = notmuch_message_file_get_header (message_file, "date");
+-      _notmuch_message_set_date (message, date);
+-
+-      _notmuch_message_index_file (message, filename);
+-
+       _notmuch_message_sync (message);
+     } catch (const Xapian::Error &error) {
+       fprintf (stderr, "A Xapian exception occurred: %s.\n",
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/11 b/test/corpus/11
new file mode 100644 (file)
index 0000000..c0701de
--- /dev/null
@@ -0,0 +1,27 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 13:15:25 -0800
+Subject: [notmuch] [PATCH 1/2] Close message file after parsing message
+ headers
+In-Reply-To: <87lji5cbwo.fsf@yoom.home.cworth.org>
+References: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+       <87lji5cbwo.fsf@yoom.home.cworth.org>
+Message-ID: <yunbpj0etua.fsf@aiko.keithp.com>
+
+On Tue, 17 Nov 2009 09:13:27 -0800, Carl Worth <cworth at cworth.org> wrote:
+
+> I didn't apply Keith's fix yet, because I think I'd rather just fix the
+> indexer to store the In-Reply-To header in a separate term prefix from
+> the term used for the References header[*]. That will then let us lookup
+> the in-reply-to value later for thread constructions without having to
+> open the original email file at all.
+
+Threading the message also involves displaying the from and to contents,
+which requires opening the message file. The alternative to the fix I
+provided is to just parse all of the message headers when first opening
+the message; it could then be immediately closed and the hash referred
+to for all header data. Given the choice, just having the caller say
+when it has finished with a message is probably a reasonable option...
+
+-keith
+
diff --git a/test/corpus/12 b/test/corpus/12
new file mode 100644 (file)
index 0000000..fbc604c
--- /dev/null
@@ -0,0 +1,27 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 13:24:13 -0800
+Subject: [notmuch] Working with Maildir storage?
+In-Reply-To: <20091117203301.GV3165@dottiness.seas.harvard.edu>
+References: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+       <87iqd9rn3l.fsf@vertex.dottedmag>
+       <20091117203301.GV3165@dottiness.seas.harvard.edu>
+Message-ID: <yunaayketfm.fsf@aiko.keithp.com>
+
+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
+
diff --git a/test/corpus/13 b/test/corpus/13
new file mode 100644 (file)
index 0000000..03cb374
--- /dev/null
@@ -0,0 +1,178 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 13:32:45 -0800
+Subject: [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove
+       inbox (and unread) tags
+Message-ID: <1258493565-13508-1-git-send-email-keithp@keithp.com>
+
+When closing a thread view, mark the thread as archived by removing
+the "inbox" tag, and for the 'x' variant, the "unread" tag as well,
+then kill the buffer and update the search window view as well.
+
+This makes 'x' much the same as 'a', but instead of taking you to the
+next message, it takes you back to the search window instead.
+
+Signed-off-by: Keith Packard <keithp at keithp.com>
+---
+ notmuch.el |   86 ++++++++++++++++++++++++++++++++++++++++++++++-------------
+ 1 files changed, 67 insertions(+), 19 deletions(-)
+
+diff --git a/notmuch.el b/notmuch.el
+index 638d49d..7b0d72c 100644
+--- a/notmuch.el
++++ b/notmuch.el
+@@ -31,8 +31,8 @@
+     ; Will be much preferable to switch to direct manipulation for
+     ; toggling visibility of these components. Probably using
+     ; overlays-at to query and manipulate the current overlay.
+-    (define-key map "a" 'notmuch-show-archive-thread)
+-    (define-key map "A" 'notmuch-show-mark-read-then-archive-thread)
++    (define-key map "a" 'notmuch-show-mark-read-archive-thread-next-thread)
++    (define-key map "A" 'notmuch-show-archive-thread-next-thread)
+     (define-key map "b" 'notmuch-show-toggle-body-read-visible)
+     (define-key map "c" 'notmuch-show-toggle-citations-visible)
+     (define-key map "h" 'notmuch-show-toggle-headers-visible)
+@@ -47,7 +47,8 @@
+     (define-key map "s" 'notmuch-show-toggle-signatures-visible)
+     (define-key map "v" 'notmuch-show-view-all-mime-parts)
+     (define-key map "w" 'notmuch-show-view-raw-message)
+-    (define-key map "x" 'kill-this-buffer)
++    (define-key map "x" 'notmuch-show-mark-read-archive-thread-kill-buffer)
++    (define-key map "X" 'notmuch-show-archive-thread-kill-buffer)
+     (define-key map "+" 'notmuch-show-add-tag)
+     (define-key map "-" 'notmuch-show-remove-tag)
+     (define-key map (kbd "DEL") 'notmuch-show-rewind)
+@@ -183,7 +184,33 @@ Unlike builtin `next-line' this version accepts no arguments."
+                        (cons (notmuch-show-get-message-id) nil)))
+         (notmuch-show-set-tags (sort (set-difference tags toremove :test 'string=) 'string<))))))
+-(defun notmuch-show-archive-thread-maybe-mark-read (markread)
++(defun notmuch-show-next-thread (markread)
++  (let ((parent-buffer notmuch-show-parent-buffer))
++    (kill-this-buffer)
++    (if parent-buffer
++      (progn
++        (switch-to-buffer parent-buffer)
++        (forward-line)
++        (notmuch-search-show-thread)))))
++  
++(defun notmuch-delete-tags (to-remove from)
++  (if to-remove
++      (delete (car to-remove) (notmuch-delete-tags (cdr to-remove) from))
++    from))
++
++(defun notmuch-kill-message-buffer (markread)
++  (let ((parent-buffer notmuch-show-parent-buffer))
++    (kill-this-buffer)
++    (if parent-buffer
++      (progn
++        (switch-to-buffer parent-buffer)
++        (let ((tags (notmuch-search-get-tags)))
++          (setq tags (delete "inbox" tags))
++          (if markread (setq tags (delete "unread" tags)))
++          (notmuch-search-set-tags tags))
++        (forward-line)))))
++
++(defun notmuch-show-archive-thread-maybe-mark-read (markread shownext)
+   (save-excursion
+     (goto-char (point-min))
+     (while (not (eobp))
+@@ -194,15 +221,9 @@ Unlike builtin `next-line' this version accepts no arguments."
+         (forward-char))
+       (if (not (re-search-forward notmuch-show-message-begin-regexp nil t))
+         (goto-char (point-max)))))
+-  (let ((parent-buffer notmuch-show-parent-buffer))
+-    (kill-this-buffer)
+-    (if parent-buffer
+-      (progn
+-        (switch-to-buffer parent-buffer)
+-        (forward-line)
+-        (notmuch-search-show-thread)))))
++  (if shownext (notmuch-show-next-thread markread) (notmuch-kill-message-buffer markread)))
+-(defun notmuch-show-mark-read-then-archive-thread ()
++(defun notmuch-show-mark-read-archive-thread-next-thread ()
+   "Remove \"unread\" tag from each message, then archive and show next thread.
+ Archive each message currrently shown by removing the \"unread\"
+@@ -215,9 +236,22 @@ being delivered to the same thread. It does not archive the
+ entire thread, but only the messages shown in the current
+ buffer."
+   (interactive)
+-  (notmuch-show-archive-thread-maybe-mark-read t))
++  (notmuch-show-archive-thread-maybe-mark-read t t))
++
++(defun notmuch-show-mark-read-archive-thread-kill-buffer ()
++  "Remove \"unread\" tag from each message, then archive and kill the buffer.
++
++Archive each message currrently shown by removing the \"unread\"
++and \"inbox\" tag from each. Then kill this buffer.
++
++Note: This command is safe from any race condition of new messages
++being delivered to the same thread. It does not archive the
++entire thread, but only the messages shown in the current
++buffer."
++  (interactive)
++  (notmuch-show-archive-thread-maybe-mark-read t nil))
+-(defun notmuch-show-archive-thread ()
++(defun notmuch-show-archive-thread-next-thread ()
+   "Archive each message in thread, and show next thread from search.
+ Archive each message currrently shown by removing the \"inbox\"
+@@ -229,7 +263,20 @@ being delivered to the same thread. It does not archive the
+ entire thread, but only the messages shown in the current
+ buffer."
+   (interactive)
+-  (notmuch-show-archive-thread-maybe-mark-read nil))
++  (notmuch-show-archive-thread-maybe-mark-read nil t))
++
++(defun notmuch-show-archive-thread-kill-buffer ()
++  "Archive each message in thread, and kill the thread buffer.
++
++Archive each message currrently shown by removing the \"inbox\"
++tag from each. Then kill this buffer.
++
++Note: This command is safe from any race condition of new messages
++being delivered to the same thread. It does not archive the
++entire thread, but only the messages shown in the current
++buffer."
++  (interactive)
++  (notmuch-show-archive-thread-maybe-mark-read nil t))
+ (defun notmuch-show-view-raw-message ()
+   "View the raw email of the current message."
+@@ -297,7 +344,7 @@ by searching backward)."
+       (not (re-search-forward notmuch-show-message-begin-regexp nil t)))))
+ (defun notmuch-show-message-unread-p ()
+-  "Preficate testing whether current message is unread."
++  "Predicate testing whether current message is unread."
+   (member "unread" (notmuch-show-get-tags)))
+ (defun notmuch-show-next-message ()
+@@ -434,7 +481,7 @@ which this thread was originally shown."
+       (let ((last (notmuch-show-last-message-p)))
+       (notmuch-show-mark-read-then-next-open-message)
+       (if last
+-          (notmuch-show-archive-thread))))))
++          (notmuch-show-archive-thread-next-thread))))))
+ (defun notmuch-show-markup-citations-region (beg end depth)
+   (goto-char beg)
+@@ -618,8 +665,9 @@ messages. Each time you navigate away from a message with
+ You can add or remove tags from the current message with '+' and
+ '-'.  You can also archive all messages in the current
+-view, (remove the \"inbox\" tag from each), with
+-`notmuch-show-archive-thread' (bound to 'a' by default).
++view, (remove the \"inbox\" tag from each), with either
++`notmuch-show-archive-thread-next-thread' (bound to 'a' by default) or
++`notmuch-show-archive-thread-kill-buffer' (bound to 'x' by default).
+ \\{notmuch-show-mode-map}"
+   (interactive)
+-- 
+1.6.5.2
+
+
diff --git a/test/corpus/14 b/test/corpus/14
new file mode 100644 (file)
index 0000000..d3fe78d
--- /dev/null
@@ -0,0 +1,39 @@
+From: "Jan Janak" <jan@ryngle.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 23:18:47 +0100
+Subject: [notmuch] [PATCH] Older versions of install do not support -C.
+Message-ID: <1258496327-12086-1-git-send-email-jan@ryngle.com>
+
+Do not use -C cmdline option of install, older versions, commonly found in
+distributions like Debian, do not seem to support it. Running make install
+on such systems (tested on Debian Lenny) fails.
+
+Signed-off-by: Jan Janak <jan at ryngle.com>
+---
+ Makefile.local |    8 ++++----
+ 1 files changed, 4 insertions(+), 4 deletions(-)
+
+diff --git a/Makefile.local b/Makefile.local
+index f824bed..f51f1d1 100644
+--- a/Makefile.local
++++ b/Makefile.local
+@@ -27,11 +27,11 @@ install: all notmuch.1.gz
+       for d in $(DESTDIR)$(prefix)/bin/ $(DESTDIR)$(prefix)/share/man/man1 \
+               $(DESTDIR)/etc/bash_completion.d/ ; \
+       do \
+-              install -C -d $$d ; \
++              install -d $$d ; \
+       done ;
+-      install -C notmuch $(DESTDIR)$(prefix)/bin/
+-      install -C -m0644 notmuch.1.gz $(DESTDIR)$(prefix)/share/man/man1/
+-      install -C notmuch-completion.bash \
++      install notmuch $(DESTDIR)$(prefix)/bin/
++      install -m0644 notmuch.1.gz $(DESTDIR)$(prefix)/share/man/man1/
++      install notmuch-completion.bash \
+               $(DESTDIR)/etc/bash_completion.d/notmuch
+ SRCS  := $(SRCS) $(notmuch_client_srcs)
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/15 b/test/corpus/15
new file mode 100644 (file)
index 0000000..6824d5e
--- /dev/null
@@ -0,0 +1,22 @@
+From: "Jan Janak" <jan@ryngle.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 23:35:30 +0100
+Subject: [notmuch] What a great idea!
+Message-ID: <f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com>
+
+Hello,
+
+First of all, notmuch is a wonderful idea, both the cmdline tool and
+the emacs interface! Thanks a lot for writing it, I was really excited
+when I read the announcement today.
+
+Have you considered sending an announcement to the org-mode mailing list?
+http://org-mode.org
+
+Various ways of searching/referencing emails from emacs were discussed
+there several times and none of them were as elegant as notmuch (not
+even close). Maybe notmuch would attract some of the developers
+there..
+
+   -- Jan
+
diff --git a/test/corpus/16 b/test/corpus/16
new file mode 100644 (file)
index 0000000..f531eb9
--- /dev/null
@@ -0,0 +1,27 @@
+From: "Jan Janak" <jan@ryngle.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 23:38:47 +0100
+Subject: [notmuch] What a great idea!
+In-Reply-To: <f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com>
+References: <f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com>
+Message-ID: <f35dbb950911171438k5df6eb56k77b6c0944e2e79ae@mail.gmail.com>
+
+On Tue, Nov 17, 2009 at 11:35 PM, Jan Janak <jan at ryngle.com> wrote:
+> Hello,
+>
+> First of all, notmuch is a wonderful idea, both the cmdline tool and
+> the emacs interface! Thanks a lot for writing it, I was really excited
+> when I read the announcement today.
+>
+> Have you considered sending an announcement to the org-mode mailing list?
+> http://org-mode.org
+
+Sorry, wrong URL, the correct one is: http://orgmode.org
+
+> Various ways of searching/referencing emails from emacs were discussed
+> there several times and none of them were as elegant as notmuch (not
+> even close). Maybe notmuch would attract some of the developers
+> there..
+
+  -- Jan
+
diff --git a/test/corpus/17 b/test/corpus/17
new file mode 100644 (file)
index 0000000..d3b7568
--- /dev/null
@@ -0,0 +1,23 @@
+From: "Israel Herraiz" <isra@herraiz.org>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 23:57:18 +0100
+Subject: [notmuch] New to the list
+Message-ID: <1258498485-sup-142@elly>
+
+Hi all,
+
+I have subscribed to the list. As suggested by the welcome message, I
+am introducing myself. My name is Israel Herraiz, and I have done a
+couple of contributions to Sup, the probably well-known here e-mail
+client.
+
+"Not much" sounds interesting, and I wonder whether it could be
+integrated with the views of Sup (inbox, threads, etc). So I have
+subscribed to the list to keep an eye on what's going on here.
+
+I have just heard of "Not much". I have not even tried to download the
+code yet.
+
+Cheers,
+Israel
+
diff --git a/test/corpus/18 b/test/corpus/18
new file mode 100644 (file)
index 0000000..f522f69
--- /dev/null
@@ -0,0 +1,12 @@
+From: "Aron Griffis" <agriffis@n01se.net>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 18:21:38 -0500
+Subject: [notmuch] archive
+Message-ID: <20091117232137.GA7669@griffis1.net>
+
+Just subscribed, I'd like to catch up on the previous postings,
+but the archive link seems to be bogus?
+
+Thanks,
+Aron
+
diff --git a/test/corpus/19 b/test/corpus/19
new file mode 100644 (file)
index 0000000..1b7872b
--- /dev/null
@@ -0,0 +1,360 @@
+From: "Ingmar Vanhassel" <ingmar@exherbo.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 00:23:42 +0100
+Subject: [notmuch] [PATCH] Typsos
+Message-ID: <1258500222-32066-1-git-send-email-ingmar@exherbo.org>
+
+---
+ Makefile                |    4 ++--
+ README                  |    6 +++---
+ gmime-filter-reply.h    |    2 +-
+ lib/database.cc         |    2 +-
+ lib/index.cc            |    2 +-
+ lib/message.cc          |    2 +-
+ lib/messages.c          |    2 +-
+ lib/notmuch-private.h   |    2 +-
+ lib/notmuch.h           |   10 +++++-----
+ lib/sha1.c              |    2 +-
+ lib/thread.cc           |    2 +-
+ notmuch-completion.bash |    2 +-
+ notmuch-new.c           |    4 ++--
+ notmuch-search.c        |    2 +-
+ notmuch.1               |    4 ++--
+ notmuch.el              |   10 +++++-----
+ show-message.c          |    2 +-
+ 17 files changed, 30 insertions(+), 30 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 436dacf..96aaa73 100644
+--- a/Makefile
++++ b/Makefile
+@@ -1,4 +1,4 @@
+-# Default FLAGS, (can be overriden by user such as "make CFLAGS=-O2")
++# Default FLAGS, (can be overridden by user such as "make CFLAGS=-O2")
+ WARN_FLAGS=-Wall -Wextra -Wmissing-declarations -Wwrite-strings -Wswitch-enum
+ CFLAGS=-O2
+@@ -14,7 +14,7 @@ override CXXFLAGS += $(WARN_FLAGS) $(extra_cflags) $(extra_cxxflags)
+ override LDFLAGS += `pkg-config --libs glib-2.0 gmime-2.4 talloc` \
+                       `xapian-config --libs`
+-# Include our local Makfile.local first so that its first target is default
++# Include our local Makefile.local first so that its first target is default
+ include Makefile.local
+ include lib/Makefile.local
+diff --git a/README b/README
+index 40f05ab..27af77f 100644
+--- a/README
++++ b/README
+@@ -3,7 +3,7 @@ Notmuch - thread-based email index, search and tagging.
+ Notmuch is a system for indexing, searching, reading, and tagging
+ large collections of email messages. It uses the Xapian library to
+ provide fast, full-text search of very large collection of email with
+-a very convenient search syntas.
++a very convenient search syntax.
+ Notmuch is free software, released under the GNU General Public
+ License version 3 (or later).
+@@ -45,7 +45,7 @@ obtaining a more sophisticated interface:
+       notmuch.el file in this distribution.
+       If someone were to write a curses-based interface, or similar,
+-      it might also be reasonable to buil on the "notmuch"
++      it might also be reasonable to build on the "notmuch"
+       command-line interface.
+      2. Build on top of the notmuch library interface.
+@@ -67,4 +67,4 @@ still in development. We would appreciate any contributions to these
+ efforts.
+-      
+\ No newline at end of file
++      
+diff --git a/gmime-filter-reply.h b/gmime-filter-reply.h
+index 41cbc13..b7cbc6b 100644
+--- a/gmime-filter-reply.h
++++ b/gmime-filter-reply.h
+@@ -40,7 +40,7 @@ typedef struct _GMimeFilterReplyClass GMimeFilterReplyClass;
+  * @saw_nl: previous char was a \n
+  * @saw_angle: previous char was a >
+  *
+- * A filter to insert/remove reply markers (lines begining with >)
++ * A filter to insert/remove reply markers (lines beginning with >)
+  **/
+ struct _GMimeFilterReply {
+       GMimeFilter parent_object;
+diff --git a/lib/database.cc b/lib/database.cc
+index 3c8d626..27597cf 100644
+--- a/lib/database.cc
++++ b/lib/database.cc
+@@ -180,7 +180,7 @@ notmuch_status_to_string (notmuch_status_t status)
+     case NOTMUCH_STATUS_TAG_TOO_LONG:
+       return "Tag value is too long (exceeds NOTMUCH_TAG_MAX)";
+     case NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW:
+-      return "Unblanced number of calls to notmuch_message_freeze/thaw";
++      return "Unbalanced number of calls to notmuch_message_freeze/thaw";
+     default:
+     case NOTMUCH_STATUS_LAST_STATUS:
+       return "Unknown error status value";
+diff --git a/lib/index.cc b/lib/index.cc
+index 65b83b3..80df64b 100644
+--- a/lib/index.cc
++++ b/lib/index.cc
+@@ -198,7 +198,7 @@ _index_mime_part (notmuch_message_t *message,
+               if (i == 1)
+                   continue;
+               if (i > 1)
+-                  fprintf (stderr, "Warning: Unexpected extra parts of mutlipart/signed. Indexing anyway.\n");
++                  fprintf (stderr, "Warning: Unexpected extra parts of multipart/signed. Indexing anyway.\n");
+           }
+           _index_mime_part (message,
+                             g_mime_multipart_get_part (multipart, i));
+diff --git a/lib/message.cc b/lib/message.cc
+index a4b090b..1d6623f 100644
+--- a/lib/message.cc
++++ b/lib/message.cc
+@@ -144,7 +144,7 @@ _notmuch_message_create (const void *talloc_owner,
+ }
+ /* Create a new notmuch_message_t object for a specific message ID,
+- * (which may or may not already exist in the databas).
++ * (which may or may not already exist in the database).
+  *
+  * Here, 'talloc owner' is an optional talloc context to which the new
+  * message will belong. This allows for the caller to not bother
+diff --git a/lib/messages.c b/lib/messages.c
+index a588f8f..2f7c283 100644
+--- a/lib/messages.c
++++ b/lib/messages.c
+@@ -47,7 +47,7 @@ _notmuch_message_list_create (const void *ctx)
+     return list;
+ }
+-/* Append 'node' (which can of course point to an aribtrarily long
++/* Append 'node' (which can of course point to an arbitrarily long
+  * list of nodes) to the end of 'list'.
+  */
+ void
+diff --git a/lib/notmuch-private.h b/lib/notmuch-private.h
+index 6036ce4..af82e58 100644
+--- a/lib/notmuch-private.h
++++ b/lib/notmuch-private.h
+@@ -235,7 +235,7 @@ notmuch_message_file_open (const char *filename);
+ notmuch_message_file_t *
+ _notmuch_message_file_open_ctx (void *ctx, const char *filename);
+-/* Close a notmuch message preivously opened with notmuch_message_open. */
++/* Close a notmuch message previously opened with notmuch_message_open. */
+ void
+ notmuch_message_file_close (notmuch_message_file_t *message);
+diff --git a/lib/notmuch.h b/lib/notmuch.h
+index 32b5332..384c177 100644
+--- a/lib/notmuch.h
++++ b/lib/notmuch.h
+@@ -222,7 +222,7 @@ notmuch_database_get_timestamp (notmuch_database_t *database,
+ /* Add a new message to the given notmuch database.
+  *
+- * Here,'filename' should be a path relative to the the path of
++ * Here,'filename' should be a path relative to the path of
+  * 'database' (see notmuch_database_get_path), or else should be an
+  * absolute filename with initial components that match the path of
+  * 'database'.
+@@ -258,7 +258,7 @@ notmuch_database_add_message (notmuch_database_t *database,
+                             const char *filename,
+                             notmuch_message_t **message);
+-/* Find a message with the given messsage_id.
++/* Find a message with the given message_id.
+  *
+  * If the database contains a message with the given message_id, then
+  * a new notmuch_message_t object is returned. The caller should call
+@@ -620,7 +620,7 @@ notmuch_messages_advance (notmuch_messages_t *messages);
+ /* Destroy a notmuch_messages_t object.
+  *
+  * It's not strictly necessary to call this function. All memory from
+- * the notmuch_messages_t object will be reclaimed when the containg
++ * the notmuch_messages_t object will be reclaimed when the containing
+  * query object is destroyed.
+  */
+ void
+@@ -865,7 +865,7 @@ notmuch_tags_has_more (notmuch_tags_t *tags);
+ /* Get the current tag from 'tags' as a string.
+  *
+  * Note: The returned string belongs to 'tags' and has a lifetime
+- * identical to it (and the query to which it utlimately belongs).
++ * identical to it (and the query to which it ultimately belongs).
+  *
+  * See the documentation of notmuch_message_get_tags for example code
+  * showing how to iterate over a notmuch_tags_t object.
+@@ -884,7 +884,7 @@ notmuch_tags_advance (notmuch_tags_t *tags);
+ /* Destroy a notmuch_tags_t object.
+  *
+  * It's not strictly necessary to call this function. All memory from
+- * the notmuch_tags_t object will be reclaimed when the containg
++ * the notmuch_tags_t object will be reclaimed when the containing
+  * message or query objects are destroyed.
+  */
+ void
+diff --git a/lib/sha1.c b/lib/sha1.c
+index ff4dd16..cc48108 100644
+--- a/lib/sha1.c
++++ b/lib/sha1.c
+@@ -43,7 +43,7 @@ _hex_of_sha1_digest (const unsigned char digest[SHA1_DIGEST_SIZE])
+     return result;
+ }
+-/* Create a hexadcimal string version of the SHA-1 digest of 'str'
++/* Create a hexadecimal string version of the SHA-1 digest of 'str'
+  * (including its null terminating character).
+  *
+  * This function returns a newly allocated string which the caller
+diff --git a/lib/thread.cc b/lib/thread.cc
+index 4411d64..da58edc 100644
+--- a/lib/thread.cc
++++ b/lib/thread.cc
+@@ -190,7 +190,7 @@ _resolve_thread_relationships (unused (notmuch_thread_t *thread))
+  * subject line, the total count of messages, and all authors). The
+  * second search is for all messages that are in the thread and that
+  * also match the given query_string. This is to allow for a separate
+- * count of matched messages, and to allow a viewer to diplay these
++ * count of matched messages, and to allow a viewer to display these
+  * messages differently.
+  *
+  * Here, 'ctx' is talloc context for the resulting thread object.
+diff --git a/notmuch-completion.bash b/notmuch-completion.bash
+index ad55f6d..cdad05d 100644
+--- a/notmuch-completion.bash
++++ b/notmuch-completion.bash
+@@ -1,4 +1,4 @@
+-# Bash completion for notmutch
++# Bash completion for notmuch
+ #
+ # Copyright ?? 2009 Carl Worth
+ #
+diff --git a/notmuch-new.c b/notmuch-new.c
+index 83a05ba..5405a9f 100644
+--- a/notmuch-new.c
++++ b/notmuch-new.c
+@@ -303,7 +303,7 @@ add_files (notmuch_database_t *notmuch,
+ /* XXX: This should be merged with the add_files function since it
+  * shares a lot of logic with it. */
+-/* Recursively count all regular files in path and all sub-direcotries
++/* Recursively count all regular files in path and all sub-directories
+  * of path.  The result is added to *count (which should be
+  * initialized to zero by the top-level caller before calling
+  * count_files). */
+@@ -469,7 +469,7 @@ notmuch_new_command (void *ctx,
+     if (elapsed > 1 && ! add_files_state.saw_read_only_directory) {
+       printf ("\nTip: If you have any sub-directories that are archives (that is,\n"
+-              "they will never receive new mail), marking these directores as\n"
++              "they will never receive new mail), marking these directories as\n"
+               "read-only (chmod u-w /path/to/dir) will make \"notmuch new\"\n"
+               "much more efficient (it won't even look in those directories).\n");
+     }
+diff --git a/notmuch-search.c b/notmuch-search.c
+index 8db09c7..ac81372 100644
+--- a/notmuch-search.c
++++ b/notmuch-search.c
+@@ -76,7 +76,7 @@ notmuch_search_command (void *ctx, int argc, char *argv[])
+     query_str = query_string_from_args (ctx, argc, argv);
+     if (query_str == NULL) {
+-      fprintf (stderr, "Out of moemory.\n");
++      fprintf (stderr, "Out of memory.\n");
+       return 1;
+     }
+diff --git a/notmuch.1 b/notmuch.1
+index 6c3d10f..86d5f59 100644
+--- a/notmuch.1
++++ b/notmuch.1
+@@ -60,7 +60,7 @@ archives, and will then proceed to build a database that indexes the
+ mail to allow for fast search of the archive.
+ This directory can contain any number of sub-directories and should
+-primarily contain only files with indvidual email messages
++primarily contain only files with individual email messages
+ (eg. maildir or mh archives are perfect). If there are other,
+ non-email files (such as indexes maintained by other email programs)
+ then notmuch will do its best to detect those and ignore them.
+@@ -173,7 +173,7 @@ Constructs a reply template for a set of messages.
+ See the documentation of
+ .B search
+-for deatils of the supported syntax of search terms.
++for details of the supported syntax of search terms.
+ To make replying to email easier,
+ .B notmuch reply
+diff --git a/notmuch.el b/notmuch.el
+index 8894a8e..7e01ed6 100644
+--- a/notmuch.el
++++ b/notmuch.el
+@@ -205,7 +205,7 @@ Unlike builtin `next-line' this version accepts no arguments."
+ (defun notmuch-show-mark-read-then-archive-thread ()
+   "Remove \"unread\" tag from each message, then archive and show next thread.
+-Archive each message currrently shown by removing the \"unread\"
++Archive each message currently shown by removing the \"unread\"
+ and \"inbox\" tag from each. Then kill this buffer and show the
+ next thread from the search from which this thread was originally
+ shown.
+@@ -220,7 +220,7 @@ buffer."
+ (defun notmuch-show-archive-thread ()
+   "Archive each message in thread, and show next thread from search.
+-Archive each message currrently shown by removing the \"inbox\"
++Archive each message currently shown by removing the \"inbox\"
+ tag from each. Then kill this buffer and show the next thread
+ from the search from which this thread was originally shown.
+@@ -340,7 +340,7 @@ there are no more unread messages past the current point."
+       (notmuch-show-next-message)))
+ (defun notmuch-show-next-open-message ()
+-  "Advance to the the next message which is not hidden.
++  "Advance to the next message which is not hidden.
+ If read messages are currently hidden, advance to the next unread
+ message. Otherwise, advance to the next message."
+@@ -674,7 +674,7 @@ thread from that buffer can be show when done with this one)."
+       )))
+ (defvar notmuch-search-authors-width 40
+-  "Number of columns to use to diplay authors in a notmuch-search buffer.")
++  "Number of columns to use to display authors in a notmuch-search buffer.")
+ (defvar notmuch-search-mode-map
+   (let ((map (make-sparse-keymap)))
+@@ -910,7 +910,7 @@ the beginning of the buffer).
+ This command toggles the sort order for the current search.
+-Note that any fitlered searches created by
++Note that any filtered searches created by
+ `notmuch-search-filter' retain the search order of the parent
+ search."
+   (interactive)
+diff --git a/show-message.c b/show-message.c
+index 79b02e2..38f5897 100644
+--- a/show-message.c
++++ b/show-message.c
+@@ -38,7 +38,7 @@ show_message_part (GMimeObject *part, int *part_count,
+               if (i == 1)
+                   continue;
+               if (i > 1)
+-                  fprintf (stderr, "Warning: Unexpected extra parts of mutlipart/signed. Continuing.\n");
++                  fprintf (stderr, "Warning: Unexpected extra parts of multipart/signed. Continuing.\n");
+           }
+           show_message_part (g_mime_multipart_get_part (multipart, i),
+                              part_count, show_part);
+-- 
+1.6.5.2.433.g23cdb
+
+
diff --git a/test/corpus/20 b/test/corpus/20
new file mode 100644 (file)
index 0000000..f08a314
--- /dev/null
@@ -0,0 +1,101 @@
+Date: Wed, 18 Nov 2009 00:20:59 +0100
+From: Adrian Perez de Castro <aperez@igalia.com>
+To: notmuch@notmuchmail.org
+Message-ID: <20091118002059.067214ed@hikari>
+Organization: Igalia
+X-Mailer: Claws Mail 3.7.3 (GTK+ 2.18.3; x86_64-redhat-linux-gnu)
+Face: iVBORw0KGgoAAAANSUhEUgAAADAAAAAwBAMAAAClLOS0AAAAAXNSR0IArs4c6QAAADBQTFRFBwcHFhYWKCgoNzc3SEhIV1dXaGhod3d3iIiIlpaWqKiouLi4x8fH2NjY5+fn/v7+rSjDkgAAAjVJREFUOE9l07tvE0EQwOHfrkV9O+eko7g701BBfECJsIigT2IpooIqaSiRUEB0REj00FBQgYSCkhry+gecUPJybJeIxLumTbilsH2PMNXufDOa3ZVW+1JkpbUmD/8+vXR3c7or4Gz93mH309Kz8/C9/RQge7VfhW/LW+PF8IkrQ7Z6OKmQr1tl+LU/yWP9mxJka9O88fZHPwf/7u0kLyCnX3I4fQhgjAgIfi+HHw5A1Y2ggIMcFKAEnRoL0M3BosI4TI2IATjuT8DvSNJoNNJgkIhxlr9TUHeSpDnfohlIrMBlU+BGmsZqfr69FMfGMw4NoG835+J62riWyjQ/uXlTQjNUIoYegMsBM0pCD8oDas7n4HQsBghXFxJTW42KDs+4XLfjsN0wOYgABqARjMKIHIaAQnmHjsI5Cvi9Cf6k03OoWBkpIP3Q7354+dEimFBKHbMP9oKjwfd9gbrxR5KDToczK4uPF8UgNomKU2GaENRi77zyDKICxKBS4xXYbONPMQMdYZTBwMiMWiUg9g6UJ3OBogzjV8E7sBVwyvfAOYdQhsABzuOxI1MGZbs98Q6Md5UOfbbR2R0eWOesrnRw5ajT6f60LrNhWIHZpBnUWv2s14ukArWWTqTes3YQxRXgFkcMu70TPYqqUBs0YwmO967OVIdTG4bY4a7WLaqgLm5vbHdH5np0Dri//fmg7y8scB4u3+zsuNlH0X+g19bby69b+TYH6isvns8VdQWgxj9tHP8AR5/hSdYqkwsAAAAASUVORK5CYII=
+Mime-Version: 1.0
+Subject: [notmuch] Introducing myself
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============1167731900=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+--===============1167731900==
+Content-Type: multipart/signed; micalg=PGP-SHA1;
+ boundary="Sig_/ayZz9m37AOMROJCyUudvXvZ"; protocol="application/pgp-signature"
+
+--Sig_/ayZz9m37AOMROJCyUudvXvZ
+Content-Type: text/plain; charset=US-ASCII
+Content-Transfer-Encoding: quoted-printable
+
+
+Hello to all,
+
+I have just heard about Not Much today in some random Linux-related news
+site (LWN?), my name is Adrian Perez and I work as systems administrator
+(although I can do some code as well :P). I have always thought that the
+ideas behind Sup were great, but after some time using it, I got tired of
+the oddities that it has. I also do not like doing things like having to
+install Ruby just for reading and sorting mails. Some time ago I thought
+about doing something like Not Much and in fact I played a bit with the
+Python+Xapian and the Python+Whoosh combinations, because I find relaxing
+to code things in Python when I am not working and also it is installed
+by default on most distribution. I got to have some mailboxes indexed and
+basic searching working a couple of months ago. Lately I have been very
+busy and had no time for coding, and them... boom! Not Much appears -- and
+it is almost exactly what I was trying to do, but faster. I have been
+playing a bit with Not Much today, and I think it has potential.
+
+Also, I would like to share one idea I had in mind, that you might find
+interesting: One thing I have found very annoying is having to re-tag my
+mail when the indexes get b0rked (it happened a couple of times to me while
+using Sup), so I was planning to mails as read/unread and adding the tags
+not just to the index, but to the mail text itself, e.g. by adding a
+"X-Tags" header field or by reusing the "Keywords" one. This way, the index
+could be totally recreated by re-reading the mail directories, and this
+would also allow to a tools like OfflineIMAP [1] to get the mails into a
+local maildir, tagging and indexing the mails with the e-mail reader and
+then syncing back the messages with the "X-Tags" header to the IMAP server.
+This would allow to use the mail reader from a different computer and still
+have everything tagged finely.
+
+Best regards,
+
+
+---
+[1] http://software.complete.org/software/projects/show/offlineimap
+
+--=20
+Adrian Perez de Castro <aperez@igalia.com>
+Igalia - Free Software Engineering
+
+--Sig_/ayZz9m37AOMROJCyUudvXvZ
+Content-Type: application/pgp-signature; name=signature.asc
+Content-Disposition: attachment; filename=signature.asc
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v2.0.13 (GNU/Linux)
+
+iEYEARECAAYFAksDL+AACgkQkcVZ2+TJEjtsuQCfXmilW8WpMQHCnwwJjRE1PWZy
+oFAAn3MmXC5sW7MvCFjs7ks6U16zgMEg
+=eL9p
+-----END PGP SIGNATURE-----
+
+--Sig_/ayZz9m37AOMROJCyUudvXvZ--
+
+--===============1167731900==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============1167731900==--
+
diff --git a/test/corpus/21 b/test/corpus/21
new file mode 100644 (file)
index 0000000..7ff55cc
--- /dev/null
@@ -0,0 +1,102 @@
+MIME-Version: 1.0
+Date: Tue, 17 Nov 2009 16:23:53 -0800
+Message-ID: <cf0c4d610911171623q3e27a0adx802e47039b57604b@mail.gmail.com>
+From: Alex Botero-Lowry <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Content-Type: multipart/mixed; boundary=0016e64ca4d8f27a4804789a4139
+Subject: [notmuch] [PATCH] Error out if no query is supplied to search
+       instead of going into an infinite loop
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+--0016e64ca4d8f27a4804789a4139
+Content-Type: multipart/alternative; boundary=0016e64ca4d8f27a3604789a4137
+
+--0016e64ca4d8f27a3604789a4137
+Content-Type: text/plain; charset=ISO-8859-1
+
+In this case error out when no query is supplied. There seems to be an
+infinite-loop casued by i think notmuch_query_search_threads having
+an exception:
+
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+A Xapian exception occurred: Syntax: <expression> AND <expression>
+
+I'll look into that bug specifically a bit later.
+
+It might be better to do a usage instead of just throwing an error here?
+
+alex
+
+--0016e64ca4d8f27a3604789a4137
+Content-Type: text/html; charset=ISO-8859-1
+
+In this case error out when no query is supplied. There seems to be an infinite-loop casued by i think notmuch_query_search_threads having<br>an exception:<br><br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>
+A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>
+A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>
+A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>
+A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>
+A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br>A Xapian exception occurred: Syntax: &lt;expression&gt; AND &lt;expression&gt;<br><br>I&#39;ll look into that bug specifically a bit later.<br>
+<br>It might be better to do a usage instead of just throwing an error here?<br><br>alex<br>
+
+--0016e64ca4d8f27a3604789a4137--
+--0016e64ca4d8f27a4804789a4139
+Content-Type: application/octet-stream; 
+       name="0001-Error-out-if-no-query-is-supplied-to-search-instead-.patch"
+Content-Disposition: attachment; 
+       filename="0001-Error-out-if-no-query-is-supplied-to-search-instead-.patch"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_g25cms190
+
+RnJvbSAzZjk0MzFmNzRhNWZmNjZjODRjODY5YTNlMjZjMmJhZDQyYmVkMWIxIE1vbiBTZXAgMTcg
+MDA6MDA6MDAgMjAwMQpGcm9tOiBBbGV4YW5kZXIgQm90ZXJvLUxvd3J5IDxhbGV4LmJvdGVyb2xv
+d3J5QGdtYWlsLmNvbT4KRGF0ZTogVHVlLCAxNyBOb3YgMjAwOSAxNjoyMDoyOCAtMDgwMApTdWJq
+ZWN0OiBbUEFUQ0hdIEVycm9yIG91dCBpZiBubyBxdWVyeSBpcyBzdXBwbGllZCB0byBzZWFyY2gg
+aW5zdGVhZCBvZiBnb2luZyBpbnRvIGFuIGluZmluaXRlIGxvb3AKCi0tLQogbm90bXVjaC1zZWFy
+Y2guYyB8ICAgIDUgKysrKysKIDEgZmlsZXMgY2hhbmdlZCwgNSBpbnNlcnRpb25zKCspLCAwIGRl
+bGV0aW9ucygtKQoKZGlmZiAtLWdpdCBhL25vdG11Y2gtc2VhcmNoLmMgYi9ub3RtdWNoLXNlYXJj
+aC5jCmluZGV4IDhkYjA5YzcuLmQ5NGZjY2QgMTAwNjQ0Ci0tLSBhL25vdG11Y2gtc2VhcmNoLmMK
+KysrIGIvbm90bXVjaC1zZWFyY2guYwpAQCAtNjYsNiArNjYsMTEgQEAgbm90bXVjaF9zZWFyY2hf
+Y29tbWFuZCAodm9pZCAqY3R4LCBpbnQgYXJnYywgY2hhciAqYXJndltdKQogICAgIGFyZ2MgLT0g
+aTsKICAgICBhcmd2ICs9IGk7CiAKKyAgICBpZiAoYXJnYyA9PSAwKSB7CisgICAgICAgIGZwcmlu
+dGYgKHN0ZGVyciwgIk5vIHF1ZXJ5IHByb3ZpZGVkXG4iKTsKKyAgICAgICAgcmV0dXJuIDE7Cisg
+ICAgfQorCiAgICAgY29uZmlnID0gbm90bXVjaF9jb25maWdfb3BlbiAoY3R4LCBOVUxMLCBOVUxM
+KTsKICAgICBpZiAoY29uZmlnID09IE5VTEwpCiAJcmV0dXJuIDE7Ci0tIAoxLjYuNS4yCgo=
+--0016e64ca4d8f27a4804789a4139
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--0016e64ca4d8f27a4804789a4139--
+
diff --git a/test/corpus/22 b/test/corpus/22
new file mode 100644 (file)
index 0000000..08adada
--- /dev/null
@@ -0,0 +1,84 @@
+Date: Tue, 17 Nov 2009 19:50:40 -0500
+From: Lars Kellogg-Stedman <lars@seas.harvard.edu>
+To: Keith Packard <keithp@keithp.com>
+Message-ID: <20091118005040.GA25380@dottiness.seas.harvard.edu>
+References: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+       <87iqd9rn3l.fsf@vertex.dottedmag>
+       <20091117203301.GV3165@dottiness.seas.harvard.edu>
+       <yunaayketfm.fsf@aiko.keithp.com>
+MIME-Version: 1.0
+In-Reply-To: <yunaayketfm.fsf@aiko.keithp.com>
+User-Agent: Mutt/1.5.19 (2009-01-05)
+Cc: notmuch@notmuchmail.org
+Subject: Re: [notmuch] Working with Maildir storage?
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============1483126515=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+
+--===============1483126515==
+Content-Type: multipart/signed; micalg=pgp-sha256;
+       protocol="application/pgp-signature"; boundary="9amGYk9869ThD9tj"
+Content-Disposition: inline
+
+
+--9amGYk9869ThD9tj
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+> 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).
+
+--=20
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+
+--9amGYk9869ThD9tj
+Content-Type: application/pgp-signature
+Content-Disposition: inline
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLA0TgAAoJENdGlQYxQazYsG0IAJ1t9h4Q3ma8z8ejeKR22Xh0
+WcuRX2x9yEXy/+aG9W7Mot0mqUQCiLdmHM/2h5N9BFHyJvfOUf8lmssrJ5OS/kp5
+j7FIx3GUELBmEZqFUPjRSQPk1hZURYdRsloKkrbQ2kAivjjb50zAAQ8Av4Cgj6cS
+3HvNNmeVfJt1NS75vm+/wn48M8Vrcdv4gvNlSOhgFOixknvRoxSyNDOHYBKvHnSV
+2HnO0GzhAQzDZAwdHBzJtb8vRmglrH33TVdrE7OW+sojYB3Wyz8r9+HIt8Q8ovzX
+nQ8p0Nf5DlF7tye3JYo0EeNm5EQJ4q0YyVYInhmtpi3A5Cyu50GcB/GZ5Sd6ajo=
+=WULe
+-----END PGP SIGNATURE-----
+
+--9amGYk9869ThD9tj--
+
+--===============1483126515==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============1483126515==--
+
diff --git a/test/corpus/23 b/test/corpus/23
new file mode 100644 (file)
index 0000000..9bb62d7
--- /dev/null
@@ -0,0 +1,145 @@
+Date: Tue, 17 Nov 2009 19:58:29 -0500
+From: Lars Kellogg-Stedman <lars@seas.harvard.edu>
+To: notmuch <notmuch@notmuchmail.org>
+Message-ID: <20091118005829.GB25380@dottiness.seas.harvard.edu>
+MIME-Version: 1.0
+User-Agent: Mutt/1.5.19 (2009-01-05)
+Subject: [notmuch] "notmuch help" outputs to stderr?
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============1359248349=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+
+--===============1359248349==
+Content-Type: multipart/signed; micalg=pgp-sha256;
+       protocol="application/pgp-signature"; boundary="L6iaP+gRLNZHKoI4"
+Content-Disposition: inline
+
+
+--L6iaP+gRLNZHKoI4
+Content-Type: multipart/mixed; boundary="z6Eq5LdranGa6ru8"
+Content-Disposition: inline
+
+
+--z6Eq5LdranGa6ru8
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+I'm just noticing that 'notmuch help ...' outputs to stderr, which
+isn't terribly intuitive.  For example, the obvious invocation:
+
+  notmuch help | less
+
+=2E..isn't terribly helpful.
+
+I've attached a patch that lets usage() take a FILE * argument so that
+you can output to stderr in response to usage errors, and stdout in
+response to an explicit request.
+
+--=20
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+
+--z6Eq5LdranGa6ru8
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: attachment; filename="notmuch-help.patch"
+Content-Transfer-Encoding: quoted-printable
+
+diff --git a/notmuch.c b/notmuch.c
+index c47e640..a35cb99 100644
+--- a/notmuch.c
++++ b/notmuch.c
+@@ -157,23 +157,23 @@ command_t commands[] =3D {
+ };
+=20
+ static void
+-usage (void)
++usage (FILE *out)
+ {
+     command_t *command;
+     unsigned int i;
+=20
+-    fprintf (stderr, "Usage: notmuch <command> [args...]\n");
+-    fprintf (stderr, "\n");
+-    fprintf (stderr, "Where <command> and [args...] are as follows:\n");
+-    fprintf (stderr, "\n");
++    fprintf (out, "Usage: notmuch <command> [args...]\n");
++    fprintf (out, "\n");
++    fprintf (out, "Where <command> and [args...] are as follows:\n");
++    fprintf (out, "\n");
+=20
+     for (i =3D 0; i < ARRAY_SIZE (commands); i++) {
+       command =3D &commands[i];
+=20
+-      fprintf (stderr, "\t%s\t%s\n\n", command->name, command->summary);
++      fprintf (out, "\t%s\t%s\n\n", command->name, command->summary);
+     }
+=20
+-    fprintf (stderr, "Use \"notmuch help <command>\" for more details on e=
+ach command.\n\n");
++    fprintf (out, "Use \"notmuch help <command>\" for more details on each=
+ command.\n\n");
+ }
+=20
+ static int
+@@ -183,8 +183,8 @@ notmuch_help_command (unused (void *ctx), int argc, cha=
+r *argv[])
+     unsigned int i;
+=20
+     if (argc =3D=3D 0) {
+-      fprintf (stderr, "The notmuch mail system.\n\n");
+-      usage ();
++      fprintf (stdout, "The notmuch mail system.\n\n");
++      usage (stdout);
+       return 0;
+     }
+=20
+
+--z6Eq5LdranGa6ru8--
+
+--L6iaP+gRLNZHKoI4
+Content-Type: application/pgp-signature
+Content-Disposition: inline
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLA0a1AAoJENdGlQYxQazYr78IAJtqTWIpBqSdOWqTzt/r4XNn
+KJ5mWAoNfq4H+3kx3xoWOFYS7qAYeJoHQWCDbMdb+zEXvPX6hMFn9+OxRN+N5FdQ
+uxGTugSG9xSsK28oGDCQUtr5uheo+tH0jygPjI+LTD97vjUYS4K2qzhLGFJmpLcj
+1akMJXM0gSdPZT8dJyjxvC15pgboLspE4+b6jexXmd4UoFvXgqvjkYHeV4Wk+s0L
+xu+HkCGXL9WHYc3t171fFAru4Zd1AUxFQl4BZ2Y+OqRZUrD28Mtz3zGQxbJQoifl
+JFrgPAWioLN71SkVq/y+efjvGSl0osPpKU5dftMmyY1zV7k7mMlO08ZSJU+wANA=
+=Iijt
+-----END PGP SIGNATURE-----
+
+--L6iaP+gRLNZHKoI4--
+
+--===============1359248349==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============1359248349==--
+
diff --git a/test/corpus/24 b/test/corpus/24
new file mode 100644 (file)
index 0000000..c800020
--- /dev/null
@@ -0,0 +1,204 @@
+Return-path: <notmuch-bounces@notmuchmail.org>
+Envelope-to: cworth@localhost
+Delivery-date: Wed, 18 Nov 2009 01:43:47 -0800
+Received: from yoom.home.cworth.org ([127.0.0.1])
+       by yoom.home.cworth.org with esmtp (Exim 4.69)
+       (envelope-from <notmuch-bounces@notmuchmail.org>)
+       id 1NAgpH-0005Ab-20
+       for cworth@localhost; Wed, 18 Nov 2009 01:27:47 -0800
+X-Original-To: cworth@cworth.org
+Delivered-To: cworth@cworth.org
+Received: from olra.theworths.org [82.165.184.25]
+       by yoom.home.cworth.org with IMAP (fetchmail-6.3.9-rc2)
+       for <cworth@localhost> (single-drop); Wed, 18 Nov 2009 01:27:47 -0800 (PST)
+Received: from localhost (localhost [127.0.0.1])
+       by olra.theworths.org (Postfix) with ESMTP id 12248431FC3
+       for <cworth@cworth.org>; Tue, 17 Nov 2009 17:01:22 -0800 (PST)
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
+X-Spam-Flag: NO
+X-Spam-Score: -6.17
+X-Spam-Level: 
+X-Spam-Status: No, score=-6.17 tagged_above=-999 required=2 tests=[AWL=0.429,
+       BAYES_00=-2.599, RCVD_IN_DNSWL_MED=-4] autolearn=unavailable
+Received: from olra.theworths.org ([127.0.0.1])
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
+       with ESMTP id TmBdVd1i-Wjb; Tue, 17 Nov 2009 17:01:20 -0800 (PST)
+Received: from olra.theworths.org (localhost [127.0.0.1])
+       by olra.theworths.org (Postfix) with ESMTP id AF876431FBC;
+       Tue, 17 Nov 2009 17:01:20 -0800 (PST)
+X-Original-To: notmuch@notmuchmail.org
+Delivered-To: notmuch@notmuchmail.org
+Received: from localhost (localhost [127.0.0.1])
+       by olra.theworths.org (Postfix) with ESMTP id 75784431FBC
+       for <notmuch@notmuchmail.org>; Tue, 17 Nov 2009 17:01:19 -0800 (PST)
+X-Virus-Scanned: Debian amavisd-new at olra.theworths.org
+Received: from olra.theworths.org ([127.0.0.1])
+       by localhost (olra.theworths.org [127.0.0.1]) (amavisd-new, port 10024)
+       with ESMTP id IoYHzHoKBskU for <notmuch@notmuchmail.org>;
+       Tue, 17 Nov 2009 17:01:18 -0800 (PST)
+Received: from smtp-outbound.seas.harvard.edu (smtp-outbound.seas.harvard.edu
+       [140.247.51.171])
+       by olra.theworths.org (Postfix) with ESMTP id 7E033431FAE
+       for <notmuch@notmuchmail.org>; Tue, 17 Nov 2009 17:01:18 -0800 (PST)
+Received: from dottiness.seas.harvard.edu (dottiness.seas.harvard.edu
+       [140.247.52.224])
+       by smtp-outbound.seas.harvard.edu (8.13.8/8.13.8) with SMTP id
+       nAI11Gkj008772
+       for <notmuch@notmuchmail.org>; Tue, 17 Nov 2009 20:01:16 -0500
+Received: by dottiness.seas.harvard.edu (sSMTP sendmail emulation);
+       Tue, 17 Nov 2009 20:01:16 -0500
+Date: Tue, 17 Nov 2009 20:01:16 -0500
+From: Lars Kellogg-Stedman <lars@seas.harvard.edu>
+To: notmuch <notmuch@notmuchmail.org>
+Message-ID: <20091118010116.GC25380@dottiness.seas.harvard.edu>
+References: <20091118005829.GB25380@dottiness.seas.harvard.edu>
+MIME-Version: 1.0
+In-Reply-To: <20091118005829.GB25380@dottiness.seas.harvard.edu>
+User-Agent: Mutt/1.5.19 (2009-01-05)
+Subject: Re: [notmuch] "notmuch help" outputs to stderr?
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Content-Type: multipart/mixed; boundary="===============0848253760=="
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+
+--===============0848253760==
+Content-Type: multipart/signed; micalg=pgp-sha256;
+       protocol="application/pgp-signature"; boundary="ZInfyf7laFu/Kiw7"
+Content-Disposition: inline
+
+
+--ZInfyf7laFu/Kiw7
+Content-Type: multipart/mixed; boundary="KdquIMZPjGJQvRdI"
+Content-Disposition: inline
+
+
+--KdquIMZPjGJQvRdI
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: inline
+Content-Transfer-Encoding: quoted-printable
+
+> I've attached a patch that lets usage() take a FILE * argument so that
+> you can output to stderr in response to usage errors, and stdout in
+> response to an explicit request.
+
+Whoops, missed a couple of stderr's in that last patch.  New one
+attached.
+
+--=20
+Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Senior Technologist, Computing and Information Technology
+Harvard University School of Engineering and Applied Sciences
+
+
+--KdquIMZPjGJQvRdI
+Content-Type: text/plain; charset=us-ascii
+Content-Disposition: attachment; filename="notmuch-help.patch"
+Content-Transfer-Encoding: quoted-printable
+
+diff --git a/notmuch.c b/notmuch.c
+index c47e640..446c810 100644
+--- a/notmuch.c
++++ b/notmuch.c
+@@ -157,23 +157,23 @@ command_t commands[] =3D {
+ };
+=20
+ static void
+-usage (void)
++usage (FILE *out)
+ {
+     command_t *command;
+     unsigned int i;
+=20
+-    fprintf (stderr, "Usage: notmuch <command> [args...]\n");
+-    fprintf (stderr, "\n");
+-    fprintf (stderr, "Where <command> and [args...] are as follows:\n");
+-    fprintf (stderr, "\n");
++    fprintf (out, "Usage: notmuch <command> [args...]\n");
++    fprintf (out, "\n");
++    fprintf (out, "Where <command> and [args...] are as follows:\n");
++    fprintf (out, "\n");
+=20
+     for (i =3D 0; i < ARRAY_SIZE (commands); i++) {
+       command =3D &commands[i];
+=20
+-      fprintf (stderr, "\t%s\t%s\n\n", command->name, command->summary);
++      fprintf (out, "\t%s\t%s\n\n", command->name, command->summary);
+     }
+=20
+-    fprintf (stderr, "Use \"notmuch help <command>\" for more details on e=
+ach command.\n\n");
++    fprintf (out, "Use \"notmuch help <command>\" for more details on each=
+ command.\n\n");
+ }
+=20
+ static int
+@@ -183,8 +183,8 @@ notmuch_help_command (unused (void *ctx), int argc, cha=
+r *argv[])
+     unsigned int i;
+=20
+     if (argc =3D=3D 0) {
+-      fprintf (stderr, "The notmuch mail system.\n\n");
+-      usage ();
++      fprintf (stdout, "The notmuch mail system.\n\n");
++      usage (stdout);
+       return 0;
+     }
+=20
+@@ -192,8 +192,8 @@ notmuch_help_command (unused (void *ctx), int argc, cha=
+r *argv[])
+       command =3D &commands[i];
+=20
+       if (strcmp (argv[0], command->name) =3D=3D 0) {
+-          fprintf (stderr, "Help for \"notmuch %s\":\n\n", argv[0]);
+-          fprintf (stderr, "\t%s\t%s\n\n%s\n\n", command->name,
++          fprintf (stdout, "Help for \"notmuch %s\":\n\n", argv[0]);
++          fprintf (stdout, "\t%s\t%s\n\n%s\n\n", command->name,
+                    command->summary, command->documentation);
+           return 0;
+       }
+
+--KdquIMZPjGJQvRdI--
+
+--ZInfyf7laFu/Kiw7
+Content-Type: application/pgp-signature
+Content-Disposition: inline
+
+-----BEGIN PGP SIGNATURE-----
+Version: GnuPG v1.4.9 (GNU/Linux)
+
+iQEcBAEBCAAGBQJLA0dcAAoJENdGlQYxQazY4nIIAIBCds86/uTmnouvyoPruUUR
+Bg5mXcnjuopz1Nwotl9s9U5sGeZuZngxyEvDz1Z1aTEjwab8ndNTf1xCwIoqBs+l
+i/sc4nPYubLdy1Ab/84DKVtCSbj+v5rtqhegwUWV7S1BY7t8dKNPNv7YBg7P0Azs
+6s3CUxDV5eJCcxCGxxWHH8JDKRf7rDs6vzDwyPWLxlg1Xb1lEM/sRgPCKiShPdO3
+Ak2hECusjskALhSDYX8/FLMd9HwLBC13sfWuSi/pHUAIOI2jru2p5sXrVSlTnFIJ
+fiMbPhKWiEaJj2kmm4pRwAhbTWp/J8ZvXWp0AyosxXQhQUWqujiyxgfiXS70SdQ=
+=t3Yc
+-----END PGP SIGNATURE-----
+
+--ZInfyf7laFu/Kiw7--
+
+--===============0848253760==
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--===============0848253760==--
+
diff --git a/test/corpus/25 b/test/corpus/25
new file mode 100644 (file)
index 0000000..7378f82
--- /dev/null
@@ -0,0 +1,32 @@
+From: "Stewart Smith" <stewart@flamingspork.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 12:05:53 +1100
+Subject: [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++
+       libs.
+Message-ID: <1258506353-20352-1-git-send-email-stewart@flamingspork.com>
+
+Previously, Ubuntu 9.10, gcc 4.4.1 was getting:
+
+ccache gcc `pkg-config --libs glib-2.0 gmime-2.4 talloc` `xapian-config --libs` notmuch.o notmuch-config.o notmuch-dump.o notmuch-new.o notmuch-reply.o notmuch-restore.o notmuch-search.o notmuch-setup.o notmuch-show.o notmuch-tag.o notmuch-time.o gmime-filter-reply.o query-string.o show-message.o lib/notmuch.a -o notmuch
+/usr/bin/ld: lib/notmuch.a(database.o): in function global constructors keyed to BOOLEAN_PREFIX_INTERNAL:database.cc(.text+0x3a): error: undefined reference to 'std::ios_base::Init::Init()'
+---
+ Makefile.local |    2 +-
+ 1 files changed, 1 insertions(+), 1 deletions(-)
+
+diff --git a/Makefile.local b/Makefile.local
+index f824bed..dbd3e20 100644
+--- a/Makefile.local
++++ b/Makefile.local
+@@ -18,7 +18,7 @@ notmuch_client_srcs =                \
+ notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
+ notmuch: $(notmuch_client_modules) lib/notmuch.a
+-      $(CC) $(LDFLAGS) $^ -o $@
++      $(CXX) $(LDFLAGS) $^ -o $@
+ notmuch.1.gz:
+       gzip --stdout notmuch.1 > notmuch.1.gz
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/26 b/test/corpus/26
new file mode 100644 (file)
index 0000000..f3c5f53
--- /dev/null
@@ -0,0 +1,121 @@
+From: "Stewart Smith" <stewart@flamingspork.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 12:56:40 +1100
+Subject: [notmuch] [PATCH 2/2] Read mail directory in inode number order
+Message-ID: <1258509400-32511-1-git-send-email-stewart@flamingspork.com>
+
+This gives a rather decent reduction in number of seeks required when
+reading a Maildir that isn't in pagecache.
+
+Most filesystems give some locality on disk based on inode numbers.
+In ext[234] this is the inode tables, in XFS groups of sequential inode
+numbers are together on disk and the most significant bits indicate
+allocation group (i.e inode 1,000,000 is always after inode 1,000).
+
+With this patch, we read in the whole directory, sort by inode number
+before stat()ing the contents.
+
+Ideally, directory is sequential and then we make one scan through the
+file system stat()ing.
+
+Since the universe is not ideal, we'll probably seek during reading the
+directory and a fair bit while reading the inodes themselves.
+
+However... with readahead, and stat()ing in inode order, we should be
+in the best place possible to hit the cache.
+
+In a (not very good) benchmark of "how long does it take to find the first
+15,000 messages in my Maildir after 'echo 3 > /proc/sys/vm/drop_caches'",
+this patch consistently cut at least 8 seconds off the scan time.
+
+Without patch: 50 seconds
+With patch: 38-42 seconds.
+
+(I did this in a previous maildir reading project and saw large improvements too)
+---
+ notmuch-new.c |   32 +++++++++++++++-----------------
+ 1 files changed, 15 insertions(+), 17 deletions(-)
+
+diff --git a/notmuch-new.c b/notmuch-new.c
+index 83a05ba..11fad8c 100644
+--- a/notmuch-new.c
++++ b/notmuch-new.c
+@@ -73,6 +73,11 @@ add_files_print_progress (add_files_state_t *state)
+     fflush (stdout);
+ }
++static int ino_cmp(const struct dirent **a, const struct dirent **b)
++{
++  return ((*a)->d_ino < (*b)->d_ino)? -1: 1;
++}
++
+ /* Examine 'path' recursively as follows:
+  *
+  *   o Ask the filesystem for the mtime of 'path' (path_mtime)
+@@ -100,13 +105,12 @@ add_files_recursive (notmuch_database_t *notmuch,
+                    add_files_state_t *state)
+ {
+     DIR *dir = NULL;
+-    struct dirent *e, *entry = NULL;
+-    int entry_length;
+-    int err;
++    struct dirent *entry = NULL;
+     char *next = NULL;
+     time_t path_mtime, path_dbtime;
+     notmuch_status_t status, ret = NOTMUCH_STATUS_SUCCESS;
+     notmuch_message_t *message = NULL;
++    struct dirent **namelist = NULL;
+     /* If we're told to, we bail out on encountering a read-only
+      * directory, (with this being a clear clue from the user to
+@@ -122,31 +126,23 @@ add_files_recursive (notmuch_database_t *notmuch,
+     path_mtime = st->st_mtime;
+     path_dbtime = notmuch_database_get_timestamp (notmuch, path);
++    int n_entries= scandir(path, &namelist, 0, ino_cmp);
+-    dir = opendir (path);
+-    if (dir == NULL) {
++    if (n_entries == -1) {
+       fprintf (stderr, "Error opening directory %s: %s\n",
+                path, strerror (errno));
+       ret = NOTMUCH_STATUS_FILE_ERROR;
+       goto DONE;
+     }
+-    entry_length = offsetof (struct dirent, d_name) +
+-      pathconf (path, _PC_NAME_MAX) + 1;
+-    entry = malloc (entry_length);
++    int i=0;
+     while (!interrupted) {
+-      err = readdir_r (dir, entry, &e);
+-      if (err) {
+-          fprintf (stderr, "Error reading directory: %s\n",
+-                   strerror (errno));
+-          ret = NOTMUCH_STATUS_FILE_ERROR;
+-          goto DONE;
+-      }
+-
+-      if (e == NULL)
++      if (i == n_entries)
+           break;
++        entry= namelist[i++];
++
+       /* If this directory hasn't been modified since the last
+        * add_files, then we only need to look further for
+        * sub-directories. */
+@@ -243,6 +239,8 @@ add_files_recursive (notmuch_database_t *notmuch,
+       free (entry);
+     if (dir)
+       closedir (dir);
++    if (namelist)
++      free (namelist);
+     return ret;
+ }
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/27 b/test/corpus/27
new file mode 100644 (file)
index 0000000..7f0f045
--- /dev/null
@@ -0,0 +1,21 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 17:59:49 -0800
+Subject: [notmuch] New to the list
+In-Reply-To: <1258498485-sup-142@elly>
+References: <1258498485-sup-142@elly>
+Message-ID: <yun3a4cegoa.fsf@aiko.keithp.com>
+
+On Tue, 17 Nov 2009 23:57:18 +0100, Israel Herraiz <isra at herraiz.org> wrote:
+
+> "Not much" sounds interesting, and I wonder whether it could be
+> integrated with the views of Sup (inbox, threads, etc). So I have
+> subscribed to the list to keep an eye on what's going on here.
+
+We've tried to clone much of the sup UI inside emacs, including the
+inbox and threaded message presentation. Of course, we had to "improve"
+it a bit, as much due to the differences between curses and emacs as due
+to personal preferences...
+
+-keith
+
diff --git a/test/corpus/28 b/test/corpus/28
new file mode 100644 (file)
index 0000000..83ce01b
--- /dev/null
@@ -0,0 +1,38 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 18:03:17 -0800
+Subject: [notmuch] Introducing myself
+In-Reply-To: <20091118002059.067214ed@hikari>
+References: <20091118002059.067214ed@hikari>
+Message-ID: <yun1vjwegii.fsf@aiko.keithp.com>
+
+On Wed, 18 Nov 2009 00:20:59 +0100, Adrian Perez de Castro <aperez at igalia.com> wrote:
+
+> Some time ago I thought
+> about doing something like Not Much and in fact I played a bit with the
+> Python+Xapian and the Python+Whoosh combinations, because I find relaxing
+> to code things in Python when I am not working and also it is installed
+> by default on most distribution. I got to have some mailboxes indexed and
+> basic searching working a couple of months ago.
+
+Sup certainly started a lot of people thinking...
+
+> Also, I would like to share one idea I had in mind, that you might find
+> interesting: One thing I have found very annoying is having to re-tag my
+> mail when the indexes get b0rked (it happened a couple of times to me while
+> using Sup), so I was planning to mails as read/unread and adding the tags
+> not just to the index, but to the mail text itself, e.g. by adding a
+> "X-Tags" header field or by reusing the "Keywords" one.
+
+Easier than that, notmuch (and sup too), provide a 'dump' command which
+just lists all of the message IDs and their associated tags. Makes
+saving tags easy and doesn't involve rewriting messages. I do this once
+a day just before my computer is backed up to an external drive.
+
+If the index is destroyed, you can reindex the messages and then reapply
+all of the tags with 'notmuch restore'.
+
+--
+keith.packard at intel.com
+
+
diff --git a/test/corpus/29 b/test/corpus/29
new file mode 100644 (file)
index 0000000..c76eff3
--- /dev/null
@@ -0,0 +1,21 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 18:04:31 -0800
+Subject: [notmuch] archive
+In-Reply-To: <20091117232137.GA7669@griffis1.net>
+References: <20091117232137.GA7669@griffis1.net>
+Message-ID: <yunzl6kd1w0.fsf@aiko.keithp.com>
+
+On Tue, 17 Nov 2009 18:21:38 -0500, Aron Griffis <agriffis at n01se.net> wrote:
+
+> Just subscribed, I'd like to catch up on the previous postings,
+> but the archive link seems to be bogus?
+
+Yeah, the archive appears broken and will need to wait until Carl
+arrives in Barcelona to get fixed.
+
+--
+keith.packard at intel.com
+
+
+
diff --git a/test/corpus/30 b/test/corpus/30
new file mode 100644 (file)
index 0000000..a5b94a0
--- /dev/null
@@ -0,0 +1,75 @@
+From: "Stewart Smith" <stewart@flamingspork.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 13:22:20 +1100
+Subject: [notmuch] [PATCH] count_files: sort directory in inode order before
+       statting
+Message-ID: <1258510940-7018-1-git-send-email-stewart@flamingspork.com>
+
+---
+ notmuch-new.c |   30 ++++++++++--------------------
+ 1 files changed, 10 insertions(+), 20 deletions(-)
+
+diff --git a/notmuch-new.c b/notmuch-new.c
+index 11fad8c..c5f841a 100644
+--- a/notmuch-new.c
++++ b/notmuch-new.c
+@@ -308,36 +308,26 @@ add_files (notmuch_database_t *notmuch,
+ static void
+ count_files (const char *path, int *count)
+ {
+-    DIR *dir;
+-    struct dirent *e, *entry = NULL;
+-    int entry_length;
+-    int err;
++    struct dirent *entry = NULL;
+     char *next;
+     struct stat st;
++    struct dirent **namelist = NULL;
+-    dir = opendir (path);
++    int n_entries= scandir(path, &namelist, 0, ino_cmp);
+-    if (dir == NULL) {
++    if (n_entries == -1) {
+       fprintf (stderr, "Warning: failed to open directory %s: %s\n",
+                path, strerror (errno));
+       goto DONE;
+     }
+-    entry_length = offsetof (struct dirent, d_name) +
+-      pathconf (path, _PC_NAME_MAX) + 1;
+-    entry = malloc (entry_length);
++    int i=0;
+     while (!interrupted) {
+-      err = readdir_r (dir, entry, &e);
+-      if (err) {
+-          fprintf (stderr, "Error reading directory: %s\n",
+-                   strerror (errno));
+-          free (entry);
+-          goto DONE;
+-      }
++        if (i == n_entries)
++            break;
+-      if (e == NULL)
+-          break;
++        entry= namelist[i++];
+       /* Ignore special directories to avoid infinite recursion.
+        * Also ignore the .notmuch directory.
+@@ -376,8 +366,8 @@ count_files (const char *path, int *count)
+   DONE:
+     if (entry)
+       free (entry);
+-
+-    closedir (dir);
++    if (namelist)
++        free (namelist);
+ }
+ int
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/31 b/test/corpus/31
new file mode 100644 (file)
index 0000000..88f17ca
--- /dev/null
@@ -0,0 +1,31 @@
+From: "Jjgod Jiang" <gzjjgod@gmail.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 11:50:17 +0800
+Subject: [notmuch] Mac OS X/Darwin compatibility issues
+Message-ID: <ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com>
+
+Hi,
+
+When I tried to compile notmuch under Mac OS X 10.6, several issues
+arisen:
+
+1. g++ reports 'warning: command line option "-Wmissing-declarations"
+is valid for C/ObjC but not for C++'
+
+2.
+notmuch-reply.c: In function ?address_is_users?:
+notmuch-reply.c:87: warning: passing argument 2 of
+?notmuch_config_get_user_other_email? from incompatible pointer type
+
+That's due to the size incompatibility of 'unsigned int' and 'size_t'
+(size_t is uint64_t in Mac OS X).
+
+3. Several errors about missing GNU extensions like getline() and strndup():
+
+warning: implicit declaration of function ?getline?
+error: ?strndup? was not declared in this scope
+
+We can implement these with fgets() and strncpy() though.
+
+- Jiang
+
diff --git a/test/corpus/32 b/test/corpus/32
new file mode 100644 (file)
index 0000000..c1633cd
--- /dev/null
@@ -0,0 +1,165 @@
+From: "Jan Janak" <jan@ryngle.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 05:57:03 +0100
+Subject: [notmuch] [PATCH] notmuch new: Support for conversion of spool
+       subdirectories into tags
+Message-ID: <1258520223-15328-1-git-send-email-jan@ryngle.com>
+
+This patch makes it possible to convert subdirectory names inside the
+spool directory into message tags. Messages stored in subdirectory
+"foo" will be marked with tag "foo". Message duplicates found in several
+subdirectories will be given one tag per subdirectory.
+
+This feature can be used to synchronize notmuch's tags with with gmail
+tags, for example. Gmail IMAP servers convert tags to IMAP
+subdirectories and those subdirectories can be converted back to tags
+in notmuch.
+
+The patch modifies notmuch_database_add_message function to return a
+pointer to the message even if a message duplicate was found in the
+database. This is needed if we want to add a tag for each subdirectory
+in which a message duplicate was found.
+
+In addition to that, it makes the pointer to notmuch_config_t global
+(previously it was a local variable in notmuch_new_command). The
+configuration data structure is used by the function which converts
+subdirectory names to tags.
+
+Finally, there is a new function called subdir_to_tag. The function
+extracts the name of the subdirectory inside the spool from the full
+path of the message (also removing Maildir's cur,dir,and tmp
+subdirectories) and adds it as a new tag to the message.
+
+Signed-off-by: Jan Janak <jan at ryngle.com>
+---
+ lib/database.cc |    3 +-
+ notmuch-new.c   |   74 +++++++++++++++++++++++++++++++++++++++++++++++++++++-
+ 2 files changed, 74 insertions(+), 3 deletions(-)
+
+diff --git a/lib/database.cc b/lib/database.cc
+index 3c8d626..f7799d2 100644
+--- a/lib/database.cc
++++ b/lib/database.cc
+@@ -949,7 +949,8 @@ notmuch_database_add_message (notmuch_database_t *notmuch,
+   DONE:
+     if (message) {
+-      if (ret == NOTMUCH_STATUS_SUCCESS && message_ret)
++              if ((ret == NOTMUCH_STATUS_SUCCESS ||
++                       ret == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) && message_ret)
+           *message_ret = message;
+       else
+           notmuch_message_destroy (message);
+diff --git a/notmuch-new.c b/notmuch-new.c
+index 83a05ba..d94ce16 100644
+--- a/notmuch-new.c
++++ b/notmuch-new.c
+@@ -19,6 +19,9 @@
+  */
+ #include "notmuch-client.h"
++#include <libgen.h>
++
++static notmuch_config_t *config = 0;
+ static volatile sig_atomic_t do_add_files_print_progress = 0;
+@@ -45,6 +48,69 @@ tag_inbox_and_unread (notmuch_message_t *message)
+     notmuch_message_add_tag (message, "unread");
+ }
++/*
++ * Extracts the sub-directory from the filename and adds it as a new tag to
++ * the message. The filename must begin with the database directory configured
++ * in the configuration file. This prefix is then removed. If the remaining
++ * sub-directory ends with one of the Maildir special directories (/tmp, /new,
++ * /cur) then they are removed as well. If there is anything left then the
++ * function adds it as a new tag to the message.
++ *
++ * The function does nothing if it cannot extract a sub-directory from
++ * filename.
++ */
++static void
++subdir_to_tag (char* filename, notmuch_message_t *message)
++{
++      const char* db_path;
++      char *msg_dir, *tmp;
++      int db_path_len, msg_dir_len;
++
++      if (config == NULL) return;
++    db_path = notmuch_config_get_database_path (config);
++      if (db_path == NULL) return;
++      db_path_len = strlen(db_path);
++
++      /* Make a copy of the string as dirname may need to modify it. */
++      tmp = talloc_strdup(message, filename);
++      msg_dir = dirname(tmp);
++      msg_dir_len = strlen(msg_dir);
++
++      /* If msg_dir starts with db_path, remove it, including the / which delimits
++       * it from the rest of the directory name. */
++      if (db_path_len < msg_dir_len &&
++              !strncmp(db_path, msg_dir, db_path_len)) {
++              msg_dir += db_path_len + 1;
++              msg_dir_len -= db_path_len + 1;
++      } else {
++              /* If we get here, either the message filename is not inside the
++               * database directory configured in the configuration file, or it is a
++               * file in the root directory of the database. Either way we just skip
++               * it because we do not know how to convert it to a meaningful
++               * subdirectory string that we could add as tag. */
++              goto out;
++      }
++
++      /* Special conditioning for Maildirs. If the remainder of the directory
++       * name ends with /new, /cur, or /tmp then remove it. */
++      if ((msg_dir_len >= 4) &&
++              (!strncmp(msg_dir + msg_dir_len - 4, "/new", 4) ||
++               !strncmp(msg_dir + msg_dir_len - 4, "/cur", 4) ||
++               !strncmp(msg_dir + msg_dir_len - 4, "/tmp", 4))) {
++              msg_dir[msg_dir_len - 4] = '\0';
++      }
++
++      /* If, after all the modifications, we still have a subdirectory, add it
++       * as tag. */
++      if (strlen(msg_dir)) {
++              notmuch_message_add_tag (message, msg_dir);
++      }
++
++out:
++      talloc_free(tmp);
++}
++
++
+ static void
+ add_files_print_progress (add_files_state_t *state)
+ {
+@@ -186,10 +252,15 @@ add_files_recursive (notmuch_database_t *notmuch,
+                   case NOTMUCH_STATUS_SUCCESS:
+                       state->added_messages++;
+                       tag_inbox_and_unread (message);
++                      subdir_to_tag(next, message);
+                       break;
+                   /* Non-fatal issues (go on to next file) */
+                   case NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID:
+-                      /* Stay silent on this one. */
++                      /* Stay silent on this one. The message already exists in the
++                               * database, that means we may have found a duplicate in
++                               * another directory. If that's the case then we add another
++                               * tag to the message with the sub-directory. */
++                              subdir_to_tag(next, message);
+                       break;
+                   case NOTMUCH_STATUS_FILE_NOT_EMAIL:
+                       fprintf (stderr, "Note: Ignoring non-mail file: %s\n",
+@@ -386,7 +457,6 @@ int
+ notmuch_new_command (void *ctx,
+                    unused (int argc), unused (char *argv[]))
+ {
+-    notmuch_config_t *config;
+     notmuch_database_t *notmuch;
+     add_files_state_t add_files_state;
+     double elapsed;
+-- 
+1.6.3.3
+
+
diff --git a/test/corpus/33 b/test/corpus/33
new file mode 100644 (file)
index 0000000..a9b3252
--- /dev/null
@@ -0,0 +1,13 @@
+From: "Rolland Santimano" <rollandsantimano@yahoo.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 21:12:23 -0800 (PST)
+Subject: [notmuch] Link to mailing list archives ?
+Message-ID: <736613.51770.qm@web113505.mail.gq1.yahoo.com>
+
+The link[1] provided on the list page[2] is broken:
+[1] http://notmuchmail.org/pipermail/notmuch/
+[2] http://notmuchmail.org/mailman/listinfo/notmuch
+
+
+      
+
diff --git a/test/corpus/34 b/test/corpus/34
new file mode 100644 (file)
index 0000000..b94dd06
--- /dev/null
@@ -0,0 +1,46 @@
+From: "Alexander Botero-Lowry" <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 21:45:36 -0800
+Subject: [notmuch] Mac OS X/Darwin compatibility issues
+In-Reply-To: <ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com>
+References: <ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com>
+Message-ID: <86einw2xof.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+
+On Wed, 18 Nov 2009 11:50:17 +0800, Jjgod Jiang <gzjjgod at gmail.com> wrote:
+> Hi,
+> 
+> When I tried to compile notmuch under Mac OS X 10.6, several issues
+> arisen:
+> 
+> 1. g++ reports 'warning: command line option "-Wmissing-declarations"
+> is valid for C/ObjC but not for C++'
+> 
+I got that too. I presume it's newly supported in GCC4.4?
+
+> 3. Several errors about missing GNU extensions like getline() and strndup():
+> 
+strndup from V8:
+
+char* strndup(char* str, size_t n) {
+  // Stupid implementation of strndup since macos isn't born with
+  // one.
+  size_t len = strlen(str);
+  if (len <= n)
+    return StrDup(str);
+  char* result = new char[n+1];
+  size_t i;
+  for (i = 0; i <= n; i++)
+    result[i] = str[i];
+  result[i] = '\0';
+  return result;
+}
+
+> warning: implicit declaration of function ?getline?
+> error: ?strndup? was not declared in this scope
+> 
+for getline do you mind trying #define _GNU_SOURCE 1
+before #include <stdio.h> in the offending files? The FreeBSD man pages
+mentions that as a way of enabling the GNU version of getline().
+
+Alex
+
diff --git a/test/corpus/35 b/test/corpus/35
new file mode 100644 (file)
index 0000000..d727670
--- /dev/null
@@ -0,0 +1,24 @@
+From: "Jjgod Jiang" <gzjjgod@gmail.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 14:14:27 +0800
+Subject: [notmuch] Mac OS X/Darwin compatibility issues
+In-Reply-To: <86einw2xof.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+References: <ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com>
+       <86einw2xof.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+Message-ID: <ddd65cda0911172214t60d22b63hcfeb5a19ab54a39b@mail.gmail.com>
+
+Hi,
+
+On Wed, Nov 18, 2009 at 1:45 PM, Alexander Botero-Lowry
+<alex.boterolowry at gmail.com> wrote:
+> for getline do you mind trying #define _GNU_SOURCE 1
+> before #include <stdio.h> in the offending files? The FreeBSD man pages
+> mentions that as a way of enabling the GNU version of getline().
+
+It seems even _GNU_SOURCE is defined, getline is still not present.
+the C lib in Mac OS X simply doesn't have it. See also [1].
+
+- Jiang
+
+[1] http://stackoverflow.com/questions/1117108/compiling-c-code-using-gnu-c-getline-on-mac-osx
+
diff --git a/test/corpus/36 b/test/corpus/36
new file mode 100644 (file)
index 0000000..4cd0d20
--- /dev/null
@@ -0,0 +1,25 @@
+From: "Alexander Botero-Lowry" <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 22:19:29 -0800
+Subject: [notmuch] Mac OS X/Darwin compatibility issues
+In-Reply-To: <ddd65cda0911172214t60d22b63hcfeb5a19ab54a39b@mail.gmail.com>
+References: <ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com>
+       <86einw2xof.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+       <ddd65cda0911172214t60d22b63hcfeb5a19ab54a39b@mail.gmail.com>
+Message-ID: <86d43g2w3y.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+
+On Wed, 18 Nov 2009 14:14:27 +0800, Jjgod Jiang <gzjjgod at gmail.com> wrote:
+> Hi,
+> 
+> On Wed, Nov 18, 2009 at 1:45 PM, Alexander Botero-Lowry
+> <alex.boterolowry at gmail.com> wrote:
+> > for getline do you mind trying #define _GNU_SOURCE 1
+> > before #include <stdio.h> in the offending files? The FreeBSD man pages
+> > mentions that as a way of enabling the GNU version of getline().
+> 
+> It seems even _GNU_SOURCE is defined, getline is still not present.
+> the C lib in Mac OS X simply doesn't have it. See also [1].
+> 
+Alas. Since it's ostensibly based on the FreeBSD one, I figured there
+was a chance that would fix the problem. :/
+
diff --git a/test/corpus/37 b/test/corpus/37
new file mode 100644 (file)
index 0000000..4e17e82
--- /dev/null
@@ -0,0 +1,22 @@
+From: "Alexander Botero-Lowry" <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 00:02:56 -0800
+Subject: [notmuch] request for pull
+Message-ID: <86aayk2rbj.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+
+The following changes since commit e8c9c3e6a534fc6c2919c2c1de63cea7250eb488:
+  Ingmar Vanhassel (1):
+        Makefile: Manual pages shouldn't be executable
+
+are available in the git repository at:
+
+  git://alexbl.net/notmuch.git master
+
+Alexander Botero-Lowry (2):
+      Error out if no query is supplied to search instead of going into an infinite loop
+      set a local truncate-line variable in notmuch-search-mode, so that subjects don't wrap and make the output look weird
+
+ notmuch-search.c |    5 +++++
+ notmuch.el       |    1 +
+ 2 files changed, 6 insertions(+), 0 deletions(-)
+
diff --git a/test/corpus/38 b/test/corpus/38
new file mode 100644 (file)
index 0000000..f5537ff
--- /dev/null
@@ -0,0 +1,40 @@
+From: "Keith Packard" <keithp@keithp.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 00:29:59 -0800
+Subject: [notmuch] [PATCH] Create a default notmuch-show-hook that
+       highlights URLs and uses word-wrap
+Message-ID: <1258532999-9316-1-git-send-email-keithp@keithp.com>
+
+I created the notmuch-show-hook precisely so I could add these two
+options, but I suspect most people will want them, so I just made them
+the default. If you don't want them, you can use remove-hook to get
+rid of this.
+
+Signed-off-by: Keith Packard <keithp at keithp.com>
+---
+ notmuch.el |    8 ++++++++
+ 1 files changed, 8 insertions(+), 0 deletions(-)
+
+diff --git a/notmuch.el b/notmuch.el
+index 1bb1294..c95cb43 100644
+--- a/notmuch.el
++++ b/notmuch.el
+@@ -698,6 +698,14 @@ view, (remove the \"inbox\" tag from each), with either
+   :options '(goto-address)
+   :group 'notmuch)
++; Make show mode a bit prettier, highlighting URLs and using word wrap
++
++(defun notmuch-show-pretty-hook ()
++  (goto-address-mode 1)
++  (visual-line-mode))
++
++(add-hook 'notmuch-show-hook 'notmuch-show-pretty-hook)
++
+ (defun notmuch-show (thread-id &optional parent-buffer)
+   "Run \"notmuch show\" with the given thread ID and display results.
+-- 
+1.6.5.2
+
+
diff --git a/test/corpus/39 b/test/corpus/39
new file mode 100644 (file)
index 0000000..637b3c7
--- /dev/null
@@ -0,0 +1,32 @@
+From: "Alexander Botero-Lowry" <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 00:52:34 -0800
+Subject: [notmuch] [PATCH] Create a default notmuch-show-hook that
+ highlights URLs and uses word-wrap
+In-Reply-To: <1258532999-9316-1-git-send-email-keithp@keithp.com>
+References: <1258532999-9316-1-git-send-email-keithp@keithp.com>
+Message-ID: <867hto2p0t.fsf@fortitudo.i-did-not-set--mail-host-address--so-tickle-me>
+
+On Wed, 18 Nov 2009 00:29:59 -0800, Keith Packard <keithp at keithp.com> wrote:
+> I created the notmuch-show-hook precisely so I could add these two
+> options, but I suspect most people will want them, so I just made them
+> the default. If you don't want them, you can use remove-hook to get
+> rid of this.
+> 
+Yes, hooks should be added for search as well. :)
+
+> +; Make show mode a bit prettier, highlighting URLs and using word wrap
+> +
+> +(defun notmuch-show-pretty-hook ()
+> +  (goto-address-mode 1)
+> +  (visual-line-mode))
+> +
+visual-line-mode turns out to make subject look pretty ugly if there is a
+continuation. It doesn't do much good for the citation headers
+either. We probably need to do our own intelligent wrapping rather then
+use visual-line-mode to make this actually look right.
+
+goto-address-mode is important though. :)
+
+alex
+
diff --git a/test/corpus/40 b/test/corpus/40
new file mode 100644 (file)
index 0000000..91a15a8
--- /dev/null
@@ -0,0 +1,31 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 01:42:02 -0800
+Subject: [notmuch] [PATCH 1/2] Close message file after parsing message
+ headers
+In-Reply-To: <yunbpj0etua.fsf@aiko.keithp.com>
+References: <1258471718-6781-1-git-send-email-dottedmag@dottedmag.net>
+       <87lji5cbwo.fsf@yoom.home.cworth.org> <yunbpj0etua.fsf@aiko.keithp.com>
+Message-ID: <87pr7gqidx.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 13:15:25 -0800, Keith Packard <keithp at keithp.com> wrote:
+> Threading the message also involves displaying the from and to contents,
+> which requires opening the message file. The alternative to the fix I
+> provided is to just parse all of the message headers when first opening
+> the message; it could then be immediately closed and the hash referred
+> to for all header data. Given the choice, just having the caller say
+> when it has finished with a message is probably a reasonable option...
+
+Hi Keith,
+
+Once I finally got back on the ground again, I pushed out a revised
+version of your patch, (didn't need the reply-to stuff anymore since I
+had fixed that differently in the meantime).
+
+I'm pretty happy with the state of this portion of the code now.
+
+Thanks Keith and Mikhail for your input on and code to fix this bug.
+
+-Carl
+
+
diff --git a/test/corpus/41 b/test/corpus/41
new file mode 100644 (file)
index 0000000..da22cc0
--- /dev/null
@@ -0,0 +1,37 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:08:10 -0800
+Subject: [notmuch] Working with Maildir storage?
+In-Reply-To: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+References: <20091117190054.GU3165@dottiness.seas.harvard.edu>
+Message-ID: <87ocn0qh6d.fsf@yoom.home.cworth.org>
+
+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/corpus/42 b/test/corpus/42
new file mode 100644 (file)
index 0000000..98fa75f
--- /dev/null
@@ -0,0 +1,30 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:19:26 -0800
+Subject: [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands
+ remove inbox (and unread) tags
+In-Reply-To: <1258493565-13508-1-git-send-email-keithp@keithp.com>
+References: <1258493565-13508-1-git-send-email-keithp@keithp.com>
+Message-ID: <87k4xoqgnl.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 13:32:45 -0800, Keith Packard <keithp at keithp.com> wrote:
+> When closing a thread view, mark the thread as archived by removing
+> the "inbox" tag, and for the 'x' variant, the "unread" tag as well,
+> then kill the buffer and update the search window view as well.
+> 
+> This makes 'x' much the same as 'a', but instead of taking you to the
+> next message, it takes you back to the search window instead.
+
+I don't like this---but that's because I use 'x' precisely *because* it
+preserves these tags.
+
+Otherwise, you might as well just remove inbox and unread as soon as the
+message is presented to the user. And that's a bug in a lot of other
+email programs that I'm unwilling to replicate.
+
+We may run into a need to define different ways that people like to work
+with their email here. (I know that so far I've just been coding up the
+way I want my mail to work.)
+
+-Carl
+
diff --git a/test/corpus/43 b/test/corpus/43
new file mode 100644 (file)
index 0000000..2f6c8bc
--- /dev/null
@@ -0,0 +1,26 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:22:12 -0800
+Subject: [notmuch] archive
+In-Reply-To: <yunzl6kd1w0.fsf@aiko.keithp.com>
+References: <20091117232137.GA7669@griffis1.net>
+       <yunzl6kd1w0.fsf@aiko.keithp.com>
+Message-ID: <87iqd8qgiz.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 18:04:31 -0800, Keith Packard <keithp at keithp.com> wrote:
+> On Tue, 17 Nov 2009 18:21:38 -0500, Aron Griffis <agriffis at n01se.net> wrote:
+> 
+> > Just subscribed, I'd like to catch up on the previous postings,
+> > but the archive link seems to be bogus?
+> 
+> Yeah, the archive appears broken and will need to wait until Carl
+> arrives in Barcelona to get fixed.
+
+Fixed it in transit in Frankfurt---with only moments to spare on my
+battery and no outlets in sight.
+
+Thanks for the report, Aron. And welcome to notmuch!
+
+-Carl (who wants to reply to a lot more mail, but will have to wait
+ until later for that)
+
diff --git a/test/corpus/44 b/test/corpus/44
new file mode 100644 (file)
index 0000000..c896c18
--- /dev/null
@@ -0,0 +1,29 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:43:50 -0800
+Subject: [notmuch] [PATCH] Older versions of install do not support -C.
+In-Reply-To: <1258496327-12086-1-git-send-email-jan@ryngle.com>
+References: <1258496327-12086-1-git-send-email-jan@ryngle.com>
+Message-ID: <87hbssqfix.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 23:18:47 +0100, Jan Janak <jan at ryngle.com> wrote:
+> Do not use -C cmdline option of install, older versions, commonly found in
+> distributions like Debian, do not seem to support it. Running make install
+> on such systems (tested on Debian Lenny) fails.
+> 
+> Signed-off-by: Jan Janak <jan at ryngle.com>
+
+Thanks, Jan. This is pushed now.
+
+And did I say welcome to notmuch yet? (It's easy to lose track with all
+the newcomers---which I'm not complaining about---especially since so
+many are sharing code.)
+
+-Carl
+
+PS. I actually really like the behavior of -C (especially when
+installing a low-level library to avoid big waterfalls of needless
+recompiles). But since we're *not* actually installing a library (yet)
+I'm happy with this patch rather than writing code in configure to check
+if "install -C" works or not.
+
diff --git a/test/corpus/45 b/test/corpus/45
new file mode 100644 (file)
index 0000000..806b0e8
--- /dev/null
@@ -0,0 +1,41 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 02:49:52 -0800
+Subject: [notmuch] What a great idea!
+In-Reply-To: <f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com>
+References: <f35dbb950911171435ieecd458o853c873e35f4be95@mail.gmail.com>
+Message-ID: <87fx8cqf8v.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 23:35:30 +0100, Jan Janak <jan at ryngle.com> wrote:
+> First of all, notmuch is a wonderful idea, both the cmdline tool and
+> the emacs interface! Thanks a lot for writing it, I was really excited
+> when I read the announcement today.
+
+Ah, here's where I planned a nice welcome. So welcome (again), Jan! :-)
+
+I've been having a lot of fun with notmuch already, (though there have
+been some days of pain before it was functional enough and my
+email-reply latency went way up). But regardless---I got through that,
+and I'm able to work more efficiently with notmuch now than I could with
+sup before. So I'm happy.
+
+And I'm delighted when other people find this interesting as well.
+
+> Have you considered sending an announcement to the org-mode mailing list?
+> http://orgmode.org
+
+Thanks for the idea. I think I may have looked into org-mode years ago,
+(when I was investigating planner-mode and various emacs "personal wiki"
+systems for keeping random notes and what-not).
+
+> Various ways of searching/referencing emails from emacs were discussed
+> there several times and none of them were as elegant as notmuch (not
+> even close). Maybe notmuch would attract some of the developers
+> there..
+
+Yeah. I'll drop them a mail. Having a real emacs wizard on board would
+be nice. (I'm afraid the elisp I've written so far for this project is
+fairly grim.)
+
+-Carl
+
diff --git a/test/corpus/46 b/test/corpus/46
new file mode 100644 (file)
index 0000000..bbd1b37
--- /dev/null
@@ -0,0 +1,57 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 03:02:43 -0800
+Subject: [notmuch] New to the list
+In-Reply-To: <1258498485-sup-142@elly>
+References: <1258498485-sup-142@elly>
+Message-ID: <87bpj0qeng.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 23:57:18 +0100, Israel Herraiz <isra at herraiz.org> wrote:
+> I have subscribed to the list. As suggested by the welcome message, I
+> am introducing myself. My name is Israel Herraiz, and I have done a
+> couple of contributions to Sup, the probably well-known here e-mail
+> client.
+
+Welcome, Israel!
+
+I'm glad people read that little bit of text in the welcome email and
+are introducing themselves. I like to think of our new notmuch community
+as a very personable place.
+
+> "Not much" sounds interesting, and I wonder whether it could be
+> integrated with the views of Sup (inbox, threads, etc). So I have
+> subscribed to the list to keep an eye on what's going on here.
+> 
+> I have just heard of "Not much". I have not even tried to download the
+> code yet.
+
+Yes, take a look. If you're already an emacs user, then you'll find the
+interface of notmuch very comfortable, (looks a lot like sup, but lives
+inside of emacs). Even outside of emacs, the command line interface of
+notmuch gives view *fairly* similar to those of sup:
+
+    notmuch search tag:inbox           # Very much like sup's inbox
+
+    notmuch show thread:some-thread-id # A lot like sup's thread -view
+
+The command-line output right now isn't nearly as neat as sup's, (it
+doesn't elide comments--it doesn't do the indenting of threads, etc.),
+even though the command-line interface has all the information it needs
+to do that. The reason for that is to let the emacs code own most of the
+formatting, (so that it can be more flexible--such as making hidden
+things visible, changing column widths, etc.).
+
+But one thing I wonder is if there would be situations where it would
+make sense to get the cleaner output directly out of the command-line
+tool.
+
+For example, for someone who isn't an emacs user, the command-line
+interface might be their only introduction to what the "notmuch
+experience" is like. So maybe "notmuch show" should give nice clean
+output by default and then the emacs code could call "notmuch show
+--format=emacs-friendly" or something to get the current output.
+
+That's an idea anyway.
+
+-Carl
+
diff --git a/test/corpus/47 b/test/corpus/47
new file mode 100644 (file)
index 0000000..9de5532
--- /dev/null
@@ -0,0 +1,84 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 03:15:31 -0800
+Subject: [notmuch] Introducing myself
+In-Reply-To: <20091118002059.067214ed@hikari>
+References: <20091118002059.067214ed@hikari>
+Message-ID: <87aaykqe24.fsf@yoom.home.cworth.org>
+
+On Wed, 18 Nov 2009 00:20:59 +0100, Adrian Perez de Castro <aperez at igalia.com> wrote:
+> I have just heard about Not Much today in some random Linux-related news
+> site (LWN?), my name is Adrian Perez and I work as systems administrator
+
+Welcome to notmuch, Adrian! We're glad to have you here.
+
+> by default on most distribution. I got to have some mailboxes indexed and
+> basic searching working a couple of months ago. Lately I have been very
+> busy and had no time for coding, and them... boom! Not Much appears -- and
+> it is almost exactly what I was trying to do, but faster. I have been
+> playing a bit with Not Much today, and I think it has potential.
+
+It's funny, because I had the exact same experience with sup a couple of
+months ago. I had been frustrated for years with email programs, and had
+been thinking about how I'd like things to work n the back of my mind
+for a long time, (but never *quite* getting to the point where I would
+commit to writing an email system myself).
+
+And then... boom! I found sup and was instantly hooked. It had so much
+of what I had imagined, (and much of what I hadn't yet imagined) that I
+was quite delighted.
+
+It was really quite by accident that I ended up inventing a different
+system. I had started out just trying to speedup index creation for sup.
+If I hadn't run into the problem that it was very difficult[*] to create a
+sup-compatible index from C code, I might have stopped there.
+
+So I'd written a bunch of functional code, only to find myself stuck at
+the very last step, (hooking it up to the existing sup interface). Then
+Keith suggested emacs and it all seemed pretty easy since I'd already
+done all the Xapian work. So it's funny, I was only willing to commit to
+this project because I wasn't consciously aware I was working on it.
+Otherwise it would have seemed to overwhelming to start. :-)
+
+Anyway, that's a lot of off-topic rambling off of your introduction. But
+I'm glad that notmuch can now give that same "boom!" to others, and I'm
+glad you see potential in it.
+
+> Also, I would like to share one idea I had in mind, that you might find
+> interesting: One thing I have found very annoying is having to re-tag my
+> mail when the indexes get b0rked (it happened a couple of times to me while
+> using Sup), so I was planning to mails as read/unread and adding the tags
+> not just to the index, but to the mail text itself, e.g. by adding a
+> "X-Tags" header field or by reusing the "Keywords" one. This way, the index
+> could be totally recreated by re-reading the mail directories, and this
+> would also allow to a tools like OfflineIMAP [1] to get the mails into a
+> local maildir, tagging and indexing the mails with the e-mail reader and
+> then syncing back the messages with the "X-Tags" header to the IMAP server.
+> This would allow to use the mail reader from a different computer and still
+> have everything tagged finely.
+
+It is an interesting idea. But there's also something really comforting
+about the email indexed never modifying the mail files. If you're
+reading the notmuch commit logs closely you'll see that I'm not actually
+careful enough to be trusted with your mail (but I try). So I like that
+I don't even have to trust myself---the worst that happens is that I
+have to recreate my index.
+
+And as Keith mentioned, we've got the "notmuch dump; notmuch restore"
+idea working exactly as it did in sup. (Though I am thinking of also
+adding thread IDs to that now---more on that later.)
+
+The big annoyance I had with sup index creation, (I ended up having to
+do it more than once too), was that it takes *forever*. Right now,
+notmuch is a little bit faster, but not a lot faster. And I've got some
+ideas to fix that. It would be really nice if index creation were pain
+free. (And maybe it is for some user with small amounts of mail---oh, to
+have only 40000 messages to have to index!).
+
+-Carl
+
+[*] The problem here is that sup puts serialized ruby data structures
+into the data field of its Xapian documents. So being compatible with
+sup means being able to recreate serialized data structures for a
+particular version of ruby.
+
diff --git a/test/corpus/48 b/test/corpus/48
new file mode 100644 (file)
index 0000000..419e21d
--- /dev/null
@@ -0,0 +1,17 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 03:22:32 -0800
+Subject: [notmuch] [PATCH] Typsos
+In-Reply-To: <1258500222-32066-1-git-send-email-ingmar@exherbo.org>
+References: <1258500222-32066-1-git-send-email-ingmar@exherbo.org>
+Message-ID: <878we4qdqf.fsf@yoom.home.cworth.org>
+
+On Wed, 18 Nov 2009 00:23:42 +0100, Ingmar Vanhassel <ingmar at exherbo.org> wrote:
+>  17 files changed, 30 insertions(+), 30 deletions(-)
+
+Yikes. That's a lot of typos.
+
+Thanks Ingmar, for cleaning up after my sloppy keyboarding. Pushed.
+
+-Carl
+
diff --git a/test/corpus/49 b/test/corpus/49
new file mode 100644 (file)
index 0000000..b244f8c
--- /dev/null
@@ -0,0 +1,33 @@
+From: "Carl Worth" <cworth@cworth.org>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 03:31:23 -0800
+Subject: [notmuch] [PATCH] Error out if no query is supplied to search
+ instead of going into an infinite loop
+In-Reply-To: <cf0c4d610911171623q3e27a0adx802e47039b57604b@mail.gmail.com>
+References: <cf0c4d610911171623q3e27a0adx802e47039b57604b@mail.gmail.com>
+Message-ID: <877htoqdbo.fsf@yoom.home.cworth.org>
+
+On Tue, 17 Nov 2009 16:23:53 -0800, Alex Botero-Lowry <alex.boterolowry at gmail.com> wrote:
+> In this case error out when no query is supplied. There seems to be an
+> infinite-loop casued by i think notmuch_query_search_threads having
+> an exception:
+> A Xapian exception occurred: Syntax: <expression> AND <expression>
+> A Xapian exception occurred: Syntax: <expression> AND <expression>
+> A Xapian exception occurred: Syntax: <expression> AND <expression>
+> 
+> I'll look into that bug specifically a bit later.
+> 
+> It might be better to do a usage instead of just throwing an error here?
+
+Definitely.
+
+Priit Laes reported the same thing in IRC and I've just committed a
+patch to give a nice error message:
+
+$ ./notmuch search
+Error: notmuch search requires at least one search term.
+
+Thanks for the report!
+
+-Carl
+
diff --git a/test/corpus/50 b/test/corpus/50
new file mode 100644 (file)
index 0000000..44e8be5
--- /dev/null
@@ -0,0 +1,39 @@
+From: "Chris Wilson" <chris@chris-wilson.co.uk>
+To: notmuch@notmuchmail.org
+Date: Wed, 18 Nov 2009 11:34:54 +0000
+Subject: [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once
+Message-ID: <1258544095-16616-1-git-send-email-chris@chris-wilson.co.uk>
+
+Currently the same `pkg-config ...` is executed for every target, so
+just store the results in a variable.
+---
+ Makefile |    9 +++++----
+ 1 files changed, 5 insertions(+), 4 deletions(-)
+
+diff --git a/Makefile b/Makefile
+index 96aaa73..023b2ec 100644
+--- a/Makefile
++++ b/Makefile
+@@ -4,15 +4,16 @@ CFLAGS=-O2
+ # Additional flags that we will append to whatever the user set.
+ # These aren't intended for the user to manipulate.
+-extra_cflags = `pkg-config --cflags glib-2.0 gmime-2.4 talloc`
+-extra_cxxflags = `xapian-config --cxxflags`
++extra_cflags := $(shell pkg-config --cflags glib-2.0 gmime-2.4 talloc)
++extra_cxxflags := $(shell xapian-config --cxxflags)
+ # Now smash together user's values with our extra values
+ override CFLAGS += $(WARN_FLAGS) $(extra_cflags)
+ override CXXFLAGS += $(WARN_FLAGS) $(extra_cflags) $(extra_cxxflags)
+-override LDFLAGS += `pkg-config --libs glib-2.0 gmime-2.4 talloc` \
+-                      `xapian-config --libs`
++override LDFLAGS += \
++      $(shell pkg-config --libs glib-2.0 gmime-2.4 talloc) \
++      $(shell xapian-config --libs)
+ # Include our local Makefile.local first so that its first target is default
+ include Makefile.local
+-- 
+1.6.5.2
diff --git a/test/dump-restore b/test/dump-restore
new file mode 100755 (executable)
index 0000000..0d78f01
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+test_description="\"notmuch dump\" and \"notmuch restore\""
+. ./test-lib.sh
+
+add_email_corpus
+
+test_expect_success "Dumping all tags" "generate_message &&
+notmuch new &&
+notmuch dump dump.expected"
+
+test_begin_subtest "Clearing all tags"
+sed -e "s/(\([^(]*\))$/()/" < dump.expected > clear.expected
+notmuch restore clear.expected
+notmuch dump clear.actual
+test_expect_equal "$(< clear.actual)" "$(< clear.expected)"
+
+test_begin_subtest "Restoring original tags"
+notmuch restore dump.expected
+notmuch dump dump.actual
+test_expect_equal "$(< dump.actual)" "$(< dump.expected)"
+
+test_expect_success "Restore with nothing to do" "notmuch restore dump.expected"
+
+test_done
diff --git a/test/emacs b/test/emacs
new file mode 100755 (executable)
index 0000000..fb61e9a
--- /dev/null
@@ -0,0 +1,127 @@
+#!/bin/bash
+test_description="emacs interface"
+. test-lib.sh
+
+EXPECTED=../emacs.expected-output
+
+add_email_corpus
+
+test_begin_subtest "Basic notmuch-hello view in emacs"
+output=$(test_emacs '(notmuch-hello) (message (buffer-string))' 2>&1)
+expected=$(cat $EXPECTED/notmuch-hello)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Saved search with 0 results"
+output=$(test_emacs '(setq notmuch-show-empty-saved-searches t) (setq notmuch-saved-searches '\''(("inbox" . "tag:inbox") ("unread" . "tag:unread") ("empty" . "tag:doesnotexist"))) (notmuch-hello) (message (buffer-string))' 2>&1)
+expected=$(cat $EXPECTED/notmuch-hello-with-empty)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "No saved searches displayed (all with 0 results)"
+output=$(test_emacs '(setq notmuch-saved-searches '\''(("empty" . "tag:doesnotexist"))) (notmuch-hello) (message (buffer-string))' 2>&1)
+expected=$(cat $EXPECTED/notmuch-hello-no-saved-searches)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Basic notmuch-search view in emacs"
+output=$(test_emacs '(notmuch-search "tag:inbox") (notmuch-test-wait) (message (buffer-string))' 2>&1)
+expected=$(cat $EXPECTED/notmuch-search-tag-inbox)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Navigation of notmuch-hello to search results"
+output=$(test_emacs '(notmuch-hello) (goto-char (point-min)) (re-search-forward "inbox") (widget-button-press (point)) (notmuch-test-wait) (message (buffer-string))' 2>&1)
+expected=$(cat $EXPECTED/notmuch-hello-view-inbox)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Basic notmuch-show view in emacs"
+maildir_storage_thread=$(notmuch search --output=threads id:20091117190054.GU3165@dottiness.seas.harvard.edu)
+output=$(test_emacs "(notmuch-show \"$maildir_storage_thread\") (message (buffer-string))" 2>&1)
+expected=$(cat $EXPECTED/notmuch-show-thread-maildir-storage)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Navigation of notmuch-search to thread view"
+output=$(test_emacs '(notmuch-search "tag:inbox") (notmuch-test-wait) (goto-char (point-min)) (re-search-forward "Working with Maildir") (notmuch-search-show-thread) (notmuch-test-wait) (message (buffer-string))' 2>&1)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "Add tag from search view"
+os_x_darwin_thread=$(notmuch search --output=threads id:ddd65cda0911171950o4eea4389v86de9525e46052d3@mail.gmail.com)
+test_emacs "(notmuch-search \"$os_x_darwin_thread\") (notmuch-test-wait) (notmuch-search-add-tag \"tag-from-search-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-search-view unread)"
+
+test_begin_subtest "Remove tag from search view"
+test_emacs "(notmuch-search \"$os_x_darwin_thread\") (notmuch-test-wait) (notmuch-search-remove-tag \"tag-from-search-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "Add tag from notmuch-show view"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\") (notmuch-show-add-tag \"tag-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox tag-from-show-view unread)"
+
+test_begin_subtest "Remove tag from notmuch-show view"
+test_emacs "(notmuch-show \"$os_x_darwin_thread\") (notmuch-show-remove-tag \"tag-from-show-view\")"
+output=$(notmuch search $os_x_darwin_thread | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
+
+test_begin_subtest "Message with .. in Message-Id:"
+add_message [id]=123..456@example '[subject]="Message with .. in Message-Id"'
+test_emacs '(notmuch-search "id:\"123..456@example\"") (notmuch-test-wait) (notmuch-search-add-tag "search-add") (notmuch-search-add-tag "search-remove") (notmuch-search-remove-tag "search-remove") (notmuch-show "id:\"123..456@example\"") (notmuch-test-wait) (notmuch-show-add-tag "show-add") (notmuch-show-add-tag "show-remove") (notmuch-show-remove-tag "show-remove")'
+output=$(notmuch search 'id:"123..456@example"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Message with .. in Message-Id (inbox search-add show-add)"
+
+test_begin_subtest "Sending a message via (fake) SMTP"
+
+# Before we can send a message, we have to prepare the FCC maildir
+mkdir -p mail/sent/cur
+mkdir -p mail/sent/new
+mkdir -p mail/sent/tmp
+
+../smtp-dummy sent_message &
+smtp_dummy_pid=$!
+test_emacs "(setq message-send-mail-function 'message-smtpmail-send-it) (setq smtpmail-smtp-server \"localhost\") (setq smtpmail-smtp-service \"25025\") (notmuch-hello) (notmuch-mua-mail) (message-goto-to) (insert \"user@example.com\nDate: Fri, 29 Mar 1974 10:00:00 -0000\") (message-goto-subject) (insert \"Testing message sent via SMTP\") (message-goto-body) (insert \"This is a test that messages are sent via SMTP\") (message-send-and-exit)" >/dev/null 2>&1
+wait ${smtp_dummy_pid}
+
+output=$(sed \
+    -e s',^User-Agent: Notmuch/.* Emacs/.*,User-Agent: Notmuch/XXX Emacs/XXX,' \
+    -e s',^Message-ID: <.*>$,Message-ID: <XXX>,' < sent_message)
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: user@example.com
+Subject: Testing message sent via SMTP
+Date: Fri, 29 Mar 1974 10:00:00 -0000
+User-Agent: Notmuch/XXX Emacs/XXX
+Message-ID: <XXX>
+MIME-Version: 1.0
+Content-Type: text/plain; charset=us-ascii
+
+This is a test that messages are sent via SMTP"
+
+test_begin_subtest "Verify that sent messages are saved/searchable (via FCC)"
+notmuch new > /dev/null
+output=$(notmuch search 'subject:"testing message sent via SMTP"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   1974-03-29 [1/1] Notmuch Test Suite; Testing message sent via SMTP (inbox unread)"
+
+test_begin_subtest "Reply within emacs"
+# We sed away everything before the ^From in the output to avoid getting
+# confused by messages such as "Parsing /home/cworth/.mailrc... done"
+output=$(test_emacs '(notmuch-search "subject:\"testing message sent via SMTP\"") (notmuch-test-wait) (notmuch-search-reply-to-thread) (message (buffer-string))' 2>&1 | sed -ne '/^From/,$ p' | sed -e 's/^In-Reply-To: <.*>$/In-Reply-To: <XXX>/')
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: user@example.com
+Subject: Re: Testing message sent via SMTP
+In-Reply-To: <XXX>
+Fcc: $(pwd)/mail/sent
+--text follows this line--
+On Fri, 29 Mar 1974 10:00:00 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> This is a test that messages are sent via SMTP"
+
+test_begin_subtest "Save attachment from within emacs"
+echo "./attachment" | test_emacs '(notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com") (notmuch-show-save-attachments)' > /dev/null 2>&1
+output=$(cat attachment)
+expected=$(cat $EXPECTED/attachment)
+test_expect_equal "$output" "$expected"
+
+test_begin_subtest "View raw message within emacs"
+expected=$(cat $EXPECTED/raw-message-cf0c4d-52ad0a)
+first_line=$(echo "$expected" | head -n1)
+output=$(test_emacs '(notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com") (notmuch-show-view-raw-message) (message (buffer-string))' 2>&1 | sed -ne "/$first_line/,\$ p")
+test_expect_equal "$output" "$expected"
+
+test_done
diff --git a/test/emacs.expected-output/attachment b/test/emacs.expected-output/attachment
new file mode 100644 (file)
index 0000000..1e22d3a
--- /dev/null
@@ -0,0 +1,32 @@
+From e3bc4bbd7b9d0d086816ab5f8f2d6ffea1dd3ea4 Mon Sep 17 00:00:00 2001
+From: Alexander Botero-Lowry <alex.boterolowry@gmail.com>
+Date: Tue, 17 Nov 2009 11:30:39 -0800
+Subject: [PATCH] Deal with situation where sysconf(_SC_GETPW_R_SIZE_MAX) returns -1
+
+---
+ notmuch-config.c |    2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/notmuch-config.c b/notmuch-config.c
+index 248149c..e7220d8 100644
+--- a/notmuch-config.c
++++ b/notmuch-config.c
+@@ -77,6 +77,7 @@ static char *
+ get_name_from_passwd_file (void *ctx)
+ {
+     long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
++    if (pw_buf_size == -1) pw_buf_size = 64;
+     char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
+     struct passwd passwd, *ignored;
+     char *name;
+@@ -101,6 +102,7 @@ static char *
+ get_username_from_passwd_file (void *ctx)
+ {
+     long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
++    if (pw_buf_size == -1) pw_buf_size = 64;
+     char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
+     struct passwd passwd, *ignored;
+     char *name;
+-- 
+1.6.5.2
+
diff --git a/test/emacs.expected-output/notmuch-hello b/test/emacs.expected-output/notmuch-hello
new file mode 100644 (file)
index 0000000..64b7e42
--- /dev/null
@@ -0,0 +1,14 @@
+   Welcome to notmuch. You have 50 messages.
+
+Saved searches: [edit]
+
+         50 inbox           50 unread    
+
+Search:                                                                     
+
+[Show all tags]
+
+        Type a search query and hit RET to view matching threads.
+               Edit saved searches with the `edit' button.
+  Hit RET or click on a saved search or tag name to view matching threads.
+    `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
diff --git a/test/emacs.expected-output/notmuch-hello-no-saved-searches b/test/emacs.expected-output/notmuch-hello-no-saved-searches
new file mode 100644 (file)
index 0000000..7f8206a
--- /dev/null
@@ -0,0 +1,10 @@
+   Welcome to notmuch. You have 50 messages.
+
+Search:                                                                     
+
+[Show all tags]
+
+        Type a search query and hit RET to view matching threads.
+               Edit saved searches with the `edit' button.
+  Hit RET or click on a saved search or tag name to view matching threads.
+    `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
diff --git a/test/emacs.expected-output/notmuch-hello-view-inbox b/test/emacs.expected-output/notmuch-hello-view-inbox
new file mode 100644 (file)
index 0000000..3ba2b29
--- /dev/null
@@ -0,0 +1,23 @@
+  2009-11-17 [5/5]   Carl Worth, Keith Packard, Mikhail Gusarov [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-17 [7/7]   Carl Worth, Lars Kellogg-Stedman, Keith Packard, Mikhail Gusarov [notmuch] Working with Maildir storage? (inbox unread)
+  2009-11-17 [2/2]   Carl Worth, Alex Botero-Lowry [notmuch] preliminary FreeBSD support (attachment inbox unread)
+  2009-11-17 [1/1]   Mikhail Gusarov      [notmuch] [PATCH] Handle rename of message file (inbox unread)
+  2009-11-17 [2/2]   Carl Worth, Keith Packard [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-17 [2/2]   Carl Worth, Jan Janak [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-17 [3/3]   Carl Worth, Jan Janak [notmuch] What a great idea! (inbox unread)
+  2009-11-17 [3/3]   Carl Worth, Keith Packard, Israel Herraiz [notmuch] New to the list (inbox unread)
+  2009-11-17 [3/3]   Carl Worth, Keith Packard, Adrian Perez de Castro [notmuch] Introducing myself (inbox unread)
+  2009-11-17 [3/3]   Carl Worth, Keith Packard, Aron Griffis [notmuch] archive (inbox unread)
+  2009-11-17 [2/2]   Carl Worth, Ingmar Vanhassel [notmuch] [PATCH] Typsos (inbox unread)
+  2009-11-18 [2/2]   Carl Worth, Alex Botero-Lowry [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18 [2/2]   Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr? (attachment inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] count_files: sort directory in inode order before statting (inbox unread)
+  2009-11-18 [4/4]   Alexander Botero-Lowry, Jjgod Jiang [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
+  2009-11-18 [1/1]   Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (inbox unread)
+  2009-11-18 [1/1]   Rolland Santimano    [notmuch] Link to mailing list archives ? (inbox unread)
+  2009-11-18 [1/1]   Alexander Botero-Lowry [notmuch] request for pull (inbox unread)
+  2009-11-18 [2/2]   Alexander Botero-Lowry, Keith Packard [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18 [1/1]   Chris Wilson         [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread)
+End of search results.
diff --git a/test/emacs.expected-output/notmuch-hello-with-empty b/test/emacs.expected-output/notmuch-hello-with-empty
new file mode 100644 (file)
index 0000000..a9ed630
--- /dev/null
@@ -0,0 +1,14 @@
+   Welcome to notmuch. You have 50 messages.
+
+Saved searches: [edit]
+
+         50 inbox           50 unread           0 empty     
+
+Search:                                                                     
+
+[Show all tags]
+
+        Type a search query and hit RET to view matching threads.
+               Edit saved searches with the `edit' button.
+  Hit RET or click on a saved search or tag name to view matching threads.
+    `=' refreshes this screen. `s' jumps to the search box. `q' to quit.
diff --git a/test/emacs.expected-output/notmuch-search-tag-inbox b/test/emacs.expected-output/notmuch-search-tag-inbox
new file mode 100644 (file)
index 0000000..146f63a
--- /dev/null
@@ -0,0 +1,23 @@
+  2009-11-18 [1/1]   Chris Wilson         [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread)
+  2009-11-18 [2/2]   Carl Worth, Alex Botero-Lowry [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread)
+  2009-11-18 [2/2]   Carl Worth, Ingmar Vanhassel [notmuch] [PATCH] Typsos (inbox unread)
+  2009-11-18 [3/3]   Carl Worth, Keith Packard, Adrian Perez de Castro [notmuch] Introducing myself (inbox unread)
+  2009-11-18 [3/3]   Carl Worth, Keith Packard, Israel Herraiz [notmuch] New to the list (inbox unread)
+  2009-11-18 [3/3]   Carl Worth, Jan Janak [notmuch] What a great idea! (inbox unread)
+  2009-11-18 [2/2]   Carl Worth, Jan Janak [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
+  2009-11-18 [3/3]   Carl Worth, Keith Packard, Aron Griffis [notmuch] archive (inbox unread)
+  2009-11-18 [2/2]   Carl Worth, Keith Packard [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
+  2009-11-18 [7/7]   Carl Worth, Lars Kellogg-Stedman, Keith Packard, Mikhail Gusarov [notmuch] Working with Maildir storage? (inbox unread)
+  2009-11-18 [5/5]   Carl Worth, Keith Packard, Mikhail Gusarov [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
+  2009-11-18 [2/2]   Alexander Botero-Lowry, Keith Packard [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
+  2009-11-18 [1/1]   Alexander Botero-Lowry [notmuch] request for pull (inbox unread)
+  2009-11-18 [4/4]   Alexander Botero-Lowry, Jjgod Jiang [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
+  2009-11-18 [1/1]   Rolland Santimano    [notmuch] Link to mailing list archives ? (inbox unread)
+  2009-11-18 [1/1]   Jan Janak            [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] count_files: sort directory in inode order before statting (inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread)
+  2009-11-18 [1/1]   Stewart Smith        [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (inbox unread)
+  2009-11-18 [2/2]   Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr? (attachment inbox unread)
+  2009-11-17 [1/1]   Mikhail Gusarov      [notmuch] [PATCH] Handle rename of message file (inbox unread)
+  2009-11-17 [2/2]   Carl Worth, Alex Botero-Lowry [notmuch] preliminary FreeBSD support (attachment inbox unread)
+End of search results.
diff --git a/test/emacs.expected-output/notmuch-show-thread-maildir-storage b/test/emacs.expected-output/notmuch-show-thread-maildir-storage
new file mode 100644 (file)
index 0000000..92cdd19
--- /dev/null
@@ -0,0 +1,202 @@
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+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,
+
+[ 4-line signature. Click/Enter to toggle visibility. ]
+-- 
+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 ]
+[ 3-line signature. Click/Enter to toggle visibility. ]
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+
+ 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.
+
+ [ 1-line signature. Click/Enter to toggle visibility. ]
+ -- 
+ http://fossarchy.blogspot.com/
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 3-line signature. Click/Enter to toggle visibility. ]
+ _______________________________________________
+ notmuch mailing list
+ notmuch@notmuchmail.org
+ http://notmuchmail.org/mailman/listinfo/notmuch
+  Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox 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
+
+  > 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
+
+  [ 4-line signature. Click/Enter to toggle visibility. ]
+  -- 
+  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 ]
+  [ 3-line signature. Click/Enter to toggle visibility. ]
+  _______________________________________________
+  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
+
+   [ 9-line signature. Click/Enter to toggle visibility. ]
+   -- 
+   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 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
+
+    > 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).
+
+    [ 4-line signature. Click/Enter to toggle visibility. ]
+    -- 
+    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 ]
+    [ 3-line signature. Click/Enter to toggle visibility. ]
+    _______________________________________________
+    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/raw-message-cf0c4d-52ad0a b/test/emacs.expected-output/raw-message-cf0c4d-52ad0a
new file mode 100644 (file)
index 0000000..75b05fa
--- /dev/null
@@ -0,0 +1,104 @@
+MIME-Version: 1.0
+Date: Tue, 17 Nov 2009 11:36:14 -0800
+Message-ID: <cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com>
+From: Alex Botero-Lowry <alex.boterolowry@gmail.com>
+To: notmuch@notmuchmail.org
+Content-Type: multipart/mixed; boundary=0016e687869333b1570478963d35
+Subject: [notmuch] preliminary FreeBSD support
+X-BeenThere: notmuch@notmuchmail.org
+X-Mailman-Version: 2.1.12
+Precedence: list
+List-Id: "Use and development of the notmuch mail system."
+       <notmuch.notmuchmail.org>
+List-Unsubscribe: <http://notmuchmail.org/mailman/options/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=unsubscribe>
+List-Archive: <http://notmuchmail.org/pipermail/notmuch>
+List-Post: <mailto:notmuch@notmuchmail.org>
+List-Help: <mailto:notmuch-request@notmuchmail.org?subject=help>
+List-Subscribe: <http://notmuchmail.org/mailman/listinfo/notmuch>,
+       <mailto:notmuch-request@notmuchmail.org?subject=subscribe>
+Sender: notmuch-bounces@notmuchmail.org
+Errors-To: notmuch-bounces@notmuchmail.org
+
+--0016e687869333b1570478963d35
+Content-Type: multipart/alternative; boundary=0016e687869333b14e0478963d33
+
+--0016e687869333b14e0478963d33
+Content-Type: text/plain; charset=ISO-8859-1
+
+I saw the announcement this morning, and was very excited, as I had been
+hoping sup would be turned into a library,
+since I like the concept more than the UI (I'd rather an emacs interface).
+
+I did a preliminary compile which worked out fine, but
+sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on
+FreeBSD, so notmuch_config_open segfaulted.
+
+Attached is a patch that supplies a default buffer size of 64 in cases where
+-1 is returned.
+
+http://www.opengroup.org/austin/docs/austin_328.txt - seems to indicate this
+is acceptable behavior,
+and http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically
+uses 64 as the
+buffer size.
+
+--0016e687869333b14e0478963d33
+Content-Type: text/html; charset=ISO-8859-1
+Content-Transfer-Encoding: quoted-printable
+
+I saw the announcement this morning, and was very excited, as I had been ho=
+ping sup would be turned into a library,<br>since I like the concept more t=
+han the UI (I&#39;d rather an emacs interface).<br><br>I did a preliminary =
+compile which worked out fine, but sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns=
+ -1 on<br>
+FreeBSD, so notmuch_config_open segfaulted.<br><br>Attached is a patch that=
+ supplies a default buffer size of 64 in cases where -1 is returned.<br><br=
+><a href=3D"http://www.opengroup.org/austin/docs/austin_328.txt">http://www=
+.opengroup.org/austin/docs/austin_328.txt</a> - seems to indicate this is a=
+cceptable behavior,<br>
+and <a href=3D"http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg01680=
+8.html">http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.html<=
+/a> specifically uses 64 as the<br>buffer size.<br><br><br>
+
+--0016e687869333b14e0478963d33--
+--0016e687869333b1570478963d35
+Content-Type: application/octet-stream; 
+       name="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
+Content-Disposition: attachment; 
+       filename="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
+Content-Transfer-Encoding: base64
+X-Attachment-Id: f_g252e6gs0
+
+RnJvbSBlM2JjNGJiZDdiOWQwZDA4NjgxNmFiNWY4ZjJkNmZmZWExZGQzZWE0IE1vbiBTZXAgMTcg
+MDA6MDA6MDAgMjAwMQpGcm9tOiBBbGV4YW5kZXIgQm90ZXJvLUxvd3J5IDxhbGV4LmJvdGVyb2xv
+d3J5QGdtYWlsLmNvbT4KRGF0ZTogVHVlLCAxNyBOb3YgMjAwOSAxMTozMDozOSAtMDgwMApTdWJq
+ZWN0OiBbUEFUQ0hdIERlYWwgd2l0aCBzaXR1YXRpb24gd2hlcmUgc3lzY29uZihfU0NfR0VUUFdf
+Ul9TSVpFX01BWCkgcmV0dXJucyAtMQoKLS0tCiBub3RtdWNoLWNvbmZpZy5jIHwgICAgMiArKwog
+MSBmaWxlcyBjaGFuZ2VkLCAyIGluc2VydGlvbnMoKyksIDAgZGVsZXRpb25zKC0pCgpkaWZmIC0t
+Z2l0IGEvbm90bXVjaC1jb25maWcuYyBiL25vdG11Y2gtY29uZmlnLmMKaW5kZXggMjQ4MTQ5Yy4u
+ZTcyMjBkOCAxMDA2NDQKLS0tIGEvbm90bXVjaC1jb25maWcuYworKysgYi9ub3RtdWNoLWNvbmZp
+Zy5jCkBAIC03Nyw2ICs3Nyw3IEBAIHN0YXRpYyBjaGFyICoKIGdldF9uYW1lX2Zyb21fcGFzc3dk
+X2ZpbGUgKHZvaWQgKmN0eCkKIHsKICAgICBsb25nIHB3X2J1Zl9zaXplID0gc3lzY29uZihfU0Nf
+R0VUUFdfUl9TSVpFX01BWCk7CisgICAgaWYgKHB3X2J1Zl9zaXplID09IC0xKSBwd19idWZfc2l6
+ZSA9IDY0OwogICAgIGNoYXIgKnB3X2J1ZiA9IHRhbGxvY196ZXJvX3NpemUgKGN0eCwgcHdfYnVm
+X3NpemUpOwogICAgIHN0cnVjdCBwYXNzd2QgcGFzc3dkLCAqaWdub3JlZDsKICAgICBjaGFyICpu
+YW1lOwpAQCAtMTAxLDYgKzEwMiw3IEBAIHN0YXRpYyBjaGFyICoKIGdldF91c2VybmFtZV9mcm9t
+X3Bhc3N3ZF9maWxlICh2b2lkICpjdHgpCiB7CiAgICAgbG9uZyBwd19idWZfc2l6ZSA9IHN5c2Nv
+bmYoX1NDX0dFVFBXX1JfU0laRV9NQVgpOworICAgIGlmIChwd19idWZfc2l6ZSA9PSAtMSkgcHdf
+YnVmX3NpemUgPSA2NDsKICAgICBjaGFyICpwd19idWYgPSB0YWxsb2NfemVyb19zaXplIChjdHgs
+IHB3X2J1Zl9zaXplKTsKICAgICBzdHJ1Y3QgcGFzc3dkIHBhc3N3ZCwgKmlnbm9yZWQ7CiAgICAg
+Y2hhciAqbmFtZTsKLS0gCjEuNi41LjIKCg==
+--0016e687869333b1570478963d35
+Content-Type: text/plain; charset="us-ascii"
+MIME-Version: 1.0
+Content-Transfer-Encoding: 7bit
+Content-Disposition: inline
+
+_______________________________________________
+notmuch mailing list
+notmuch@notmuchmail.org
+http://notmuchmail.org/mailman/listinfo/notmuch
+
+--0016e687869333b1570478963d35--
+
diff --git a/test/encoding b/test/encoding
new file mode 100755 (executable)
index 0000000..68cb8ee
--- /dev/null
@@ -0,0 +1,24 @@
+#!/bin/bash
+test_description="encoding issues"
+. ./test-lib.sh
+
+test_begin_subtest "Message with text of unknown charset"
+add_message '[content-type]="text/plain; charset=unknown-8bit"' \
+            "[body]=irrelevant"
+output=$(notmuch show id:${gen_msg_id} 2>&1 | notmuch_show_sanitize)
+test_expect_equal "$output" "\fmessage{ id:msg-001@notmuch-test-suite depth:0 match:1 filename:/XXX/mail/msg-001
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (inbox unread)
+Subject: Test message #1
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Tue, 05 Jan 2001 15:43:57 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+irrelevant
+\fpart}
+\fbody}
+\fmessage}"
+
+test_done
diff --git a/test/from-guessing b/test/from-guessing
new file mode 100755 (executable)
index 0000000..8de543f
--- /dev/null
@@ -0,0 +1,195 @@
+#!/bin/bash
+test_description="From line heuristics (with multiple configured addresses)"
+. ./test-lib.sh
+
+test_begin_subtest "Magic from guessing (nothing to go on)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Envelope-to:)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[header]="Envelope-To: test_suite_other@notmuchmail.org"' \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (X-Original-To:)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[header]="X-Original-To: test_suite_other@notmuchmail.org"' \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: .. for ..)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])\
+        by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\
+        for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: domain)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])\
+        by mail.otherdomain.org (some MTA) with ESMTP id 12345678\
+        Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@otherdomain.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+
+test_begin_subtest "Testing From line heuristics (with single configured address)"
+sed -i -e "s/^other_email.*//" "${NOTMUCH_CONFIG}"
+
+test_begin_subtest "Magic from guessing (nothing to go on)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Envelope-to:)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[header]="Envelope-To: test_suite_other@notmuchmail.org"' \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (X-Original-To:)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[header]="X-Original-To: test_suite_other@notmuchmail.org"' \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: .. for ..)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])\
+        by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\
+        for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_begin_subtest "Magic from guessing (Received: domain)"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=mailinglist@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            "[header]=\"Received: from mail.example.com (mail.example.com [1.1.1.1])\
+        by mail.otherdomain.org (some MTA) with ESMTP id 12345678\
+        Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="from guessing test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, mailinglist@notmuchmail.org
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> from guessing test"
+
+test_done
diff --git a/test/json b/test/json
new file mode 100755 (executable)
index 0000000..7fe2a27
--- /dev/null
+++ b/test/json
@@ -0,0 +1,42 @@
+#!/bin/bash
+test_description="--format=json output"
+. ./test-lib.sh
+
+test_begin_subtest "Show message: json"
+add_message "[subject]=\"json-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-show-message\""
+output=$(notmuch show --format=json "json-show-message")
+test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
+
+test_begin_subtest "Search message: json"
+add_message "[subject]=\"json-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-search-message\""
+output=$(notmuch search --format=json "json-search-message" | notmuch_search_sanitize)
+test_expect_equal "$output" "[{\"thread\": \"XXX\",
+\"timestamp\": 946728000,
+\"matched\": 1,
+\"total\": 1,
+\"authors\": \"Notmuch Test Suite\",
+\"subject\": \"json-search-subject\",
+\"tags\": [\"inbox\", \"unread\"]}]"
+
+test_begin_subtest "Search by subject (utf-8):"
+add_message [subject]=utf8-sübjéct "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\""
+output=$(notmuch search subject:utf8-sübjéct | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
+
+test_begin_subtest "Show message: json, utf-8"
+add_message "[subject]=\"json-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""
+output=$(notmuch show --format=json "jsön-show-méssage")
+test_expect_equal "$output" "[[[{\"id\": \"${gen_msg_id}\", \"match\": true, \"filename\": \"${gen_msg_filename}\", \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Cc\": \"\", \"Bcc\": \"\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 -0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"
+
+test_begin_subtest "Search message: json, utf-8"
+add_message "[subject]=\"json-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
+output=$(notmuch search --format=json "jsön-search-méssage" | notmuch_search_sanitize)
+test_expect_equal "$output" "[{\"thread\": \"XXX\",
+\"timestamp\": 946728000,
+\"matched\": 1,
+\"total\": 1,
+\"authors\": \"Notmuch Test Suite\",
+\"subject\": \"json-search-utf8-body-sübjéct\",
+\"tags\": [\"inbox\", \"unread\"]}]"
+
+test_done
diff --git a/test/long-id b/test/long-id
new file mode 100755 (executable)
index 0000000..84f9294
--- /dev/null
@@ -0,0 +1,27 @@
+#!/bin/bash
+test_description="messages with ridiculously-long message IDs"
+. ./test-lib.sh
+
+test_begin_subtest "Referencing long ID before adding"
+generate_message '[subject]="Reference of ridiculously-long message ID"' \
+                "[references]=\<abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-\>"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Adding message with long ID"
+generate_message '[subject]="A ridiculously-long message ID"' \
+                "[id]=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Referencing long ID after adding"
+generate_message '[subject]="Reply to ridiculously-long message ID"' \
+                "[in-reply-to]=\<abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-\>"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Ensure all messages were threaded together"
+output=$(notmuch search 'subject:"a ridiculously-long message ID"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [1/3] Notmuch Test Suite; A ridiculously-long message ID (inbox unread)"
+
+test_done
diff --git a/test/new b/test/new
new file mode 100755 (executable)
index 0000000..7b396ae
--- /dev/null
+++ b/test/new
@@ -0,0 +1,165 @@
+#!/bin/bash
+test_description='"notmuch new" in several variations'
+. ./test-lib.sh
+
+test_begin_subtest "No new messages"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+
+test_begin_subtest "Single new message"
+generate_message
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Multiple new messages"
+generate_message
+generate_message
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 2 new messages to the database."
+
+
+test_begin_subtest "No new messages (non-empty DB)"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail."
+
+
+test_begin_subtest "New directories"
+rm -rf "${MAIL_DIR}"/* "${MAIL_DIR}"/.notmuch
+mkdir "${MAIL_DIR}"/def
+mkdir "${MAIL_DIR}"/ghi
+generate_message [dir]=def
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Alternate inode order"
+
+rm -rf "${MAIL_DIR}"/.notmuch
+mv "${MAIL_DIR}"/ghi "${MAIL_DIR}"/abc
+rm "${MAIL_DIR}"/def/*
+generate_message [dir]=abc
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Message moved in"
+rm -rf "${MAIL_DIR}"/* "${MAIL_DIR}"/.notmuch
+generate_message
+tmp_msg_filename=tmp/"$gen_msg_filename"
+mkdir -p "$(dirname "$tmp_msg_filename")"
+mv "$gen_msg_filename" "$tmp_msg_filename"
+increment_mtime "${MAIL_DIR}"
+notmuch new > /dev/null
+mv "$tmp_msg_filename" "$gen_msg_filename"
+increment_mtime "${MAIL_DIR}"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "Renamed message"
+
+generate_message
+notmuch new > /dev/null
+mv "$gen_msg_filename" "${gen_msg_filename}"-renamed
+increment_mtime "${MAIL_DIR}"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected 1 file rename."
+
+
+test_begin_subtest "Deleted message"
+
+rm "${gen_msg_filename}"-renamed
+increment_mtime "${MAIL_DIR}"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 1 message."
+
+
+test_begin_subtest "Renamed directory"
+
+generate_message [dir]=dir
+generate_message [dir]=dir
+generate_message [dir]=dir
+
+notmuch new > /dev/null
+
+mv "${MAIL_DIR}"/dir "${MAIL_DIR}"/dir-renamed
+increment_mtime "${MAIL_DIR}"
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Detected 3 file renames."
+
+
+test_begin_subtest "Deleted directory"
+
+rm -rf "${MAIL_DIR}"/dir-renamed
+increment_mtime "${MAIL_DIR}"
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 3 messages."
+
+
+test_begin_subtest "New directory (at end of list)"
+
+generate_message [dir]=zzz
+generate_message [dir]=zzz
+generate_message [dir]=zzz
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 3 new messages to the database."
+
+
+test_begin_subtest "Deleted directory (end of list)"
+
+rm -rf "${MAIL_DIR}"/zzz
+increment_mtime "${MAIL_DIR}"
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 3 messages."
+
+
+test_begin_subtest "New symlink to directory"
+
+rm -rf "${MAIL_DIR}"/.notmuch
+mv "${MAIL_DIR}" "$PWD"/actual_maildir
+
+mkdir "${MAIL_DIR}"
+ln -s "$PWD"/actual_maildir "${MAIL_DIR}"/symlink
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "New symlink to a file"
+generate_message
+external_msg_filename="$PWD"/external/"$(basename "$gen_msg_filename")"
+mkdir -p "$(dirname "$external_msg_filename")"
+mv "$gen_msg_filename" "$external_msg_filename"
+ln -s "$external_msg_filename" "$gen_msg_filename"
+increment_mtime "${MAIL_DIR}"
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+
+test_begin_subtest "New two-level directory"
+
+generate_message [dir]=two/levels
+generate_message [dir]=two/levels
+generate_message [dir]=two/levels
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 3 new messages to the database."
+
+
+test_begin_subtest "Deleted two-level directory"
+
+rm -rf "${MAIL_DIR}"/two
+increment_mtime "${MAIL_DIR}"
+
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "No new mail. Removed 3 messages."
+
+test_done
index 00ac6b0e3a2e11ba0adb6c3966b570210d999f9a..9a3c698396a57e9ce3969f26fdb0e749b2cfbe8c 100755 (executable)
 #!/bin/bash
-set -e
 
-# Messages contain time/date values with time zone and notmuch
-# displays them converted to the local time zone. We need to set fixed
-# timezone here so that the output of the tests is always the same
-# without regard to the time zone of where the test suite is run.
-export TZ=UTC+8
-
-find_notmuch_binary ()
-{
-    dir=$1
-
-    while [ -n "$dir" ]; do
-       bin=$dir/notmuch
-       if [ -x $bin ]; then
-           echo $bin
-           return
-       fi
-       dir=$(dirname $dir)
-       if [ "$dir" = "/" ]; then
-           break
-       fi
-    done
-
-    echo notmuch
-}
-
-increment_mtime_amount=0
-increment_mtime ()
-{
-    dir=$1
-
-    increment_mtime_amount=$((increment_mtime_amount + 1))
-    touch -d "+${increment_mtime_amount} seconds" $dir
-}
-
-# Generate a new message in the mail directory, with a unique message
-# ID and subject. The message is not added to the index.
-#
-# After this function returns, the filename of the generated message
-# is available as $gen_msg_filename and the message ID is available as
-# $gen_msg_id .
-#
-# This function supports named parameters with the bash syntax for
-# assigning a value to an associative array ([name]=value). The
-# supported parameters are:
-#
-#  [dir]=directory/of/choice
-#
-#      Generate the message in directory 'directory/of/choice' within
-#      the mail store. The directory will be created if necessary.
-#
-#  [body]=text
-#
-#      Text to use as the body of the email message
-#
-#  '[from]="Some User <user@example.com>"'
-#  '[to]="Some User <user@example.com>"'
-#  '[subject]="Subject of email message"'
-#  '[date]="RFC 822 Date"'
+# Run tests
 #
-#      Values for email headers. If not provided, default values will
-#      be generated instead.
+# Copyright (c) 2005 Junio C Hamano
 #
-#  '[cc]="Some User <user@example.com>"'
-#  [reply-to]=some-address
-#  [in-reply-to]=<message-id>
-#  [references]=<message-id>
-#  [content-type]=content-type-specification
-#  '[header]=full header line, including keyword'
-#
-#      Additional values for email headers. If these are not provided
-#      then the relevant headers will simply not appear in the
-#      message.
-#
-#  '[id]=message-id'
-#
-#      Controls the message-id of the created message.
-gen_msg_cnt=0
-gen_msg_filename=""
-gen_msg_id=""
-generate_message ()
-{
-    # This is our (bash-specific) magic for doing named parameters
-    local -A template="($@)"
-    local additional_headers
-
-    gen_msg_cnt=$((gen_msg_cnt + 1))
-    gen_msg_name=msg-$(printf "%03d" $gen_msg_cnt)
-
-    if [ -z "${template[id]}" ]; then
-       gen_msg_id="${gen_msg_name}@notmuch-test-suite"
-    else
-       gen_msg_id="${template[id]}"
-    fi
-
-    if [ -z "${template[dir]}" ]; then
-       gen_msg_filename="${MAIL_DIR}/$gen_msg_name"
-    else
-       gen_msg_filename="${MAIL_DIR}/${template[dir]}/$gen_msg_name"
-       mkdir -p $(dirname $gen_msg_filename)
-    fi
-
-    if [ -z "${template[body]}" ]; then
-       template[body]="This is just a test message (#${gen_msg_cnt})"
-    fi
-
-    if [ -z "${template[from]}" ]; then
-       template[from]="Notmuch Test Suite <test_suite@notmuchmail.org>"
-    fi
-
-    if [ -z "${template[to]}" ]; then
-       template[to]="Notmuch Test Suite <test_suite@notmuchmail.org>"
-    fi
-
-    if [ -z "${template[subject]}" ]; then
-       template[subject]="Test message #${gen_msg_cnt}"
-    fi
-
-    if [ -z "${template[date]}" ]; then
-       template[date]="Tue, 05 Jan 2001 15:43:57 -0800"
-    fi
-
-    additional_headers=""
-    if [ ! -z "${template[header]}" ]; then
-       additional_headers="${template[header]}
-${additional_headers}"
-    fi
-
-    if [ ! -z "${template[reply-to]}" ]; then
-       additional_headers="Reply-To: ${template[reply-to]}
-${additional_headers}"
-    fi
-
-    if [ ! -z "${template[in-reply-to]}" ]; then
-       additional_headers="In-Reply-To: ${template[in-reply-to]}
-${additional_headers}"
-    fi
-
-    if [ ! -z "${template[cc]}" ]; then
-       additional_headers="Cc: ${template[cc]}
-${additional_headers}"
-    fi
-
-    if [ ! -z "${template[references]}" ]; then
-       additional_headers="References: ${template[references]}
-${additional_headers}"
-    fi
-
-    if [ ! -z "${template[content-type]}" ]; then
-       additional_headers="Content-Type: ${template[content-type]}
-${additional_headers}"
-    fi
-
-
-cat <<EOF >$gen_msg_filename
-From: ${template[from]}
-To: ${template[to]}
-Message-Id: <${gen_msg_id}>
-Subject: ${template[subject]}
-Date: ${template[date]}
-${additional_headers}
-${template[body]}
-EOF
-
-    # Ensure that the mtime of the containing directory is updated
-    increment_mtime $(dirname ${gen_msg_filename})
-}
-
-# Generate a new message and add it to the index.
-#
-# All of the arguments and return values supported by generate_message
-# are also supported here, so see that function for details.
-add_message ()
-{
-    generate_message "$@"
-
-    $NOTMUCH new > /dev/null
-}
-
-tests=0
-test_failures=0
-
-pass_if_equal ()
-{
-    output=$1
-    expected=$2
-
-    tests=$((tests + 1))
-
-    if [ "$output" = "$expected" ]; then
-       echo "  PASS"
-    else
-       echo "  FAIL"
-       testname=test-$(printf "%03d" $tests)
-       echo "$expected" > $testname.expected
-       echo "$output" > $testname.output
-       diff -u $testname.expected $testname.output || true
-       test_failures=$((test_failures + 1))
-    fi
-}
-
-TEST_DIR=$(pwd)/test.$$
-MAIL_DIR=${TEST_DIR}/mail
-export NOTMUCH_CONFIG=${TEST_DIR}/notmuch-config
-NOTMUCH=$(find_notmuch_binary $(pwd))
-
-NOTMUCH_NEW ()
-{
-    $NOTMUCH new | grep -v -E -e '^Processed [0-9]*( total)? file|Found [0-9]* total file'
-}
-
-notmuch_search_sanitize ()
-{
-    sed -r -e 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
-}
-
-NOTMUCH_SHOW_FILENAME_SQUELCH='s,filename:.*/mail,filename:/XXX/mail,'
-notmuch_show_sanitize ()
-{
-    sed -e "$NOTMUCH_SHOW_FILENAME_SQUELCH"
-}
-
-rm -rf ${TEST_DIR}
-mkdir ${TEST_DIR}
-cd ${TEST_DIR}
-
-mkdir ${MAIL_DIR}
-
-cat <<EOF > ${NOTMUCH_CONFIG}
-[database]
-path=${MAIL_DIR}
-
-[user]
-name=Notmuch Test Suite
-primary_email=test_suite@notmuchmail.org
-other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org
-EOF
-
-printf "Testing \"notmuch new\" in several variations:\n"
-printf " No new messages...\t\t\t\t"
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail."
-
-printf " Single new message...\t\t\t\t"
-generate_message
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Multiple new messages...\t\t\t"
-generate_message
-generate_message
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 2 new messages to the database."
-
-printf " No new messages (non-empty DB)...\t\t"
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail."
-
-printf " New directories...\t\t\t\t"
-rm -rf ${MAIL_DIR}/* ${MAIL_DIR}/.notmuch
-mkdir ${MAIL_DIR}/def
-mkdir ${MAIL_DIR}/ghi
-generate_message [dir]=def
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Alternate inode order...\t\t\t"
-
-rm -rf ${MAIL_DIR}/.notmuch
-mv ${MAIL_DIR}/ghi ${MAIL_DIR}/abc
-rm ${MAIL_DIR}/def/*
-generate_message [dir]=abc
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Message moved in...\t\t\t\t"
-rm -rf ${MAIL_DIR}/* ${MAIL_DIR}/.notmuch
-generate_message
-tmp_msg_filename=tmp/$gen_msg_filename
-mkdir -p $(dirname $tmp_msg_filename)
-mv $gen_msg_filename $tmp_msg_filename
-increment_mtime ${MAIL_DIR}
-$NOTMUCH new > /dev/null
-mv $tmp_msg_filename $gen_msg_filename
-increment_mtime ${MAIL_DIR}
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Renamed message...\t\t\t\t"
-
-generate_message
-$NOTMUCH new > /dev/null
-mv $gen_msg_filename ${gen_msg_filename}-renamed
-increment_mtime ${MAIL_DIR}
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail. Detected 1 file rename."
-
-printf " Deleted message...\t\t\t\t"
-
-rm ${gen_msg_filename}-renamed
-increment_mtime ${MAIL_DIR}
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail. Removed 1 message."
-
-printf " Renamed directory...\t\t\t\t"
-
-generate_message [dir]=dir
-generate_message [dir]=dir
-generate_message [dir]=dir
-
-$NOTMUCH new > /dev/null
-
-mv ${MAIL_DIR}/dir ${MAIL_DIR}/dir-renamed
-increment_mtime ${MAIL_DIR}
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail. Detected 3 file renames."
-
-printf " Deleted directory...\t\t\t\t"
-
-rm -rf ${MAIL_DIR}/dir-renamed
-increment_mtime ${MAIL_DIR}
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail. Removed 3 messages."
-
-printf " New directory (at end of list)...\t\t"
-
-generate_message [dir]=zzz
-generate_message [dir]=zzz
-generate_message [dir]=zzz
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 3 new messages to the database."
-
-printf " Deleted directory (end of list)...\t\t"
-
-rm -rf ${MAIL_DIR}/zzz
-increment_mtime ${MAIL_DIR}
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail. Removed 3 messages."
-
-printf " New symlink to directory...\t\t\t"
-
-rm -rf ${MAIL_DIR}/.notmuch
-mv ${MAIL_DIR} ${TEST_DIR}/actual_maildir
-
-mkdir ${MAIL_DIR}
-ln -s ${TEST_DIR}/actual_maildir ${MAIL_DIR}/symlink
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " New symlink to a file...\t\t\t"
-generate_message
-external_msg_filename=${TEST_DIR}/external/$(basename $gen_msg_filename)
-mkdir -p $(dirname $external_msg_filename)
-mv $gen_msg_filename $external_msg_filename
-ln -s $external_msg_filename $gen_msg_filename
-increment_mtime ${MAIL_DIR}
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " New two-level directory...\t\t\t"
-
-generate_message [dir]=two/levels
-generate_message [dir]=two/levels
-generate_message [dir]=two/levels
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 3 new messages to the database."
-
-printf " Deleted two-level directory...\t\t\t"
-
-rm -rf ${MAIL_DIR}/two
-increment_mtime ${MAIL_DIR}
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "No new mail. Removed 3 messages."
-
-printf "\nTesting \"notmuch search\" in several variations:\n"
-
-printf " Search body...\t\t\t\t\t"
-add_message '[subject]="body search"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [body]=bodysearchtest
-output=$($NOTMUCH search bodysearchtest | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)"
-
-printf " Search by from:...\t\t\t\t"
-add_message '[subject]="search by from"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom
-output=$($NOTMUCH search from:searchbyfrom | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)"
-
-printf " Search by to:...\t\t\t\t"
-add_message '[subject]="search by to"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto
-output=$($NOTMUCH search to:searchbyto | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)"
-
-printf " Search by subject:...\t\t\t\t"
-add_message [subject]=subjectsearchtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$($NOTMUCH search subject:subjectsearchtest | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)"
-
-printf " Search by id:...\t\t\t\t"
-add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$($NOTMUCH search id:${gen_msg_id} | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)"
-
-printf " Search by tag:...\t\t\t\t"
-add_message '[subject]="search by tag"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-$NOTMUCH tag +searchbytag id:${gen_msg_id}
-output=$($NOTMUCH search tag:searchbytag | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)"
-
-printf " Search by thread:...\t\t\t\t"
-add_message '[subject]="search by thread"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-thread_id=$($NOTMUCH search id:${gen_msg_id} | sed -e 's/thread:\([a-f0-9]*\).*/\1/')
-output=$($NOTMUCH search thread:${thread_id} | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)"
-
-printf " Search body (phrase)...\t\t\t"
-add_message '[subject]="body search (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="body search (phrase)"'
-add_message '[subject]="negative result"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="This phrase should not match the body search"'
-output=$($NOTMUCH search '\"body search (phrase)\"' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)"
-
-printf " Search by from: (address)...\t\t\t"
-add_message '[subject]="search by from (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom@example.com
-output=$($NOTMUCH search from:searchbyfrom@example.com | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)"
-
-printf " Search by from: (name)...\t\t\t"
-add_message '[subject]="search by from (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[from]="Search By From Name <test@example.com>"'
-output=$($NOTMUCH search from:'Search By From Name' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)"
+# Adapted from a Makefile to a shell script by Carl Worth (2010)
 
-printf " Search by to: (address)...\t\t\t"
-add_message '[subject]="search by to (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto@example.com
-output=$($NOTMUCH search to:searchbyto@example.com | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)"
-
-printf " Search by to: (name)...\t\t\t"
-add_message '[subject]="search by to (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[to]="Search By To Name <test@example.com>"'
-output=$($NOTMUCH search to:'Search By To Name' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)"
-
-printf " Search by subject: (phrase)...\t\t\t"
-add_message '[subject]="subject search test (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-add_message '[subject]="this phrase should not match the subject search test"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$($NOTMUCH search 'subject:\"subject search test (phrase)\"' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)"
-
-printf " Search for all messages (\"*\"):...\t\t"
-output=$($NOTMUCH search '*' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Test message #6 (inbox unread)
-thread:XXX   2001-01-05 [1/1] Notmuch Test Suite; Test message #14 (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)
-thread:XXX   2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; negative result (inbox unread)
-thread:XXX   2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)
-thread:XXX   2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)
-thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; this phrase should not match the subject search test (inbox unread)"
-
-printf " Search body (utf-8):...\t\t\t"
-add_message '[subject]="utf8-message-body-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="message body utf8: bödý"'
-output=$($NOTMUCH search 'bödý' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-message-body-subject (inbox unread)"
-
-printf "\nTesting --format=json output:\n"
-
-printf " Show message: json...\t\t\t\t"
-add_message '[subject]="json-show-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="json-show-message"'
-output=$($NOTMUCH show --format=json 'json-show-message')
-pass_if_equal "$output" '[[[{"id": "'${gen_msg_id}'", "match": true, "filename": "'${gen_msg_filename}'", "timestamp": 946728000, "date_relative": "2000-01-01", "tags": ["inbox","unread"], "headers": {"Subject": "json-show-subject", "From": "Notmuch Test Suite <test_suite@notmuchmail.org>", "To": "Notmuch Test Suite <test_suite@notmuchmail.org>", "Cc": "", "Bcc": "", "Date": "Sat, 01 Jan 2000 12:00:00 -0000"}, "body": [{"id": 1, "content-type": "text/plain", "content": "json-show-message\n"}]}, []]]]'
-
-printf " Search message: json...\t\t\t"
-add_message '[subject]="json-search-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="json-search-message"'
-output=$($NOTMUCH search --format=json 'json-search-message' | notmuch_search_sanitize)
-pass_if_equal "$output" '[{"thread": "XXX",
-"timestamp": 946728000,
-"matched": 1,
-"total": 1,
-"authors": "Notmuch Test Suite",
-"subject": "json-search-subject",
-"tags": ["inbox", "unread"]}]'
-
-printf " Search by subject (utf-8):...\t\t\t"
-add_message [subject]=utf8-sübjéct '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$($NOTMUCH search subject:utf8-sübjéct | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
-
-printf " Show message: json, utf-8...\t\t\t"
-add_message '[subject]="json-show-utf8-body-sübjéct"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="jsön-show-méssage"'
-output=$($NOTMUCH show --format=json 'jsön-show-méssage')
-pass_if_equal "$output" '[[[{"id": "'${gen_msg_id}'", "match": true, "filename": "'${gen_msg_filename}'", "timestamp": 946728000, "date_relative": "2000-01-01", "tags": ["inbox","unread"], "headers": {"Subject": "json-show-utf8-body-sübjéct", "From": "Notmuch Test Suite <test_suite@notmuchmail.org>", "To": "Notmuch Test Suite <test_suite@notmuchmail.org>", "Cc": "", "Bcc": "", "Date": "Sat, 01 Jan 2000 12:00:00 -0000"}, "body": [{"id": 1, "content-type": "text/plain", "content": "jsön-show-méssage\n"}]}, []]]]'
-
-printf " Search message: json, utf-8...\t\t\t"
-add_message '[subject]="json-search-utf8-body-sübjéct"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="jsön-search-méssage"'
-output=$($NOTMUCH search --format=json 'jsön-search-méssage' | notmuch_search_sanitize)
-pass_if_equal "$output" '[{"thread": "XXX",
-"timestamp": 946728000,
-"matched": 1,
-"total": 1,
-"authors": "Notmuch Test Suite",
-"subject": "json-search-utf8-body-sübjéct",
-"tags": ["inbox", "unread"]}]'
-
-printf "\nTesting naming of threads with changing subject:\n"
-add_message '[subject]="thread-naming: Initial thread subject"' \
-            '[date]="Fri, 05 Jan 2001 15:43:56 -0800"'
-first=${gen_msg_cnt}
-parent=${gen_msg_id}
-add_message '[subject]="thread-naming: Older changed subject"' \
-            '[date]="Sat, 06 Jan 2001 15:43:56 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-add_message '[subject]="thread-naming: Newer changed subject"' \
-            '[date]="Sun, 07 Jan 2001 15:43:56 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-add_message '[subject]="thread-naming: Final thread subject"' \
-            '[date]="Mon, 08 Jan 2001 15:43:56 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-final=${gen_msg_id}
-
-printf " Initial thread name (oldest-first search)...\t"
-output=$($NOTMUCH search --sort=oldest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-05 [4/4] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
-
-printf " Initial thread name (newest-first search)...\t"
-output=$($NOTMUCH search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-08 [4/4] Notmuch Test Suite; thread-naming: Final thread subject (inbox unread)"
-
-# Remove oldest and newest messages from search results
-$NOTMUCH tag -inbox id:$parent or id:$final
-
-printf " Changed thread name (oldest-first search)...\t"
-output=$($NOTMUCH search --sort=oldest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-06 [2/4] Notmuch Test Suite; thread-naming: Older changed subject (inbox unread)"
-
-printf " Changed thread name (newest-first search)...\t"
-output=$($NOTMUCH search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-07 [2/4] Notmuch Test Suite; thread-naming: Newer changed subject (inbox unread)"
-
-printf " Ignore added reply prefix (Re:)...\t\t"
-add_message '[subject]="Re: thread-naming: Initial thread subject"' \
-            '[date]="Tue, 09 Jan 2001 15:43:45 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-output=$($NOTMUCH search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-09 [3/5] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
-
-printf " Ignore added reply prefix (Aw:)...\t\t"
-add_message '[subject]="Aw: thread-naming: Initial thread subject"' \
-            '[date]="Wed, 10 Jan 2001 15:43:45 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-output=$($NOTMUCH search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-10 [4/6] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
-
-printf " Ignore added reply prefix (Vs:)...\t\t"
-add_message '[subject]="Vs: thread-naming: Initial thread subject"' \
-            '[date]="Thu, 11 Jan 2001 15:43:45 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-output=$($NOTMUCH search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-11 [5/7] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
-
-printf " Ignore added reply prefix (Sv:)...\t\t"
-add_message '[subject]="Sv: thread-naming: Initial thread subject"' \
-            '[date]="Fri, 12 Jan 2001 15:43:45 -0800"' \
-            "[in-reply-to]=\<$parent\>"
-output=$($NOTMUCH search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-12 [6/8] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
-
-printf " Test order of messages in \"notmuch show\"\t"
-output=$($NOTMUCH show thread-naming | notmuch_show_sanitize)
-pass_if_equal "$output" "\fmessage{ id:msg-$(printf "%03d" $first)@notmuch-test-suite depth:0 match:1 filename:/XXX/mail/msg-$(printf "%03d" $first)
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (unread)
-Subject: thread-naming: Initial thread subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Fri, 05 Jan 2001 15:43:56 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$first)
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 1)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 1)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-06) (inbox unread)
-Subject: thread-naming: Older changed subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Sat, 06 Jan 2001 15:43:56 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 1)))
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 2)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 2)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-07) (inbox unread)
-Subject: thread-naming: Newer changed subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Sun, 07 Jan 2001 15:43:56 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 2)))
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 3)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 3)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-08) (unread)
-Subject: thread-naming: Final thread subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Mon, 08 Jan 2001 15:43:56 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 3)))
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 4)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 4)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-09) (inbox unread)
-Subject: Re: thread-naming: Initial thread subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Tue, 09 Jan 2001 15:43:45 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 4)))
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 5)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 5)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-10) (inbox unread)
-Subject: Aw: thread-naming: Initial thread subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Wed, 10 Jan 2001 15:43:45 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 5)))
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 6)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 6)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-11) (inbox unread)
-Subject: Vs: thread-naming: Initial thread subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Thu, 11 Jan 2001 15:43:45 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 6)))
-\fpart}
-\fbody}
-\fmessage}
-\fmessage{ id:msg-$(printf "%03d" $((first + 7)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 7)))
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-12) (inbox unread)
-Subject: Sv: thread-naming: Initial thread subject
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Fri, 12 Jan 2001 15:43:45 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-This is just a test message (#$((first + 7)))
-\fpart}
-\fbody}
-\fmessage}"
-
-printf "\nTesting \"notmuch reply\" in several variations:\n"
-
-printf " Basic reply...\t\t\t\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=test_suite@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="basic reply test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> basic reply test"
-
-printf " Multiple recipients...\t\t\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-            '[to]="test_suite@notmuchmail.org, Someone Else <someone@example.com>"' \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="Multiple recipients"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, Someone Else <someone@example.com>
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> Multiple recipients"
-
-printf " Reply with CC...\t\t\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=test_suite@notmuchmail.org \
-            '[cc]="Other Parties <cc@example.com>"' \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="reply with CC"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>
-Cc: Other Parties <cc@example.com>
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> reply with CC"
-
-printf " Reply from alternate address...\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=test_suite_other@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="reply from alternate address"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> reply from alternate address"
-
-printf " Support for Reply-To...\t\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=test_suite@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="support for reply-to"' \
-            '[reply-to]="Sender <elsewhere@example.com>"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <elsewhere@example.com>
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> support for reply-to"
-
-printf " Un-munging Reply-To...\t\t\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-            '[to]="Some List <list@example.com>"' \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="Un-munging Reply-To"' \
-            '[reply-to]="Evil Munging List <list@example.com>"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, Some List <list@example.com>
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> Un-munging Reply-To"
-
-printf " Message with header of exactly 200 bytes...\t"
-
-add_message '[subject]="This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="200-byte header"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
-> 200-byte header"
-
-printf "\nTesting handling of uuencoded data:\n"
-
-add_message [subject]=uuencodetest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' \
-'[body]="This message is used to ensure that notmuch correctly handles a
-message containing a block of uuencoded data. First, we have a marker
-this content beforeuudata . Then we beging the uunencoded data itself:
-
-begin 644 bogus-uuencoded-data
-M0123456789012345678901234567890123456789012345678901234567890
-MOBVIOUSLY, THIS IS NOT ANY SORT OF USEFUL UUNECODED DATA.    
-MINSTEAD THIS IS JUST A WAY TO ENSURE THAT THIS BLOCK OF DATA 
-MIS CORRECTLY IGNORED WHEN NOTMUCH CREATES ITS INDEX. SO WE   
-MINCLUDE A DURINGUUDATA MARKER THAT SHOULD NOT RESULT IN ANY  
-MSEARCH RESULT.                                               
-\`
-end
-
-Finally, we have our afteruudata marker as well."'
-
-printf " Ensure content before uu data is indexed...\t"
-output=$($NOTMUCH search beforeuudata | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; uuencodetest (inbox unread)"
-printf " Ensure uu data is not indexed...\t\t"
-output=$($NOTMUCH search DURINGUUDATA | notmuch_search_sanitize)
-pass_if_equal "$output" ""
-printf " Ensure content after uu data is indexed...\t"
-output=$($NOTMUCH search afteruudata | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; uuencodetest (inbox unread)"
-
-printf "\nTesting \"notmuch dump\" and \"notmuch restore\":\n"
-
-printf " Dumping all tags...\t\t\t\t"
-$NOTMUCH dump dump.expected
-pass_if_equal "$?" "0"
-
-printf " Clearing all tags...\t\t\t\t"
-sed -e 's/(\([^(]*\))$/()/' < dump.expected > clear.expected
-$NOTMUCH restore clear.expected
-$NOTMUCH dump clear.actual
-pass_if_equal "$(< clear.actual)" "$(< clear.expected)"
-
-printf " Restoring original tags...\t\t\t"
-$NOTMUCH restore dump.expected
-$NOTMUCH dump dump.actual
-pass_if_equal "$(< dump.actual)" "$(< dump.expected)"
-
-printf " Restore with nothing to do...\t\t\t"
-$NOTMUCH restore dump.expected
-pass_if_equal "$?" "0"
-
-printf "\nTesting threading when messages received out of order:\n"
-printf " Adding initial child message...\t\t"
-generate_message [body]=foo '[in-reply-to]=\<parent-id\>' [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-printf " Searching returns the message...\t\t"
-output=$($NOTMUCH search foo | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; brokenthreadtest (inbox unread)"
-printf " Adding second child message...\t\t\t"
-generate_message [body]=foo '[in-reply-to]=\<parent-id\>' [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-printf " Searching returns both messages in one thread..."
-output=$($NOTMUCH search foo | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [2/2] Notmuch Test Suite; brokenthreadtest (inbox unread)"
-printf " Adding parent message...\t\t\t"
-generate_message [body]=foo [id]=parent-id [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-printf " Searching returns all three messages in one thread..."
-output=$($NOTMUCH search foo | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [3/3] Notmuch Test Suite; brokenthreadtest (inbox unread)"
-
-printf "\nTesting author reordering;\n"
-printf " Adding parent message...\t\t\t"
-generate_message [body]=findme [id]=new-parent-id [subject]=author-reorder-threadtest '[from]="User <user@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-printf " Adding initial child message...\t\t"
-generate_message [body]=findme '[in-reply-to]=\<new-parent-id\>' [subject]=author-reorder-threadtest '[from]="User1 <user1@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-printf " Adding second child message...\t\t\t"
-generate_message [body]=findme '[in-reply-to]=\<new-parent-id\>' [subject]=author-reorder-threadtest '[from]="User2 <user2@example.com>"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-printf " Searching when all three messages match...\t"
-output=$($NOTMUCH search findme | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [3/3] User, User1, User2; author-reorder-threadtest (inbox unread)"
-printf " Searching when two messages match...\t\t"
-output=$($NOTMUCH search User1 or User2 | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [2/3] User1, User2| User; author-reorder-threadtest (inbox unread)"
-printf " Searching when only one message matches...\t"
-output=$($NOTMUCH search User2 | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/3] User2| User, User1; author-reorder-threadtest (inbox unread)"
-printf " Searching when only first message matches...\t"
-output=$($NOTMUCH search User | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2000-01-01 [1/3] User| User1, User2; author-reorder-threadtest (inbox unread)"
-
-printf "\nTesting From line heuristics (with multiple configured addresses):\n"
-printf " Magic from guessing (nothing to go on)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (Envelope-to:)...\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="Envelope-To: test_suite_other@notmuchmail.org"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (X-Original-To:)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="X-Original-To: test_suite_other@notmuchmail.org"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (Received: .. for ..)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="Received: from mail.example.com (mail.example.com [1.1.1.1])\
-        by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\
-        for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (Received: domain)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="Received: from mail.example.com (mail.example.com [1.1.1.1])\
-        by mail.otherdomain.org (some MTA) with ESMTP id 12345678\
-        Sat, 10 Apr 2010 07:54:51 -0400 (EDT)"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@otherdomain.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-
-printf "\nTesting From line heuristics (with single configured address):\n"
-sed -i -e 's/^other_email.*//' ${NOTMUCH_CONFIG}
-
-printf " Magic from guessing (nothing to go on)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (Envelope-to:)...\t\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="Envelope-To: test_suite_other@notmuchmail.org"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (X-Original-To:)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="X-Original-To: test_suite_other@notmuchmail.org"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (Received: .. for ..)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="Received: from mail.example.com (mail.example.com [1.1.1.1])\
-        by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\
-        for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf " Magic from guessing (Received: domain)...\t"
-add_message '[from]="Sender <sender@example.com>"' \
-             [to]=mailinglist@notmuchmail.org \
-             [subject]=notmuch-reply-test \
-            '[header]="Received: from mail.example.com (mail.example.com [1.1.1.1])\
-        by mail.otherdomain.org (some MTA) with ESMTP id 12345678\
-        Sat, 10 Apr 2010 07:54:51 -0400 (EDT)"' \
-            '[date]="Tue, 05 Jan 2010 15:43:56 -0800"' \
-            '[body]="from guessing test"'
-
-output=$($NOTMUCH reply id:${gen_msg_id})
-pass_if_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
-Subject: Re: notmuch-reply-test
-To: Sender <sender@example.com>, mailinglist@notmuchmail.org
-Bcc: test_suite@notmuchmail.org
-In-Reply-To: <${gen_msg_id}>
-References: <${gen_msg_id}>
-
-On Tue, 05 Jan 2010 15:43:56 -0800, Sender <sender@example.com> wrote:
-> from guessing test"
-
-printf "\nTesting messages with ridiculously-long message IDs...\n"
-printf " Referencing long ID before adding...\t\t"
-generate_message '[subject]="Reference of ridiculously-long message ID"' \
-                '[references]=\<abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-\>'
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Adding message with long ID...\t\t\t"
-generate_message '[subject]="A ridiculously-long message ID"' \
-                '[id]=abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-'
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Referencing long ID after adding...\t\t"
-generate_message '[subject]="Reply to ridiculously-long message ID"' \
-                '[in-reply-to]=\<abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_-\>'
-
-output=$(NOTMUCH_NEW)
-pass_if_equal "$output" "Added 1 new message to the database."
-
-printf " Ensure all messages were threaded together...\t"
-
-output=$($NOTMUCH search 'subject:"a ridiculously-long message ID"' | notmuch_search_sanitize)
-pass_if_equal "$output" "thread:XXX   2001-01-05 [1/3] Notmuch Test Suite; A ridiculously-long message ID (inbox unread)"
+if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
+    echo "Error: The notmuch test suite requires a bash version >= 4.0"
+    echo "due to use of associative arrays within the test suite."
+    echo "Please try again with a newer bash (or help us fix the"
+    echo "test suite to be more portable). Thanks."
+    exit 1
+fi
 
-printf "\nTesting encoding issues...\n"
-printf " Message with text of unknown charset...\t"
-add_message '[content-type]="text/plain; charset=unknown-8bit"' \
-            '[body]=irrelevant'
+cd $(dirname "$0")
 
-output=$($NOTMUCH show id:${gen_msg_id} 2>&1 | notmuch_show_sanitize)
-pass_if_equal "$output" "\fmessage{ id:msg-074@notmuch-test-suite depth:0 match:1 filename:/XXX/mail/msg-074
-\fheader{
-Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (inbox unread)
-Subject: Test message #74
-From: Notmuch Test Suite <test_suite@notmuchmail.org>
-To: Notmuch Test Suite <test_suite@notmuchmail.org>
-Date: Tue, 05 Jan 2001 15:43:57 -0800
-\fheader}
-\fbody{
-\fpart{ ID: 1, Content-type: text/plain
-irrelevant
-\fpart}
-\fbody}
-\fmessage}"
+TESTS="basic new search json thread-naming raw reply dump-restore uuencode thread-order author-order from-guessing long-id encoding emacs"
 
-echo ""
-echo "Notmuch test suite complete."
+# Clean up any results from a previous run
+rm -r test-results >/dev/null 2>/dev/null
 
-if [ "$test_failures" = "0" ]; then
-    echo "All $tests tests passed."
-    rm -rf ${TEST_DIR}
-else
-    echo "$test_failures/$tests tests failed. The failures can be investigated in:"
-    echo "${TEST_DIR}"
-fi
+# Run the tests
+for test in $TESTS; do
+       ./$test "$@"
+done
 
-echo ""
+# Report results
+./aggregate-results.sh test-results/*
 
-exit $test_failures
+# Clean up
+rm -r test-results corpus.mail
diff --git a/test/raw b/test/raw
new file mode 100755 (executable)
index 0000000..4ed237c
--- /dev/null
+++ b/test/raw
@@ -0,0 +1,36 @@
+#!/bin/bash
+
+test_description='notmuch show --format=raw'
+. ./test-lib.sh
+
+test_begin_subtest "Generate some messages"
+generate_message
+generate_message
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 2 new messages to the database."
+
+test_begin_subtest "Attempt to show multiple raw messages"
+output=$(notmuch show --format=raw "*" 2>&1)
+test_expect_equal "$output" "Error: search term did not match precisely one message."
+
+test_begin_subtest "Show a raw message"
+output=$(notmuch show --format=raw id:msg-001@notmuch-test-suite)
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Message-Id: <msg-001@notmuch-test-suite>
+Subject: Test message #1
+Date: Tue, 05 Jan 2001 15:43:57 -0000
+
+This is just a test message (#1)"
+
+test_begin_subtest "Show another raw message"
+output=$(notmuch show --format=raw id:msg-002@notmuch-test-suite)
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Message-Id: <msg-002@notmuch-test-suite>
+Subject: Test message #2
+Date: Tue, 05 Jan 2001 15:43:57 -0000
+
+This is just a test message (#2)"
+
+test_done
diff --git a/test/reply b/test/reply
new file mode 100755 (executable)
index 0000000..99c3376
--- /dev/null
@@ -0,0 +1,123 @@
+#!/bin/bash
+test_description="\"notmuch reply\" in several variations"
+. ./test-lib.sh
+
+test_begin_subtest "Basic reply"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="basic reply test"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> basic reply test"
+
+test_begin_subtest "Multiple recipients"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]="test_suite@notmuchmail.org, Someone Else <someone@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="Multiple recipients"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, Someone Else <someone@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Multiple recipients"
+
+test_begin_subtest "Reply with CC"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite@notmuchmail.org \
+            '[cc]="Other Parties <cc@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="reply with CC"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+Cc: Other Parties <cc@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply with CC"
+
+test_begin_subtest "Reply from alternate address"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite_other@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="reply from alternate address"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite_other@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> reply from alternate address"
+
+test_begin_subtest "Support for Reply-To"
+add_message '[from]="Sender <sender@example.com>"' \
+             [to]=test_suite@notmuchmail.org \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="support for reply-to"' \
+            '[reply-to]="Sender <elsewhere@example.com>"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <elsewhere@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> support for reply-to"
+
+test_begin_subtest "Un-munging Reply-To"
+add_message '[from]="Sender <sender@example.com>"' \
+            '[to]="Some List <list@example.com>"' \
+             [subject]=notmuch-reply-test \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="Un-munging Reply-To"' \
+            '[reply-to]="Evil Munging List <list@example.com>"'
+
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: notmuch-reply-test
+To: Sender <sender@example.com>, Some List <list@example.com>
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
+> Un-munging Reply-To"
+
+test_begin_subtest "Message with header of exactly 200 bytes"
+add_message '[subject]="This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces"' \
+            '[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
+            '[body]="200-byte header"'
+output=$(notmuch reply id:${gen_msg_id})
+test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
+Subject: Re: This subject is exactly 200 bytes in length. Other than its length there is not much of note here. Note that the length of 200 bytes includes the Subject: and Re: prefixes with two spaces
+In-Reply-To: <${gen_msg_id}>
+References: <${gen_msg_id}>
+
+On Tue, 05 Jan 2010 15:43:56 -0000, Notmuch Test Suite <test_suite@notmuchmail.org> wrote:
+> 200-byte header"
+test_done
diff --git a/test/search b/test/search
new file mode 100755 (executable)
index 0000000..5939c6a
--- /dev/null
@@ -0,0 +1,104 @@
+#!/bin/bash
+test_description='"notmuch search" in several variations'
+. ./test-lib.sh
+
+add_email_corpus
+
+test_begin_subtest "Search body"
+add_message '[subject]="body search"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [body]=bodysearchtest
+output=$(notmuch search bodysearchtest | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)"
+
+test_begin_subtest "Search by from:"
+add_message '[subject]="search by from"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom
+output=$(notmuch search from:searchbyfrom | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)"
+
+test_begin_subtest "Search by to:"
+add_message '[subject]="search by to"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto
+output=$(notmuch search to:searchbyto | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)"
+
+test_begin_subtest "Search by subject:"
+add_message [subject]=subjectsearchtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search subject:subjectsearchtest | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)"
+
+test_begin_subtest "Search by id:"
+add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search id:${gen_msg_id} | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)"
+
+test_begin_subtest "Search by tag:"
+add_message '[subject]="search by tag"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+notmuch tag +searchbytag id:${gen_msg_id}
+output=$(notmuch search tag:searchbytag | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)"
+
+test_begin_subtest "Search by thread:"
+add_message '[subject]="search by thread"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+thread_id=$(notmuch search id:${gen_msg_id} | sed -e "s/thread:\([a-f0-9]*\).*/\1/")
+output=$(notmuch search thread:${thread_id} | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)"
+
+test_begin_subtest "Search body (phrase)"
+add_message '[subject]="body search (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="body search (phrase)"'
+add_message '[subject]="negative result"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="This phrase should not match the body search"'
+output=$(notmuch search '"body search (phrase)"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)"
+
+test_begin_subtest "Search by from: (address)"
+add_message '[subject]="search by from (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom@example.com
+output=$(notmuch search from:searchbyfrom@example.com | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)"
+
+test_begin_subtest "Search by from: (name)"
+add_message '[subject]="search by from (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[from]="Search By From Name <test@example.com>"'
+output=$(notmuch search from:"Search By From Name" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)"
+
+test_begin_subtest "Search by to: (address)"
+add_message '[subject]="search by to (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto@example.com
+output=$(notmuch search to:searchbyto@example.com | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)"
+
+test_begin_subtest "Search by to: (name)"
+add_message '[subject]="search by to (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[to]="Search By To Name <test@example.com>"'
+output=$(notmuch search to:"Search By To Name" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)"
+
+test_begin_subtest "Search by subject: (phrase)"
+add_message '[subject]="subject search test (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+add_message '[subject]="this phrase should not match the subject search test"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(notmuch search 'subject:"subject search test (phrase)"' | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)"
+
+test_begin_subtest 'Search for all messages ("*"
+output=$(notmuch search '*' | notmuch_search_sanitize)
+test_expect_equal):' "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)
+thread:XXX   2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; negative result (inbox unread)
+thread:XXX   2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; subject search test (phrase) (inbox unread)
+thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; this phrase should not match the subject search test (inbox unread)"
+
+test_begin_subtest "Search body (utf-8):"
+add_message '[subject]="utf8-message-body-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="message body utf8: bödý"'
+output=$(notmuch search "bödý" | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; utf8-message-body-subject (inbox unread)"
+
+test_expect_success "Search for non-existent message prints nothing" '
+echo -n > expected &&
+notmuch search "no-message-matches-this" > actual &&
+test_cmp expected actual'
+
+test_done
diff --git a/test/smtp-dummy.c b/test/smtp-dummy.c
new file mode 100644 (file)
index 0000000..e58d0ad
--- /dev/null
@@ -0,0 +1,206 @@
+/* smtp-dummy - Dummy SMTP server that delivers mail to the given file
+ *
+ * Copyright © 2010 Carl Worth
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Authors: Carl Worth <cworth@cworth.org>
+ */
+
+/* This (non-compliant) SMTP server listens on localhost, port 25025
+ * and delivers a mail received to the given filename, (specified as a
+ * command-line argument). It exists after the first client connection
+ * completes.
+ *
+ * It implements very little of the SMTP protocol, even less than
+ * specified as the minimum implementation in the SMTP RFC, (not
+ * implementing RSET, NOOP, nor VRFY). And it doesn't do any
+ * error-checking on the input.
+ *
+ * That is to say, if you use this program, you will very likely find
+ * cases where it doesn't do everything your SMTP client expects. You
+ * have been warned.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <netinet/ip.h>
+#include <netdb.h>
+#include <unistd.h>
+
+#define STRNCMP_LITERAL(var, literal) \
+    strncmp ((var), (literal), sizeof (literal) - 1)
+
+static void
+receive_data_to_file (FILE *peer, FILE *output)
+{
+       char *line = NULL;
+       size_t line_size;
+       ssize_t line_len;
+
+       while ((line_len = getline (&line, &line_size, peer)) != -1) {
+               if (STRNCMP_LITERAL (line, ".\r\n") == 0)
+                       break;
+               if (line_len < 2)
+                       continue;
+               if (line[line_len-1] == '\n' && line[line_len-2] == '\r') {
+                       line[line_len-2] = '\n';
+                       line[line_len-1] = '\0';
+               }
+               fprintf (output, "%s",
+                        line[0] == '.' ? line + 1 : line);
+       }
+
+       free (line);
+}
+
+static int
+process_command (FILE *peer, FILE *output, const char *command)
+{
+       if (STRNCMP_LITERAL (command, "EHLO ") == 0) {
+               fprintf (peer, "502\r\n");
+               fflush (peer);
+       } else if (STRNCMP_LITERAL (command, "HELO ") == 0) {
+               fprintf (peer, "250 localhost\r\n");
+               fflush (peer);
+       } else if (STRNCMP_LITERAL (command, "MAIL FROM:") == 0 ||
+                  STRNCMP_LITERAL (command, "RCPT TO:") == 0) {
+               fprintf (peer, "250 OK\r\n");
+               fflush (peer);
+       } else if (STRNCMP_LITERAL (command, "DATA") == 0) {
+               fprintf (peer, "354 End data with <CR><LF>.<CR><LF>\r\n");
+               fflush (peer);
+               receive_data_to_file (peer, output);
+               fprintf (peer, "250 OK\r\n");
+               fflush (peer);
+       } else if (STRNCMP_LITERAL (command, "QUIT") == 0) {
+               fprintf (peer, "221 BYE\r\n");
+               fflush (peer);
+               return 1;
+       } else {
+               fprintf (stderr, "Unknown command: %s\n", command);
+       }
+       return 0;
+}
+
+static void
+do_smtp_to_file (FILE *peer, FILE *output)
+{
+       char buf[4096];
+       ssize_t bytes;
+       char greeting[] = "220 localhost smtp-dummy\r\n";
+       char *line = NULL;
+       size_t line_size;
+       ssize_t line_len;
+
+       fprintf (peer, "220 localhost smtp-dummy\r\n");
+       fflush (peer);
+
+       while ((line_len = getline (&line, &line_size, peer)) != -1) {
+               if (process_command (peer, output, line))
+                       break;
+       }
+
+       free (line);
+}
+
+int
+main (int argc, char *argv[])
+{
+       char *output_filename;
+       FILE *peer_file, *output;
+       int sock, peer, err;
+       struct sockaddr_in addr, peer_addr;
+       struct hostent *hostinfo;
+       socklen_t peer_addr_len;
+       int reuse;
+
+       if (argc != 2) {
+               fprintf (stderr, "Usage: %s <output-file>\n", argv[0]);
+               exit (1);
+       }
+
+       output_filename = argv[1];
+       output = fopen (output_filename, "w");
+       if (output == NULL) {
+               fprintf (stderr, "Failed to open %s for writing: %s\n",
+                        output_filename, strerror (errno));
+               exit (1);
+       }
+
+       sock = socket (AF_INET, SOCK_STREAM, 0);
+       if (sock == -1) {
+               fprintf (stderr, "Error: socket() failed: %s\n",
+                        strerror (errno));
+               exit (1);
+       }
+
+       reuse = 1;
+       err = setsockopt (sock, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof (reuse));
+       if (err) {
+               fprintf (stderr, "Error: setsockopt() failed: %s\n",
+                        strerror (errno));
+               exit (1);
+       }
+
+       hostinfo = gethostbyname ("localhost");
+       if (hostinfo == NULL) {
+               fprintf (stderr, "Unknown host: localhost\n");
+               exit (1);
+       }
+
+       addr.sin_family = AF_INET;
+       addr.sin_port = htons (25025);
+       addr.sin_addr = *(struct in_addr *) hostinfo->h_addr;
+       err = bind (sock, (struct sockaddr *) &addr, sizeof(addr));
+       if (err) {
+               fprintf (stderr, "Error: bind() failed: %s\n",
+                        strerror (errno));
+               close (sock);
+               exit (1);
+       }
+
+       err = listen (sock, 1);
+       if (err) {
+               fprintf (stderr, "Error: listen() failed: %s\n",
+                        strerror (errno));
+               close (sock);
+               exit (1);
+       }
+
+       peer_addr_len = sizeof (peer_addr);
+       peer = accept (sock, (struct sockaddr *) &peer_addr, &peer_addr_len);
+       if (peer == -1) {
+               fprintf (stderr, "Error: accept() failed: %s\n",
+                        strerror (errno));
+               exit (1);
+       }
+
+       peer_file = fdopen (peer, "w+");
+       if (peer_file == NULL) {
+               fprintf (stderr, "Error: fdopen() failed: %s\n",
+                        strerror (errno));
+               return;
+       }
+
+       do_smtp_to_file (peer_file, output);
+
+       fclose (output);
+       fclose (peer_file);
+       close (sock);
+
+       return 0;
+}
diff --git a/test/test-lib.sh b/test/test-lib.sh
new file mode 100644 (file)
index 0000000..8ecc9a0
--- /dev/null
@@ -0,0 +1,979 @@
+#!/bin/bash
+#
+# Copyright (c) 2005 Junio C Hamano
+#
+# 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, see http://www.gnu.org/licenses/ .
+
+if [ ${BASH_VERSINFO[0]} -lt 4 ]; then
+    echo "Error: The notmuch test suite requires a bash version >= 4.0"
+    echo "due to use of associative arrays within the test suite."
+    echo "Please try again with a newer bash (or help us fix the"
+    echo "test suite to be more portable). Thanks."
+    exit 1
+fi
+
+# if --tee was passed, write the output not only to the terminal, but
+# additionally to the file test-results/$BASENAME.out, too.
+case "$GIT_TEST_TEE_STARTED, $* " in
+done,*)
+       # do not redirect again
+       ;;
+*' --tee '*|*' --va'*)
+       mkdir -p test-results
+       BASE=test-results/$(basename "$0" .sh)
+       (GIT_TEST_TEE_STARTED=done ${SHELL-sh} "$0" "$@" 2>&1;
+        echo $? > $BASE.exit) | tee $BASE.out
+       test "$(cat $BASE.exit)" = 0
+       exit
+       ;;
+esac
+
+# Keep the original TERM for say_color
+ORIGINAL_TERM=$TERM
+
+# For repeatability, reset the environment to known value.
+LANG=C
+LC_ALL=C
+PAGER=cat
+TZ=UTC
+TERM=dumb
+export LANG LC_ALL PAGER TERM TZ
+GIT_TEST_CMP=${GIT_TEST_CMP:-diff -u}
+
+# Protect ourselves from common misconfiguration to export
+# CDPATH into the environment
+unset CDPATH
+
+unset GREP_OPTIONS
+
+# Convenience
+#
+# A regexp to match 5 and 40 hexdigits
+_x05='[0-9a-f][0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x40="$_x05$_x05$_x05$_x05$_x05$_x05$_x05$_x05"
+
+_x04='[0-9a-f][0-9a-f][0-9a-f][0-9a-f]'
+_x32="$_x04$_x04$_x04$_x04$_x04$_x04$_x04$_x04"
+
+# Each test should start with something like this, after copyright notices:
+#
+# test_description='Description of this test...
+# This test checks if command xyzzy does the right thing...
+# '
+# . ./test-lib.sh
+[ "x$ORIGINAL_TERM" != "xdumb" ] && (
+               TERM=$ORIGINAL_TERM &&
+               export TERM &&
+               [ -t 1 ] &&
+               tput bold >/dev/null 2>&1 &&
+               tput setaf 1 >/dev/null 2>&1 &&
+               tput sgr0 >/dev/null 2>&1
+       ) &&
+       color=t
+
+while test "$#" -ne 0
+do
+       case "$1" in
+       -d|--d|--de|--deb|--debu|--debug)
+               debug=t; shift ;;
+       -i|--i|--im|--imm|--imme|--immed|--immedi|--immedia|--immediat|--immediate)
+               immediate=t; shift ;;
+       -l|--l|--lo|--lon|--long|--long-|--long-t|--long-te|--long-tes|--long-test|--long-tests)
+               GIT_TEST_LONG=t; export GIT_TEST_LONG; shift ;;
+       -h|--h|--he|--hel|--help)
+               help=t; shift ;;
+       -v|--v|--ve|--ver|--verb|--verbo|--verbos|--verbose)
+               verbose=t; shift ;;
+       -q|--q|--qu|--qui|--quie|--quiet)
+               quiet=t; shift ;;
+       --with-dashes)
+               with_dashes=t; shift ;;
+       --no-color)
+               color=; shift ;;
+       --no-python)
+               # noop now...
+               shift ;;
+       --va|--val|--valg|--valgr|--valgri|--valgrin|--valgrind)
+               valgrind=t; verbose=t; shift ;;
+       --tee)
+               shift ;; # was handled already
+       --root=*)
+               root=$(expr "z$1" : 'z[^=]*=\(.*\)')
+               shift ;;
+       *)
+               echo "error: unknown test option '$1'" >&2; exit 1 ;;
+       esac
+done
+
+if test -n "$color"; then
+       say_color () {
+               (
+               TERM=$ORIGINAL_TERM
+               export TERM
+               case "$1" in
+                       error) tput bold; tput setaf 1;; # bold red
+                       skip)  tput bold; tput setaf 2;; # bold green
+                       pass)  tput setaf 2;;            # green
+                       info)  tput setaf 3;;            # brown
+                       *) test -n "$quiet" && return;;
+               esac
+               shift
+               printf " "
+                printf "$@"
+               tput sgr0
+               )
+       }
+else
+       say_color() {
+               test -z "$1" && test -n "$quiet" && return
+               shift
+               printf " "
+                printf "$@"
+       }
+fi
+
+error () {
+       say_color error "error: $*"
+       GIT_EXIT_OK=t
+       exit 1
+}
+
+say () {
+       say_color info "$*"
+}
+
+test "${test_description}" != "" ||
+error "Test script did not set test_description."
+
+if test "$help" = "t"
+then
+       echo "Tests ${test_description}"
+       exit 0
+fi
+
+echo $(basename "$0"): "Testing ${test_description}"
+
+exec 5>&1
+if test "$verbose" = "t"
+then
+       exec 4>&2 3>&1
+else
+       exec 4>/dev/null 3>/dev/null
+fi
+
+test_failure=0
+test_count=0
+test_fixed=0
+test_broken=0
+test_success=0
+
+die () {
+       code=$?
+       if test -n "$GIT_EXIT_OK"
+       then
+               exit $code
+       else
+               echo >&5 "FATAL: Unexpected exit with code $code"
+               exit 1
+       fi
+}
+
+GIT_EXIT_OK=
+trap 'die' EXIT
+
+test_decode_color () {
+       sed     -e 's/.\[1m/<WHITE>/g' \
+               -e 's/.\[31m/<RED>/g' \
+               -e 's/.\[32m/<GREEN>/g' \
+               -e 's/.\[33m/<YELLOW>/g' \
+               -e 's/.\[34m/<BLUE>/g' \
+               -e 's/.\[35m/<MAGENTA>/g' \
+               -e 's/.\[36m/<CYAN>/g' \
+               -e 's/.\[m/<RESET>/g'
+}
+
+q_to_nul () {
+       perl -pe 'y/Q/\000/'
+}
+
+q_to_cr () {
+       tr Q '\015'
+}
+
+append_cr () {
+       sed -e 's/$/Q/' | tr Q '\015'
+}
+
+remove_cr () {
+       tr '\015' Q | sed -e 's/Q$//'
+}
+
+# Notmuch helper functions
+increment_mtime_amount=0
+increment_mtime ()
+{
+    dir="$1"
+
+    increment_mtime_amount=$((increment_mtime_amount + 1))
+    touch -d "+${increment_mtime_amount} seconds" "$dir"
+}
+
+# Generate a new message in the mail directory, with a unique message
+# ID and subject. The message is not added to the index.
+#
+# After this function returns, the filename of the generated message
+# is available as $gen_msg_filename and the message ID is available as
+# $gen_msg_id .
+#
+# This function supports named parameters with the bash syntax for
+# assigning a value to an associative array ([name]=value). The
+# supported parameters are:
+#
+#  [dir]=directory/of/choice
+#
+#      Generate the message in directory 'directory/of/choice' within
+#      the mail store. The directory will be created if necessary.
+#
+#  [body]=text
+#
+#      Text to use as the body of the email message
+#
+#  '[from]="Some User <user@example.com>"'
+#  '[to]="Some User <user@example.com>"'
+#  '[subject]="Subject of email message"'
+#  '[date]="RFC 822 Date"'
+#
+#      Values for email headers. If not provided, default values will
+#      be generated instead.
+#
+#  '[cc]="Some User <user@example.com>"'
+#  [reply-to]=some-address
+#  [in-reply-to]=<message-id>
+#  [references]=<message-id>
+#  [content-type]=content-type-specification
+#  '[header]=full header line, including keyword'
+#
+#      Additional values for email headers. If these are not provided
+#      then the relevant headers will simply not appear in the
+#      message.
+#
+#  '[id]=message-id'
+#
+#      Controls the message-id of the created message.
+gen_msg_cnt=0
+gen_msg_filename=""
+gen_msg_id=""
+generate_message ()
+{
+    # This is our (bash-specific) magic for doing named parameters
+    local -A template="($@)"
+    local additional_headers
+
+    gen_msg_cnt=$((gen_msg_cnt + 1))
+    gen_msg_name=msg-$(printf "%03d" $gen_msg_cnt)
+
+    if [ -z "${template[id]}" ]; then
+       gen_msg_id="${gen_msg_name}@notmuch-test-suite"
+    else
+       gen_msg_id="${template[id]}"
+    fi
+
+    if [ -z "${template[dir]}" ]; then
+       gen_msg_filename="${MAIL_DIR}/$gen_msg_name"
+    else
+       gen_msg_filename="${MAIL_DIR}/${template[dir]}/$gen_msg_name"
+       mkdir -p "$(dirname "$gen_msg_filename")"
+    fi
+
+    if [ -z "${template[body]}" ]; then
+       template[body]="This is just a test message (#${gen_msg_cnt})"
+    fi
+
+    if [ -z "${template[from]}" ]; then
+       template[from]="Notmuch Test Suite <test_suite@notmuchmail.org>"
+    fi
+
+    if [ -z "${template[to]}" ]; then
+       template[to]="Notmuch Test Suite <test_suite@notmuchmail.org>"
+    fi
+
+    if [ -z "${template[subject]}" ]; then
+       template[subject]="Test message #${gen_msg_cnt}"
+    fi
+
+    if [ -z "${template[date]}" ]; then
+       template[date]="Tue, 05 Jan 2001 15:43:57 -0000"
+    fi
+
+    additional_headers=""
+    if [ ! -z "${template[header]}" ]; then
+       additional_headers="${template[header]}
+${additional_headers}"
+    fi
+
+    if [ ! -z "${template[reply-to]}" ]; then
+       additional_headers="Reply-To: ${template[reply-to]}
+${additional_headers}"
+    fi
+
+    if [ ! -z "${template[in-reply-to]}" ]; then
+       additional_headers="In-Reply-To: ${template[in-reply-to]}
+${additional_headers}"
+    fi
+
+    if [ ! -z "${template[cc]}" ]; then
+       additional_headers="Cc: ${template[cc]}
+${additional_headers}"
+    fi
+
+    if [ ! -z "${template[references]}" ]; then
+       additional_headers="References: ${template[references]}
+${additional_headers}"
+    fi
+
+    if [ ! -z "${template[content-type]}" ]; then
+       additional_headers="Content-Type: ${template[content-type]}
+${additional_headers}"
+    fi
+
+
+cat <<EOF >"$gen_msg_filename"
+From: ${template[from]}
+To: ${template[to]}
+Message-Id: <${gen_msg_id}>
+Subject: ${template[subject]}
+Date: ${template[date]}
+${additional_headers}
+${template[body]}
+EOF
+
+    # Ensure that the mtime of the containing directory is updated
+    increment_mtime "$(dirname "${gen_msg_filename}")"
+}
+
+# Generate a new message and add it to the database.
+#
+# All of the arguments and return values supported by generate_message
+# are also supported here, so see that function for details.
+add_message ()
+{
+    generate_message "$@" &&
+    notmuch new > /dev/null
+}
+
+# Generate a corpus of email and add it to the database.
+#
+# This corpus is fixed, (it happens to be 50 messages from early in
+# the history of the notmuch mailing list), which allows for reliably
+# testing commands that need to operate on a not-totally-trivial
+# number of messages.
+add_email_corpus ()
+{
+    rm -rf ${MAIL_DIR}
+    if [ -d ../corpus.mail ]; then
+       cp -a ../corpus.mail ${MAIL_DIR}
+    else
+       cp -a ../corpus ${MAIL_DIR}
+       notmuch new >/dev/null
+       cp -a ${MAIL_DIR} ../corpus.mail
+    fi
+}
+
+test_begin_subtest ()
+{
+    test_subtest_name="$1"
+}
+
+# Pass test if two arguments match
+#
+# Note: Unlike all other test_expect_* functions, this function does
+# not accept a test name. Instead, the caller should call
+# test_begin_subtest before calling this function in order to set the
+# name.
+test_expect_equal ()
+{
+       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 2 ||
+       error "bug in the test script: not 2 or 3 parameters to test_expect_equal"
+
+       output="$1"
+       expected="$2"
+       if ! test_skip "$@"
+       then
+               if [ "$output" = "$expected" ]; then
+                       test_ok_ "$test_subtest_name"
+               else
+                       testname=$this_test.$test_count
+                       echo "$expected" > $testname.expected
+                       echo "$output" > $testname.output
+                       test_failure_ "$test_subtest_name" "$(diff -u $testname.expected $testname.output)"
+               fi
+    fi
+}
+
+test_expect_equal_failure ()
+{
+       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 2 ||
+       error "bug in the test script: not 2 or 3 parameters to test_expect_equal"
+
+       output="$1"
+       expected="$2"
+       if ! test_skip "$@"
+       then
+               if [ "$output" = "$expected" ]; then
+                       test_known_broken_ok_ "$test_subtest_name"
+               else
+                       test_known_broken_failure_ "$test_subtest_name"
+               fi
+    fi
+}
+
+NOTMUCH_NEW ()
+{
+    notmuch new | grep -v -E -e '^Processed [0-9]*( total)? file|Found [0-9]* total file'
+}
+
+notmuch_search_sanitize ()
+{
+    sed -r -e 's/("?thread"?: ?)("?)................("?)/\1\2XXX\3/'
+}
+
+NOTMUCH_SHOW_FILENAME_SQUELCH='s,filename:.*/mail,filename:/XXX/mail,'
+notmuch_show_sanitize ()
+{
+    sed -e "$NOTMUCH_SHOW_FILENAME_SQUELCH"
+}
+
+# End of notmuch helper functions
+
+# Use test_set_prereq to tell that a particular prerequisite is available.
+# The prerequisite can later be checked for in two ways:
+#
+# - Explicitly using test_have_prereq.
+#
+# - Implicitly by specifying the prerequisite tag in the calls to
+#   test_expect_{success,failure,code}.
+#
+# The single parameter is the prerequisite tag (a simple word, in all
+# capital letters by convention).
+
+test_set_prereq () {
+       satisfied="$satisfied$1 "
+}
+satisfied=" "
+
+test_have_prereq () {
+       case $satisfied in
+       *" $1 "*)
+               : yes, have it ;;
+       *)
+               ! : nope ;;
+       esac
+}
+
+# You are not expected to call test_ok_ and test_failure_ directly, use
+# the text_expect_* functions instead.
+
+test_ok_ () {
+       test_success=$(($test_success + 1))
+       say_color pass "%-6s" "PASS"
+       echo " $@"
+}
+
+test_failure_ () {
+       test_failure=$(($test_failure + 1))
+       say_color error "%-6s" "FAIL"
+       echo " $1"
+       shift
+       echo "$@" | sed -e 's/^/        /'
+       test "$immediate" = "" || { GIT_EXIT_OK=t; exit 1; }
+}
+
+test_known_broken_ok_ () {
+       test_fixed=$(($test_fixed+1))
+       say_color pass "%-6s" "FIXED"
+       echo " $@"
+}
+
+test_known_broken_failure_ () {
+       test_broken=$(($test_broken+1))
+       say_color pass "%-6s" "BROKEN"
+       echo " $@"
+}
+
+test_debug () {
+       test "$debug" = "" || eval "$1"
+}
+
+test_run_ () {
+       test_cleanup=:
+       eval >&3 2>&4 "$1"
+       eval_ret=$?
+       eval >&3 2>&4 "$test_cleanup"
+       return 0
+}
+
+test_skip () {
+       test_count=$(($test_count+1))
+       to_skip=
+       for skp in $NOTMUCH_SKIP_TESTS
+       do
+               case $this_test.$test_count in
+               $skp)
+                       to_skip=t
+               esac
+       done
+       if test -z "$to_skip" && test -n "$prereq" &&
+          ! test_have_prereq "$prereq"
+       then
+               to_skip=t
+       fi
+       case "$to_skip" in
+       t)
+               say_color skip >&3 "skipping test: $@"
+               say_color skip "%-6s" "SKIP"
+               echo " $1"
+               : true
+               ;;
+       *)
+               false
+               ;;
+       esac
+}
+
+test_expect_failure () {
+       test "$#" = 3 && { prereq=$1; shift; } || prereq=
+       test "$#" = 2 ||
+       error "bug in the test script: not 2 or 3 parameters to test-expect-failure"
+       if ! test_skip "$@"
+       then
+               test_run_ "$2"
+               if [ "$?" = 0 -a "$eval_ret" = 0 ]
+               then
+                       test_known_broken_ok_ "$1"
+               else
+                       test_known_broken_failure_ "$1"
+               fi
+       fi
+}
+
+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"
+       if ! test_skip "$@"
+       then
+               test_run_ "$2"
+               if [ "$?" = 0 -a "$eval_ret" = 0 ]
+               then
+                       test_ok_ "$1"
+               else
+                       test_failure_ "$@"
+               fi
+       fi
+}
+
+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"
+       if ! test_skip "$@"
+       then
+               test_run_ "$3"
+               if [ "$?" = 0 -a "$eval_ret" = "$1" ]
+               then
+                       test_ok_ "$2"
+               else
+                       test_failure_ "$@"
+               fi
+       fi
+}
+
+# test_external runs external test scripts that provide continuous
+# test output about their progress, and succeeds/fails on
+# zero/non-zero exit code.  It outputs the test output on stdout even
+# in non-verbose mode, and announces the external script with "* run
+# <n>: ..." before running it.  When providing relative paths, keep in
+# mind that all scripts run in "trash directory".
+# Usage: test_external description command arguments...
+# Example: test_external 'Perl API' perl ../path/to/test.pl
+test_external () {
+       test "$#" = 4 && { prereq=$1; shift; } || prereq=
+       test "$#" = 3 ||
+       error >&5 "bug in the test script: not 3 or 4 parameters to test_external"
+       descr="$1"
+       shift
+       if ! test_skip "$descr" "$@"
+       then
+               # Announce the script to reduce confusion about the
+               # test output that follows.
+               say_color "" " run $test_count: $descr ($*)"
+               # Run command; redirect its stderr to &4 as in
+               # test_run_, but keep its stdout on our stdout even in
+               # non-verbose mode.
+               "$@" 2>&4
+               if [ "$?" = 0 ]
+               then
+                       test_ok_ "$descr"
+               else
+                       test_failure_ "$descr" "$@"
+               fi
+       fi
+}
+
+# Like test_external, but in addition tests that the command generated
+# no output on stderr.
+test_external_without_stderr () {
+       # The temporary file has no (and must have no) security
+       # implications.
+       tmp="$TMPDIR"; if [ -z "$tmp" ]; then tmp=/tmp; fi
+       stderr="$tmp/git-external-stderr.$$.tmp"
+       test_external "$@" 4> "$stderr"
+       [ -f "$stderr" ] || error "Internal error: $stderr disappeared."
+       descr="no stderr: $1"
+       shift
+       if [ ! -s "$stderr" ]; then
+               rm "$stderr"
+               test_ok_ "$descr"
+       else
+               if [ "$verbose" = t ]; then
+                       output=`echo; echo Stderr is:; cat "$stderr"`
+               else
+                       output=
+               fi
+               # rm first in case test_failure exits.
+               rm "$stderr"
+               test_failure_ "$descr" "$@" "$output"
+       fi
+}
+
+# This is not among top-level (test_expect_success | test_expect_failure)
+# but is a prefix that can be used in the test script, like:
+#
+#      test_expect_success 'complain and die' '
+#           do something &&
+#           do something else &&
+#          test_must_fail git checkout ../outerspace
+#      '
+#
+# Writing this as "! git checkout ../outerspace" is wrong, because
+# the failure could be due to a segv.  We want a controlled failure.
+
+test_must_fail () {
+       "$@"
+       test $? -gt 0 -a $? -le 129 -o $? -gt 192
+}
+
+# test_cmp is a helper function to compare actual and expected output.
+# You can use it like:
+#
+#      test_expect_success 'foo works' '
+#              echo expected >expected &&
+#              foo >actual &&
+#              test_cmp expected actual
+#      '
+#
+# This could be written as either "cmp" or "diff -u", but:
+# - cmp's output is not nearly as easy to read as diff -u
+# - not all diff versions understand "-u"
+
+test_cmp() {
+       $GIT_TEST_CMP "$@"
+}
+
+# This function can be used to schedule some commands to be run
+# unconditionally at the end of the test to restore sanity:
+#
+#      test_expect_success 'test core.capslock' '
+#              git config core.capslock true &&
+#              test_when_finished "git config --unset core.capslock" &&
+#              hello world
+#      '
+#
+# That would be roughly equivalent to
+#
+#      test_expect_success 'test core.capslock' '
+#              git config core.capslock true &&
+#              hello world
+#              git config --unset core.capslock
+#      '
+#
+# except that the greeting and config --unset must both succeed for
+# the test to pass.
+
+test_when_finished () {
+       test_cleanup="{ $*
+               } && (exit \"\$eval_ret\"); eval_ret=\$?; $test_cleanup"
+}
+
+test_done () {
+       GIT_EXIT_OK=t
+       test_results_dir="$TEST_DIRECTORY/test-results"
+       mkdir -p "$test_results_dir"
+       test_results_path="$test_results_dir/${0%.sh}-$$"
+
+       echo "total $test_count" >> $test_results_path
+       echo "success $test_success" >> $test_results_path
+       echo "fixed $test_fixed" >> $test_results_path
+       echo "broken $test_broken" >> $test_results_path
+       echo "failed $test_failure" >> $test_results_path
+       echo "" >> $test_results_path
+
+       echo
+
+       if [ "$test_failure" = "0" ]; then
+           rm -rf "$remove_tmp"
+           exit 0
+       else
+           exit 1
+       fi
+}
+
+test_emacs () {
+       # Construct a little test script here for the benefit of the user,
+       # (who can easily run "run_emacs" to get the same emacs environment
+       # for investigating any failures).    
+       cat <<EOF > run_emacs
+#!/bin/sh
+export PATH=$PATH
+export NOTMUCH_CONFIG=$NOTMUCH_CONFIG
+
+# We assume that the user will give a command-line argument only if
+# wanting to run in batch mode.
+if [ \$# -gt 0 ]; then
+       BATCH=--batch
+fi
+
+# Here's what we are using here:
+#
+# --batch:             Quit after given commands and print all (messages)
+#
+# --no-init-file       Don't load users ~/.emacs
+#
+# --no-site-file       Don't load the site-wide startup stuff
+#
+# --directory          Ensure that the local notmuch.el source is found
+#
+# --load               Force loading of notmuch.el
+#
+# notmuch-test-wait    Function for tests to use to wait for process completion
+#
+# message-signature    Avoiding appending user's signature on messages
+#
+# set-frame-width      80 columns (avoids crazy 10-column default of --batch)
+
+emacs \$BATCH --no-init-file --no-site-file \
+       --directory ../../emacs --load notmuch.el \
+       --eval "(defun notmuch-test-wait ()
+                       (while (get-buffer-process (current-buffer))
+                               (sleep-for 0.1)))" \
+       --eval "(setq message-signature nil)" \
+       --eval "(progn (set-frame-width (window-frame (get-buffer-window)) 80) \$@)"
+EOF
+       chmod a+x ./run_emacs
+       ./run_emacs "$@"
+}
+
+
+find_notmuch_path ()
+{
+    dir="$1"
+
+    while [ -n "$dir" ]; do
+       bin="$dir/notmuch"
+       if [ -x "$bin" ]; then
+           echo "$dir"
+           return
+       fi
+       dir="$(dirname "$dir")"
+       if [ "$dir" = "/" ]; then
+           break
+       fi
+    done
+}
+
+# Test the binaries we have just built.  The tests are kept in
+# test/ subdirectory and are run in 'trash directory' subdirectory.
+TEST_DIRECTORY=$(pwd)
+if test -n "$valgrind"
+then
+       make_symlink () {
+               test -h "$2" &&
+               test "$1" = "$(readlink "$2")" || {
+                       # be super paranoid
+                       if mkdir "$2".lock
+                       then
+                               rm -f "$2" &&
+                               ln -s "$1" "$2" &&
+                               rm -r "$2".lock
+                       else
+                               while test -d "$2".lock
+                               do
+                                       say "Waiting for lock on $2."
+                                       sleep 1
+                               done
+                       fi
+               }
+       }
+
+       make_valgrind_symlink () {
+               # handle only executables
+               test -x "$1" || return
+
+               base=$(basename "$1")
+               symlink_target=$TEST_DIRECTORY/../$base
+               # do not override scripts
+               if test -x "$symlink_target" &&
+                   test ! -d "$symlink_target" &&
+                   test "#!" != "$(head -c 2 < "$symlink_target")"
+               then
+                       symlink_target=../valgrind.sh
+               fi
+               case "$base" in
+               *.sh|*.perl)
+                       symlink_target=../unprocessed-script
+               esac
+               # create the link, or replace it if it is out of date
+               make_symlink "$symlink_target" "$GIT_VALGRIND/bin/$base" || exit
+       }
+
+       # override notmuch executable in TEST_DIRECTORY/..
+       GIT_VALGRIND=$TEST_DIRECTORY/valgrind
+       mkdir -p "$GIT_VALGRIND"/bin
+       make_valgrind_symlink $TEST_DIRECTORY/../notmuch
+       OLDIFS=$IFS
+       IFS=:
+       for path in $PATH
+       do
+               ls "$path"/notmuch 2> /dev/null |
+               while read file
+               do
+                       make_valgrind_symlink "$file"
+               done
+       done
+       IFS=$OLDIFS
+       PATH=$GIT_VALGRIND/bin:$PATH
+       GIT_EXEC_PATH=$GIT_VALGRIND/bin
+       export GIT_VALGRIND
+else # normal case
+       notmuch_path=`find_notmuch_path "$TEST_DIRECTORY"`
+       test -n "$notmuch_path" && PATH="$notmuch_path:$PATH"
+fi
+export PATH
+
+# Test repository
+test="tmp.$(basename "$0" .sh)"
+test -n "$root" && test="$root/$test"
+case "$test" in
+/*) TMP_DIRECTORY="$test" ;;
+ *) TMP_DIRECTORY="$TEST_DIRECTORY/$test" ;;
+esac
+test ! -z "$debug" || remove_tmp=$TMP_DIRECTORY
+rm -fr "$test" || {
+       GIT_EXIT_OK=t
+       echo >&5 "FATAL: Cannot prepare test area"
+       exit 1
+}
+
+MAIL_DIR="${TMP_DIRECTORY}/mail"
+export NOTMUCH_CONFIG="${TMP_DIRECTORY}/notmuch-config"
+
+mkdir -p "${test}"
+mkdir -p "${MAIL_DIR}"
+
+cat <<EOF >"${NOTMUCH_CONFIG}"
+[database]
+path=${MAIL_DIR}
+
+[user]
+name=Notmuch Test Suite
+primary_email=test_suite@notmuchmail.org
+other_email=test_suite_other@notmuchmail.org;test_suite@otherdomain.org
+EOF
+
+
+# Use -P to resolve symlinks in our working directory so that the cwd
+# in subprocesses like git equals our $PWD (for pathname comparisons).
+cd -P "$test" || error "Cannot setup test environment"
+
+this_test=${0##*/}
+this_test=${this_test%%-*}
+for skp in $NOTMUCH_SKIP_TESTS
+do
+       to_skip=
+       for skp in $NOTMUCH_SKIP_TESTS
+       do
+               case "$this_test" in
+               $skp)
+                       to_skip=t
+               esac
+       done
+       case "$to_skip" in
+       t)
+               say_color skip >&3 "skipping test $this_test altogether"
+               say_color skip "skip all tests in $this_test"
+               test_done
+       esac
+done
+
+# Provide an implementation of the 'yes' utility
+yes () {
+       if test $# = 0
+       then
+               y=y
+       else
+               y="$*"
+       fi
+
+       while echo "$y"
+       do
+               :
+       done
+}
+
+# Fix some commands on Windows
+case $(uname -s) in
+*MINGW*)
+       # Windows has its own (incompatible) sort and find
+       sort () {
+               /usr/bin/sort "$@"
+       }
+       find () {
+               /usr/bin/find "$@"
+       }
+       sum () {
+               md5sum "$@"
+       }
+       # git sees Windows-style pwd
+       pwd () {
+               builtin pwd -W
+       }
+       # no POSIX permissions
+       # backslashes in pathspec are converted to '/'
+       # exec does not inherit the PID
+       ;;
+*)
+       test_set_prereq POSIXPERM
+       test_set_prereq BSLASHPSPEC
+       test_set_prereq EXECKEEPSPID
+       ;;
+esac
+
+test -z "$NO_PERL" && test_set_prereq PERL
+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
diff --git a/test/thread-naming b/test/thread-naming
new file mode 100755 (executable)
index 0000000..129536e
--- /dev/null
@@ -0,0 +1,182 @@
+#!/bin/bash
+test_description="naming of threads with changing subject"
+. ./test-lib.sh
+
+test_begin_subtest 'Generate some messages'
+add_message '[subject]="thread-naming: Initial thread subject"' \
+            '[date]="Fri, 05 Jan 2001 15:43:56 -0000"'
+first=${gen_msg_cnt}
+parent=${gen_msg_id}
+add_message '[subject]="thread-naming: Older changed subject"' \
+            '[date]="Sat, 06 Jan 2001 15:43:56 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+add_message '[subject]="thread-naming: Newer changed subject"' \
+            '[date]="Sun, 07 Jan 2001 15:43:56 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+add_message '[subject]="thread-naming: Final thread subject"' \
+            '[date]="Mon, 08 Jan 2001 15:43:56 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+final=${gen_msg_id}
+
+test_begin_subtest "Initial thread name (oldest-first search)"
+output=$(notmuch search --sort=oldest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-05 [4/4] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Initial thread name (newest-first search)"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-08 [4/4] Notmuch Test Suite; thread-naming: Final thread subject (inbox unread)"
+
+# Remove oldest and newest messages from search results
+notmuch tag -inbox id:$parent or id:$final
+
+test_begin_subtest "Changed thread name (oldest-first search)"
+output=$(notmuch search --sort=oldest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-06 [2/4] Notmuch Test Suite; thread-naming: Older changed subject (inbox unread)"
+
+test_begin_subtest "Changed thread name (newest-first search)"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-07 [2/4] Notmuch Test Suite; thread-naming: Newer changed subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Re:)"
+add_message '[subject]="Re: thread-naming: Initial thread subject"' \
+            '[date]="Tue, 09 Jan 2001 15:43:45 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-09 [3/5] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Aw:)"
+add_message '[subject]="Aw: thread-naming: Initial thread subject"' \
+            '[date]="Wed, 10 Jan 2001 15:43:45 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-10 [4/6] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Vs:)"
+add_message '[subject]="Vs: thread-naming: Initial thread subject"' \
+            '[date]="Thu, 11 Jan 2001 15:43:45 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-11 [5/7] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest "Ignore added reply prefix (Sv:)"
+add_message '[subject]="Sv: thread-naming: Initial thread subject"' \
+            '[date]="Fri, 12 Jan 2001 15:43:45 -0000"' \
+            "[in-reply-to]=\<$parent\>"
+output=$(notmuch search --sort=newest-first thread-naming and tag:inbox | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2001-01-12 [6/8] Notmuch Test Suite; thread-naming: Initial thread subject (inbox unread)"
+
+test_begin_subtest 'Test order of messages in "notmuch show"'
+output=$(notmuch show thread-naming | notmuch_show_sanitize)
+test_expect_equal "$output" "\fmessage{ id:msg-$(printf "%03d" $first)@notmuch-test-suite depth:0 match:1 filename:/XXX/mail/msg-$(printf "%03d" $first)
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-05) (unread)
+Subject: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Fri, 05 Jan 2001 15:43:56 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$first)
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 1)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 1)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-06) (inbox unread)
+Subject: thread-naming: Older changed subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Sat, 06 Jan 2001 15:43:56 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 1)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 2)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 2)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-07) (inbox unread)
+Subject: thread-naming: Newer changed subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Sun, 07 Jan 2001 15:43:56 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 2)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 3)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 3)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-08) (unread)
+Subject: thread-naming: Final thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Mon, 08 Jan 2001 15:43:56 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 3)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 4)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 4)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-09) (inbox unread)
+Subject: Re: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Tue, 09 Jan 2001 15:43:45 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 4)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 5)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 5)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-10) (inbox unread)
+Subject: Aw: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Wed, 10 Jan 2001 15:43:45 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 5)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 6)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 6)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-11) (inbox unread)
+Subject: Vs: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Thu, 11 Jan 2001 15:43:45 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 6)))
+\fpart}
+\fbody}
+\fmessage}
+\fmessage{ id:msg-$(printf "%03d" $((first + 7)))@notmuch-test-suite depth:1 match:1 filename:/XXX/mail/msg-$(printf "%03d" $((first + 7)))
+\fheader{
+Notmuch Test Suite <test_suite@notmuchmail.org> (2001-01-12) (inbox unread)
+Subject: Sv: thread-naming: Initial thread subject
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Notmuch Test Suite <test_suite@notmuchmail.org>
+Date: Fri, 12 Jan 2001 15:43:45 -0000
+\fheader}
+\fbody{
+\fpart{ ID: 1, Content-type: text/plain
+This is just a test message (#$((first + 7)))
+\fpart}
+\fbody}
+\fmessage}"
+test_done
diff --git a/test/thread-order b/test/thread-order
new file mode 100755 (executable)
index 0000000..1819fce
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+test_description="threading when messages received out of order"
+. ./test-lib.sh
+
+test_begin_subtest "Adding initial child message"
+generate_message [body]=foo "[in-reply-to]=\<parent-id\>" [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching returns the message"
+output=$(notmuch search foo | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+
+test_begin_subtest "Adding second child message"
+generate_message [body]=foo "[in-reply-to]=\<parent-id\>" [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching returns both messages in one thread"
+output=$(notmuch search foo | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [2/2] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+
+test_begin_subtest "Adding parent message"
+generate_message [body]=foo [id]=parent-id [subject]=brokenthreadtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
+output=$(NOTMUCH_NEW)
+test_expect_equal "$output" "Added 1 new message to the database."
+
+test_begin_subtest "Searching returns all three messages in one thread"
+output=$(notmuch search foo | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [3/3] Notmuch Test Suite; brokenthreadtest (inbox unread)"
+
+test_done
diff --git a/test/uuencode b/test/uuencode
new file mode 100755 (executable)
index 0000000..d509320
--- /dev/null
@@ -0,0 +1,34 @@
+#!/bin/bash
+test_description="handling of uuencoded data"
+. ./test-lib.sh
+
+add_message [subject]=uuencodetest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' \
+'[body]="This message is used to ensure that notmuch correctly handles a
+message containing a block of uuencoded data. First, we have a marker
+this content beforeuudata . Then we beging the uunencoded data itself:
+
+begin 644 bogus-uuencoded-data
+M0123456789012345678901234567890123456789012345678901234567890
+MOBVIOUSLY, THIS IS NOT ANY SORT OF USEFUL UUNECODED DATA.    
+MINSTEAD THIS IS JUST A WAY TO ENSURE THAT THIS BLOCK OF DATA 
+MIS CORRECTLY IGNORED WHEN NOTMUCH CREATES ITS INDEX. SO WE   
+MINCLUDE A DURINGUUDATA MARKER THAT SHOULD NOT RESULT IN ANY  
+MSEARCH RESULT.                                               
+\\\`
+end
+
+Finally, we have our afteruudata marker as well."'
+
+test_begin_subtest "Ensure content before uu data is indexed"
+output=$(notmuch search beforeuudata | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; uuencodetest (inbox unread)"
+
+test_begin_subtest "Ensure uu data is not indexed"
+output=$(notmuch search DURINGUUDATA | notmuch_search_sanitize)
+test_expect_equal "$output" ""
+
+test_begin_subtest "Ensure content after uu data is indexed"
+output=$(notmuch search afteruudata | notmuch_search_sanitize)
+test_expect_equal "$output" "thread:XXX   2000-01-01 [1/1] Notmuch Test Suite; uuencodetest (inbox unread)"
+
+test_done
diff --git a/test/valgrind/suppressions b/test/valgrind/suppressions
new file mode 100644 (file)
index 0000000..6abf8b2
--- /dev/null
@@ -0,0 +1,6 @@
+{
+   zlib inflation uses uninitialize values
+   Memcheck:Cond
+   fun:inflateReset2
+   fun:inflateInit2_
+}
\ No newline at end of file
diff --git a/test/valgrind/valgrind.sh b/test/valgrind/valgrind.sh
new file mode 100755 (executable)
index 0000000..3bc90f5
--- /dev/null
@@ -0,0 +1,22 @@
+#!/bin/sh
+
+base=$(basename "$0")
+
+TRACK_ORIGINS=
+
+VALGRIND_VERSION=$(valgrind --version)
+VALGRIND_MAJOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*\([0-9]*\)')
+VALGRIND_MINOR=$(expr "$VALGRIND_VERSION" : '[^0-9]*[0-9]*\.\([0-9]*\)')
+test 3 -gt "$VALGRIND_MAJOR" ||
+test 3 -eq "$VALGRIND_MAJOR" -a 4 -gt "$VALGRIND_MINOR" ||
+TRACK_ORIGINS=--track-origins=yes
+
+exec valgrind -q --error-exitcode=126 \
+       --leak-check=no \
+       --suppressions="$GIT_VALGRIND/suppressions" \
+       --gen-suppressions=all \
+       $TRACK_ORIGINS \
+       --log-fd=4 \
+       --input-fd=4 \
+       $GIT_VALGRIND_OPTIONS \
+       "$GIT_VALGRIND"/../../"$base" "$@"
diff --git a/version b/version
index 9e11b32fcaa96816319e5d0dcff9fb2873f04061..bd73f47072b1fe4b9914ec14a7f6d47fcc8f816a 100644 (file)
--- a/version
+++ b/version
@@ -1 +1 @@
-0.3.1
+0.4
index 8cd3b1a2fe87267697773a1ba81502a12d15907f..12ad2bb4f1bb3a1229f5537f4df867d5cdb00d56 100644 (file)
@@ -7,13 +7,8 @@ Dependencies:
     notmuch:
         Naturally, it expects you have notmuch installed and configured.
 
-    mail:
-        To send mail, notmuch.vim uses the UNIX mail command.
-
-    git-diff:
-        The vim interface makes use of the git-diff.vim syntax file
-        which is available from
-            http://github.com/motemen/git-vim/blob/master/syntax/git-diff.vim
+    mailx:
+        To send mail, notmuch.vim uses the UNIX mailx command.
 
 
 To install:
index a9754f2b4b2e94de332931806260323ff674bfaa..8d5d1c34e377aca92680483ac55f091eb63a93b0 100644 (file)
@@ -118,6 +118,7 @@ let g:notmuch_search_maps = {
         \ '<Enter>':    ':call <SID>NM_search_show_thread(1)<CR>',
         \ '<C-]>':      ':call <SID>NM_search_expand(''<cword>'')<CR>',
         \ 'a':          ':call <SID>NM_search_archive_thread()<CR>',
+        \ 'A':          ':call <SID>NM_search_mark_read_then_archive_thread()<CR>',
         \ 'f':          ':call <SID>NM_search_filter()<CR>',
         \ 'm':          ':call <SID>NM_new_mail()<CR>',
         \ 'o':          ':call <SID>NM_search_toggle_order()<CR>',
@@ -243,26 +244,23 @@ function! s:NM_cmd_search(words)
         let b:nm_raw_lines = lines
         let b:nm_search_words = a:words
 
-        call <SID>NM_cmd_search_mksyntax()
         call <SID>NM_set_map('n', g:notmuch_search_maps)
         setlocal cursorline
         setlocal nowrap
 endfunction
 function! s:NM_cmd_search_fmtline(line)
-        let m = matchlist(a:line, '^\(thread:\S\+\)\s\([^]]\+\]\) \([^;]\+\); \(.*\) (\([^(]*\))$')
+        let m = matchlist(a:line, '^\(thread:\S\+\)\s\(.\{12\}\) \[\(\d\+\)/\d\+\] \([^;]\+\); \%(\[[^\[]\+\] \)*\(.*\) (\([^(]*\))$')
         if !len(m)
                 return 'ERROR PARSING: ' . a:line
         endif
         let max = g:notmuch_search_from_column_width
-        let from = m[3]
-        if strlen(from) >= max
-                let from = substitute(m[3][0:max-4], '[^A-Za-z1-9_]*$', '', '') . '...'
-        endif
-        return printf('%-20s %-20s | %s (%s)', m[2], from, m[4], m[5])
-endfunction
-function! s:NM_cmd_search_mksyntax()
-        syntax clear nmSearchFrom
-        exec printf('syntax match nmSearchFrom /\(\] \)\@<=.\{%d\}/ oneline contained', g:notmuch_search_from_column_width)
+        let flist = []
+        for at in split(m[4], ", ")
+                let p = min([stridx(at, "."), stridx(at, "@")])
+                call insert(flist, tolower(at[0:p - 1]))
+        endfor
+        let from = join(flist, ", ")
+        return printf("%-12s %3s %-20.20s | %s (%s)", m[2], m[3], from, m[5], m[6])
 endfunction
 
 " --- --- search screen action functions {{{2
@@ -309,11 +307,15 @@ function! s:NM_search_edit()
 endfunction
 
 function! s:NM_search_archive_thread()
-        call <SID>NM_add_remove_tags_on_screen('', '-', ['inbox'])
         call <SID>NM_add_remove_tags([], '-', ['inbox'])
         norm j
 endfunction
 
+function! s:NM_search_mark_read_then_archive_thread()
+        call <SID>NM_add_remove_tags([], '-', ['unread', 'inbox'])
+        norm j
+endfunction
+
 function! s:NM_search_filter()
         call <SID>NM_search_filter_helper('Filter: ', '', '')
 endfunction
@@ -402,7 +404,6 @@ function! s:NM_search_add_remove_tags(prompt, prefix, intags)
                 let tags = a:intags
         endif
         call <SID>NM_add_remove_tags([], a:prefix, tags)
-        call <SID>NM_add_remove_tags_on_screen('', a:prefix, tags)
 endfunction
 
 " --- implement show screen {{{1
@@ -496,7 +497,8 @@ function! s:NM_show_archive_thread()
 endfunction
 
 function! s:NM_show_mark_read_then_archive_thread()
-        echo 'not implemented'
+        call <SID>NM_add_remove_tags(b:nm_search_words, '-', ['unread', 'inbox'])
+        call <SID>NM_show_next_thread()
 endfunction
 
 function! s:NM_show_mark_read_then_next_open_message()
@@ -571,7 +573,6 @@ function! s:NM_show_advance_marking_read_and_archiving()
 
         " if entire message fits on the screen, read/archive it, move to the next one
         if msg_top['id'] != msg_bot['id'] || msg_top['end'] <= vis_bot
-                call <SID>NM_add_remove_tags_on_screen(msg_top['start'], '-', advance_tags)
                 exec printf('norm %dG', vis_top)
                 call <SID>NM_show_next(0, 1)
                 if has_key(msg_top,'match') && msg_top['match'] != '0'
@@ -940,71 +941,20 @@ function! s:NM_compose_send()
         let fname = expand('%')
         let lnum = 1
         let line = getline(lnum)
-        let hdrs = {}
         let lst_hdr = ''
         while match(line, '^$') == -1
-                if match(line, '^Notmuch-Help:') != -1
-                        " skip it
-                elseif strlen(lst_hdr) && match(line, '^\s') != -1
-                        let hdrs[lst_hdr][-1] = hdrs[lst_hdr][-1] . substitute(line, '^\s*', ' ', '')
-                else
-                        let m = matchlist(line, '^\(\w[^:]*\):\s*\(.*\)\s*$')
-                        if !len(m)
-                                cursor(lnum, 0)
-                                throw printf('Eeek! invalid header on line %d', lnum)
-                        endif
-                        let key = substitute(m[1], '\<\w', '\U&', 'g')
-                        if strlen(m[2])
-                                if !has_key(hdrs, key)
-                                        let hdrs[key] = []
-                                endif
-                                call add(hdrs[key], m[2])
-                        endif
-                        let lst_hdr = key
+                if match(line, '^Notmuch-Help:') == -1
+                        let hdr_starts = lnum - 1
+                        break
                 endif
                 let lnum = lnum + 1
                 let line = getline(lnum)
         endwhile
-        let body_starts = lnum
-
-        "[-a header] [-b bcc-addr] [-c cc-addr] [-s subject] to-addr
-        let cmd = ['mail']
-        let tos = []
-        for [key, vals] in items(hdrs)
-                if key == 'To'
-                        call extend(tos, vals)
-                elseif key == 'Bcc'
-                        for adr in vals
-                                call add(cmd, '-b')
-                                call add(cmd, adr)
-                        endfor
-                elseif key == 'Cc'
-                        for adr in vals
-                                call add(cmd, '-c')
-                                call add(cmd, adr)
-                        endfor
-                elseif key == 'Subject'
-                        for txt in vals
-                                call add(cmd, '-s')
-                                call add(cmd, txt)
-                        endfor
-                else
-                        for val in vals
-                                call add(cmd, '-a')
-                                call add(cmd, key . ': ' . val)
-                        endfor
-                endif
-        endfor
-        call extend(cmd, tos)
-
-        " TODO: make sure we have at least one target
-        " TODO: ask about empty jubject, etc
 
-        exec printf('0,%dd', body_starts)
+        exec printf(':0,%dd', hdr_starts)
         write
 
-        call map(cmd, 's:NM_shell_escape(v:val)')
-        let cmdtxt = join(cmd) . '< ' . fname
+        let cmdtxt = 'mailx -t < ' . fname
         let out = system(cmdtxt)
         let err = v:shell_error
         if err
@@ -1236,13 +1186,16 @@ function! s:NM_run(args)
         call map(words, 's:NM_shell_escape(v:val)')
         let cmd = g:notmuch_cmd . ' ' . join(words) . '< /dev/null'
 
-        let start = reltime()
-        let out = system(cmd)
-        let err = v:shell_error
-        let delta = reltime(start)
-
         if exists('g:notmuch_debug') && g:notmuch_debug
+                let start = reltime()
+                let out = system(cmd)
+                let err = v:shell_error
+                let delta = reltime(start)
+
                 echo printf('[%s] {%s} %s', reltimestr(delta), string(err), string(cmd))
+        else
+                let out = system(cmd)
+                let err = v:shell_error
         endif
 
         if err
@@ -1327,20 +1280,6 @@ function! s:NM_add_remove_tags(filter, prefix, tags)
         call <SID>NM_run(args)
 endfunction
 
-function! s:NM_add_remove_tags_on_screen(online, prefix, tags)
-        setlocal modifiable
-        if a:prefix == '-'
-                for tagname in a:tags
-                        exec printf('silent! %ss/(\([^)]*\)\<%s\>\([^)]*\))$/(\1\2)/', string(a:online), tagname)
-                endfor
-        else
-                for tagname in a:tags
-                        exec printf('silent! %ss/(\([^)]*\))$/(\1 %s)/', string(a:online), tagname)
-                endfor
-        endif
-        setlocal nomodifiable
-endfunction
-
 " --- process and set the defaults {{{1
 
 function! NM_set_defaults(force)
diff --git a/vim/syntax/notmuch-git-diff.vim b/vim/syntax/notmuch-git-diff.vim
new file mode 100644 (file)
index 0000000..6f15fdc
--- /dev/null
@@ -0,0 +1,26 @@
+syn match diffRemoved  "^-.*"
+syn match diffAdded    "^+.*"
+
+syn match diffSeparator        "^---$"
+syn match diffSubname  " @@..*"ms=s+3 contained
+syn match diffLine     "^@.*" contains=diffSubname
+
+syn match diffFile     "^diff .*"
+syn match diffNewFile  "^+++ .*"
+syn match diffOldFile  "^--- .*"
+
+hi def link diffOldFile                diffFile
+hi def link diffNewFile                diffFile
+
+hi def link diffFile           Type
+hi def link diffRemoved                Special
+hi def link diffAdded          Identifier
+hi def link diffLine           Statement
+hi def link diffSubname                PreProc
+
+syntax match gitDiffStatLine /^ .\{-}\zs[+-]\+$/ contains=gitDiffStatAdd,gitDiffStatDelete
+syntax match gitDiffStatAdd    /+/ contained
+syntax match gitDiffStatDelete /-/ contained
+
+hi def link gitDiffStatAdd diffAdded
+hi def link gitDiffStatDelete diffRemoved
index 71839fd9aef9a9b33a0fd6db9e1b4a40732e603b..f458d778a2ccc464c8cb7f6e4814a5f20c55d865 100644 (file)
@@ -1,24 +1,12 @@
-" notmuch search mode syntax file
-
-" TODO: I cannot figure out why nmSearchTags is not matching anything :(
-
-syntax region nmSearchDate      start='^' end='\%13v'      oneline
-syntax region nmSearchCountAndFrom start='\%14v\[' end='|' oneline contains=nmSearchCount,nmSearchFrom
-syntax region nmSearchCount     start='\[' end='\]'        oneline contained contains=nmSearchCountZero,nmSearchCountSome,nmSearchCountAll
-syntax region nmSearchFrom      start='\]\@<=' end='|'     oneline contained
-syntax match  nmSearchCountZero '0/\(\d\+\)'               contained
-syntax match  nmSearchCountSome '\([1-9]\d*\)/\(\d\+\)'    contained
-syntax match  nmSearchCountAll  '\(\d\+\)/\1'              contained
-syntax match  nmSearchSquareBracketText '\(\[\w\+\]\)'
-syntax match  nmSearchTags      /([^)]\+)$/
-
-highlight link nmSearchDate      Statement
-"highlight link nmSearchCount     Comment
-highlight link nmSearchCountZero Function
-highlight link nmSearchCountSome Special
-highlight link nmSearchCountAll  Type
-highlight link nmSearchFrom      Include
-highlight link nmSearchSquareBracketText Special
-highlight link nmSearchTags      String
-
-highlight CursorLine term=reverse cterm=reverse gui=reverse
+syntax region nmSearch         start=/^/ end=/$/               oneline contains=nmSearchDate
+syntax match nmSearchDate      /^.\{-13}/                      contained nextgroup=nmSearchNum
+syntax match nmSearchNum       /.\{-4}/                        contained nextgroup=nmSearchFrom
+syntax match nmSearchFrom      /.\{-21}/                       contained nextgroup=nmSearchSubject
+syntax match nmSearchSubject   /.\{0,}\(([^()]\+)$\)\@=/       contained nextgroup=nmSearchTags
+syntax match nmSearchTags      /.\+$/                          contained
+
+highlight link nmSearchDate    Statement
+highlight link nmSearchNum     Type
+highlight link nmSearchFrom    Include
+highlight link nmSearchSubject Normal
+highlight link nmSearchTags    String
index 20bcc3996d42c908c09e03c909105b241599cb1f..c3a98b77f62d1a58fd5a6cbcbab991671c15da01 100644 (file)
@@ -12,8 +12,7 @@ syntax match   nmShowMsgHeadVal /^\([^:]\+: \)\@<=.*/ contained
 syntax cluster nmShowMsgBody contains=@nmShowMsgBodyMail,@nmShowMsgBodyGit
 syntax include @nmShowMsgBodyMail syntax/mail.vim
 
-" git-diff.vim marks up diffs in emails, see README for details
-silent! syntax include @nmShowMsgBodyGit syntax/git-diff.vim
+silent! syntax include @nmShowMsgBodyGit syntax/notmuch-git-diff.vim
 
 highlight nmShowMsgDescWho term=reverse cterm=reverse gui=reverse
 highlight link nmShowMsgDescDate Type
diff --git a/xutil.c b/xutil.c
new file mode 100644 (file)
index 0000000..5f98f3f
--- /dev/null
+++ b/xutil.c
@@ -0,0 +1,138 @@
+/* xutil.c - Various wrapper functions to abort on error.
+ *
+ * Copyright © 2009 Carl Worth
+ *
+ * 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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see http://www.gnu.org/licenses/ .
+ *
+ * Author: Carl Worth <cworth@cworth.org>
+ */
+
+#include "notmuch-private.h"
+
+#include <stdio.h>
+
+void *
+xcalloc (size_t nmemb, size_t size)
+{
+    void *ret;
+
+    ret = calloc (nmemb, size);
+    if (ret == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+
+    return ret;
+}
+
+void *
+xmalloc (size_t size)
+{
+    void *ret;
+
+    ret = malloc (size);
+    if (ret == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+
+    return ret;
+}
+
+void *
+xrealloc (void *ptr, size_t size)
+{
+    void *ret;
+
+    ret = realloc (ptr, size);
+    if (ret == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+
+    return ret;
+}
+
+char *
+xstrdup (const char *s)
+{
+    char *ret;
+
+    ret = strdup (s);
+    if (ret == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+
+    return ret;
+}
+
+char *
+xstrndup (const char *s, size_t n)
+{
+    char *ret;
+
+    if (strlen (s) <= n)
+       n = strlen (s);
+
+    ret = malloc (n + 1);
+    if (ret == NULL) {
+       fprintf (stderr, "Out of memory.\n");
+       exit (1);
+    }
+    memcpy (ret, s, n);
+    ret[n] = '\0';
+
+    return ret;
+}
+
+void
+xregcomp (regex_t *preg, const char *regex, int cflags)
+{
+    int rerr;
+
+    rerr = regcomp (preg, regex, cflags);
+    if (rerr) {
+       size_t error_size = regerror (rerr, preg, NULL, 0);
+       char *error = xmalloc (error_size);
+
+       regerror (rerr, preg, error, error_size);
+       fprintf (stderr, "Internal error: compiling regex %s: %s\n",
+                regex, error);
+       exit (1);
+    }
+}
+
+int
+xregexec (const regex_t *preg, const char *string,
+         size_t nmatch, regmatch_t pmatch[], int eflags)
+{
+    unsigned int i;
+    int rerr;
+
+    rerr = regexec (preg, string, nmatch, pmatch, eflags);
+    if (rerr)
+       return rerr;
+
+    for (i = 0; i < nmatch; i++) {
+       if (pmatch[i].rm_so == -1) {
+           fprintf (stderr, "Internal error: matching regex against %s:"
+                    "Sub-match %d not found\n",
+                    string, i);
+           exit (1);
+       }
+    }
+
+    return 0;
+}