uploaded to Debian unstable

-----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQGcBAABCAAGBQJT5kVjAAoJEPIClx2kp54sdH4L+gL7hQAh8Sq/IkEydzxa6esG
 d6emMEyoZq9gfvj2wuWWF+lJ4ik6XrL0wc9KXtzyeFKAVMwql+wdx198+uhjjG1t
 SJ9a962EiqXCPfhskrmQhzWAw0e5gQxz8pvIPR0kUZ+4sMZjjhan3l5f2Hux1zti
 YnkJ13k8KTmMUWb80GHMGl2gq6D8k44Bi2Cc7QYf2czWn5Jx66AaQmj5zTl9+J+m
 RRz7dpumUidLxvxcQrKDeFEfq5AYOZZaqdwcW+sUeqP+n8XNqXZ5HkVgspaMg+Te
 EtioFwbXj0yCy5GktQz33HdQTJBU0EqpS6bhmXNkfkI7IS+jplMXFsAFNmB1vf6K
 UcMd8T5MBMWONk98MH7d/VRfvRp0uC4msIQR3WOxELGKVMwuSQPyjbiMrajzEATB
 UX/Z4cgKqroJpX45FIx7ga/2nsvJxwQITsV8hpIZwycoch34cCR14yBF1KPgds4V
 hp67wy6GGW/5b0b29wjXsxb6pU7kIC4wvgVCTdQs1Q==
 =r7jR
 -----END PGP SIGNATURE-----

Merge tag 'debian/0.18.1-2' into wheezy-backports

uploaded to Debian unstable

Conflicts:
	debian/changelog
This commit is contained in:
David Bremner 2014-08-25 17:56:57 -07:00
commit 28fda31544
223 changed files with 7028 additions and 4133 deletions

1
.gitignore vendored
View file

@ -1,5 +1,6 @@
.first-build-message
Makefile.config
version.stamp
TAGS
tags
*cscope*

34
INSTALL
View file

@ -20,8 +20,8 @@ configure stage.
Dependencies
------------
Notmuch depends on three libraries: Xapian, GMime 2.4 or 2.6, and
Talloc which are each described below:
Notmuch depends on four libraries: Xapian, GMime 2.4 or 2.6,
Talloc, and zlib which are each described below:
Xapian
------
@ -60,16 +60,42 @@ Talloc which are each described below:
Talloc is available from http://talloc.samba.org/
zlib
----
zlib is an extremely popular compression library. It is used
by Xapian, so if you installed that you will already have
zlib. You may need to install the zlib headers separately.
Notmuch needs the transparent write feature of zlib introduced
in version 1.2.5.2 (Dec. 2011).
zlib is available from http://zlib.net
Building Documentation
----------------------
By default the documentation for notmuch is built using sphinx.
Sphinx is available from www.sphinx-doc.org.
If you prefer, you can build the man pages using rst2man, from the
python docutils package. See doc/INSTALL for details.
Installing Dependencies from Packages
-------------------------------------
On a modern, package-based operating system you can install all of the
dependencies with a simple simple command line. For example:
For Debian and similar:
sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev
sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev zlib1g-dev python-sphinx
For Fedora and similar:
sudo yum install xapian-core-devel gmime-devel libtalloc-devel
sudo yum install xapian-core-devel gmime-devel libtalloc-devel zlib-devel python-sphinx
On other systems, a similar command can be used, but the details of
the package names may be different.

View file

@ -2,15 +2,6 @@
# given explicitly on the command line) so mention it first.
all:
# List all subdirectories here. Each contains its own Makefile.local.
# Use of '=', without '+=', seems to be required for out-of-tree
# builds to work.
subdirs = compat completion emacs lib man parse-time-string performance-test util test
# We make all targets depend on the Makefiles themselves.
global_deps = Makefile Makefile.config Makefile.local \
$(subdirs:%=%/Makefile) $(subdirs:%=%/Makefile.local)
# Sub-directory Makefile.local fragments can append to these variables
# to have directory-specific cflags as necessary.
@ -26,6 +17,11 @@ extra_cxxflags :=
srcdir ?= .
include Makefile.config
# We make all targets depend on the Makefiles themselves.
global_deps = Makefile Makefile.config Makefile.local \
$(subdirs:%=%/Makefile) $(subdirs:%=%/Makefile.local)
Makefile.config: $(srcdir)/configure
ifeq ($(configure_options),)
@echo ""

View file

@ -22,6 +22,11 @@ VERSION:=$(shell cat ${srcdir}/version)
ifeq ($(filter release release-message pre-release update-versions,$(MAKECMDGOALS)),)
ifeq ($(IS_GIT),yes)
VERSION:=$(shell git describe --match '[0-9.]*'|sed -e s/_/~/ -e s/-/+/ -e s/-/~/)
# Write the file 'version.stamp' in case its contents differ from $(VERSION)
FILE_VERSION:=$(shell test -f version.stamp && read vs < version.stamp || vs=; echo $$vs)
ifneq ($(FILE_VERSION),$(VERSION))
$(shell echo "$(VERSION)" > version.stamp)
endif
endif
endif
@ -41,19 +46,20 @@ PV_FILE=bindings/python/notmuch/version.py
# Smash together user's values with our extra values
FINAL_CFLAGS = -DNOTMUCH_VERSION=$(VERSION) $(CPPFLAGS) $(CFLAGS) $(WARN_CFLAGS) $(extra_cflags) $(CONFIGURE_CFLAGS)
FINAL_CXXFLAGS = $(CPPFLAGS) $(CXXFLAGS) $(WARN_CXXFLAGS) $(extra_cflags) $(extra_cxxflags) $(CONFIGURE_CXXFLAGS)
FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS)
FINAL_NOTMUCH_LDFLAGS = $(LDFLAGS) -Lutil -lutil -Llib -lnotmuch
ifeq ($(LIBDIR_IN_LDCONFIG),0)
FINAL_NOTMUCH_LDFLAGS += $(RPATH_LDFLAGS)
endif
FINAL_NOTMUCH_LDFLAGS += $(AS_NEEDED_LDFLAGS) $(GMIME_LDFLAGS) $(TALLOC_LDFLAGS) $(ZLIB_LDFLAGS)
FINAL_NOTMUCH_LINKER = CC
ifneq ($(LINKER_RESOLVES_LIBRARY_DEPENDENCIES),1)
FINAL_NOTMUCH_LDFLAGS += $(CONFIGURE_LDFLAGS)
FINAL_NOTMUCH_LINKER = CXX
endif
ifeq ($(LIBDIR_IN_LDCONFIG),0)
FINAL_NOTMUCH_LDFLAGS += $(RPATH_LDFLAGS)
endif
FINAL_LIBNOTMUCH_LDFLAGS = $(LDFLAGS) $(AS_NEEDED_LDFLAGS) $(CONFIGURE_LDFLAGS)
.PHONY: all
all: notmuch notmuch-shared
all: notmuch notmuch-shared build-man
ifeq ($(MAKECMDGOALS),)
ifeq ($(shell cat .first-build-message 2>/dev/null),)
@NOTMUCH_FIRST_BUILD=1 $(MAKE) --no-print-directory all
@ -69,9 +75,14 @@ ifeq ($(shell cat .first-build-message 2>/dev/null),)
endif
endif
# Depend (also) on the file 'version'. In case of ifeq ($(IS_GIT),yes)
# this file may already have been updated.
version.stamp: $(srcdir)/version
echo $(VERSION) > $@
$(TAR_FILE):
if git tag -v $(VERSION) >/dev/null 2>&1; then \
ref=$(VERSION); \
if git tag -v $(UPSTREAM_TAG) >/dev/null 2>&1; then \
ref=$(UPSTREAM_TAG); \
else \
ref="HEAD" ; \
echo "Warning: No signed tag for $(VERSION)"; \
@ -95,7 +106,7 @@ dist: $(TAR_FILE)
.PHONY: update-versions
update-versions: update-man-versions
update-versions:
sed -i "s/^__VERSION__[[:blank:]]*=.*$$/__VERSION__ = \'${VERSION}\'/" $(PV_FILE)
# We invoke make recursively only to force ordering of our phony
@ -236,11 +247,11 @@ endif
quiet ?= $($(shell echo $1 | sed -e s'/ .*//'))
%.o: %.cc $(global_deps)
@mkdir -p .deps/$(@D)
@mkdir -p $(patsubst %/.,%,.deps/$(@D))
$(call quiet,CXX $(CPPFLAGS) $(CXXFLAGS)) -c $(FINAL_CXXFLAGS) $< -o $@ -MD -MP -MF .deps/$*.d
%.o: %.c $(global_deps)
@mkdir -p .deps/$(@D)
@mkdir -p $(patsubst %/.,%,.deps/$(@D))
$(call quiet,CC $(CPPFLAGS) $(CFLAGS)) -c $(FINAL_CFLAGS) $< -o $@ -MD -MP -MF .deps/$*.d
.PHONY : clean
@ -280,6 +291,8 @@ notmuch_client_srcs = \
notmuch_client_modules = $(notmuch_client_srcs:.c=.o)
notmuch.o: version.stamp
notmuch: $(notmuch_client_modules) lib/libnotmuch.a util/libutil.a parse-time-string/libparse-time-string.a
$(call quiet,CXX $(CFLAGS)) $^ $(FINAL_LIBNOTMUCH_LDFLAGS) -o $@
@ -318,10 +331,12 @@ install-desktop:
desktop-file-install --mode 0644 --dir "$(DESTDIR)$(desktop_dir)" notmuch.desktop
SRCS := $(SRCS) $(notmuch_client_srcs)
CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) notmuch.elc
CLEAN := $(CLEAN) notmuch notmuch-shared $(notmuch_client_modules) version.stamp
DISTCLEAN := $(DISTCLEAN) .first-build-message Makefile.config
DEPS := $(SRCS:%.c=.deps/%.d)
DEPS := $(DEPS:%.cc=.deps/%.d)
-include $(DEPS)
.SUFFIXES: # Delete the default suffixes. Old-Fashioned Suffix Rules not used.

293
NEWS
View file

@ -1,3 +1,296 @@
Notmuch 0.18.1 (2014-06-25)
===========================
This is a bug fix and portability release.
Build System
------------
Add a workaround for systems without zlib.pc
Make emacs install robust against the non-existence of emacs
Put notmuch lib directory first in RPATH
Fix handling of html_static_path in sphinx
Both the python bindings and the main docs had spurious settings of
this variable.
Test Suite
----------
Use --quick when starting emacs
This avoids a hang in the T160-json tests.
Allow pending break points in atomicity script
This allows the atomicity tests to run on several more architectures/OSes.
Command-Line Interface
----------------------
To improve portability use fsync instead of fdatasync in
`notmuch-dump`. There should be no functional difference.
Library changes
---------------
Resurrect support for single-message mbox files
The removal introduced a bug with previously indexed single-message
mboxes. This support remains deprecated.
Fix for phrase indexing
There were several bugs where words intermingled from different
headers and MIME parts could match a single phrase query. This fix
will affect only newly indexed messages.
Emacs Interface
---------------
Make sure tagging on an empty query is harmless
Previously tagging an empty query could lead to tags being
unintentionally removed.
Notmuch 0.18 (2014-05-06)
=========================
Overview
--------
This new release includes some enhancements to searching for messages
by filesystem location (`folder:` and `path:` prefixes under *General*
below). Saved searches in *Emacs* have also been enhanced to allow
distinct search orders for each one. Another enhancement to the
*Emacs* interface is that replies to encrypted messages are now
encrypted, reducing the risk of unintentional information disclosure.
The default dump output format has changed to the more robust
`batch-tag` format. The previously deprecated parsing of single
message mboxes has been removed. For detailed release notes, see
below.
General
-------
The `folder:` search prefix now requires an exact match
The `folder:` prefix has been changed to search for email messages
by the exact, case sensitive maildir or MH folder name. Wildcard
matching (`folder:foo*`) is no longer supported. The new behaviour
allows for more accurate mail folder based searches, makes it
possible to search for messages in the top-level folder, and should
lead to less surprising results than the old behaviour. Users are
advised to see the `notmuch-search-terms` manual page for details,
and review how the change affects their existing `folder:` searches.
There is a new `path:` search prefix.
The new `path:` search prefix complements the `folder:` prefix. The
`path:` prefix searches for email messages that are in particular
directories within the mail store, optionally recursively using a
special syntax. See the `notmuch-search-terms` manual page for
details.
Notmuch database upgrade due to `folder:` and `path:` changes
The above mentioned changes to the `folder:` prefix and the addition
of `path:` prefix require a Notmuch database upgrade. This will be
done automatically, without prompting on the next time `notmuch new`
is run after the upgrade. The upgrade is not reversible, and the
upgraded database will not be readable by older versions of
Notmuch. As a safeguard, a database dump will be created in the
`.notmuch` directory before upgrading.
Library changes
---------------
Notmuch database upgrade
The libnotmuch consumers are reminded to handle database upgrades
properly, either by relying on running `notmuch new`, or checking
`notmuch_database_needs_upgrade()` and calling
`notmuch_database_upgrade()` as necessary. This has always been the
case, but in practise there have been no database upgrades in any
released version of Notmuch before now.
Support for indexing mbox files has been dropped
There has never been proper support for mbox files containing
multiple messages, and the support for single-message mbox files has
been deprecated since Notmuch 0.15. The support has now been
dropped, and all mbox files will be rejected during indexing.
Message header parsing changes
Notmuch previously had an internal parser for message headers. The
parser has now been dropped in favour of letting GMime parse both
the headers and the message MIME structure at the same pass. This is
mostly an internal change, but the GMime parser is stricter in its
interpretation of the headers. This may result in messages with
slightly malformed message headers being now rejected.
Command-Line Interface
----------------------
`notmuch dump` now defaults to `batch-tag` format
The old format is still available with `--format=sup`.
`notmuch new` has a --quiet option
This option suppresses the progress and summary reports.
`notmuch insert` respects maildir.synchronize_flags config option
Do not synchronize tags to maildir flags in `notmuch insert` if the
user does not want it.
The commands set consistent exit status codes on failures
The cli commands now consistently set exit status of 1 on failures,
except where explicitly otherwise noted. The notable expections are
the status codes for format version mismatches for commands that
support formatted output.
Bug fix for checking configured new.tags for invalid tags
`notmuch new` and `notmuch insert` now check the user configured
new.tags for invalid tags, and refuse to apply them, similar to
`notmuch tag`. Invalid tags are currently the empty string and tags
starting with `-`.
Emacs Interface
---------------
Init file
If the file pointed by new variable `notmuch-init-file` (typically
`~/.emacs.d/notmuch-config.el`) exists, it is loaded at the end of
`notmuch.el`. Users can put their personal notmuch emacs lisp based
configuration/customization items there instead of filling
`~/.emacs` with these.
Changed format for saved searches
The format for `notmuch-saved-searches` has changed, but old style
saved searches are still supported. The new style means that a saved
search can store the desired sort order for the search, and it can
store a separate query to use for generating the count notmuch
shows.
The variable is fully customizable and any configuration done
through customize should *just work*, with the additional options
mentioned above. For manual customization see the documentation for
`notmuch-saved-searches`.
IMPORTANT: a new style notmuch-saved-searches variable will break
previous versions of notmuch-emacs (even search will not work); to
fix remove the customization for notmuch-saved-searches.
If you have a custom saved search sort function (not unsorted or
alphabetical) then the sort function will need to be
modified. Replacing (car saved-search) by (notmuch-saved-search-get
saved-search :name) and (cdr saved-search) by
(notmuch-saved-search-get saved-search :query) should be sufficient.
The keys of `notmuch-tag-formats` are now regexps
Previously, the keys were literal strings. Customized settings of
`notmuch-tag-formats` will continue to work as before unless tags
contain regexp special characters like `.` or `*`.
Changed tags are now shown in the buffer
Previously tag changes made in a buffer were shown immediately. In
some cases (particularly automatic tag changes like marking read)
this made it hard to see what had happened (e.g., whether the
message had been unread).
The changes are now shown explicitly in the buffer: by default
deleted tags are displayed with red strike-through and added tags
are displayed underlined in green (inverse video is used for deleted
tags if the terminal does not support strike-through).
The variables `notmuch-tag-deleted-formats` and
`notmuch-tag-added-formats`, which have the same syntax as
`notmuch-tag-formats`, allow this to be customized.
Setting `notmuch-tag-deleted-formats` to `'((".*" nil))` and
`notmuch-tag-added-formats` to `'((".*" tag))` will give the old
behavior of hiding deleted tags and showing added tags identically
to tags already present.
Version variable
The new, build-time generated variable `notmuch-emacs-version` is used
to distinguish between notmuch cli and notmuch emacs versions.
The function `notmuch-hello-versions` (bound to 'v' in notmuch-hello
window) prints both notmuch cli and notmuch emacs versions in case
these differ from each other.
This is especially useful when using notmuch remotely.
Ido-completing-read initialization in Emacs 23
`ido-completing-read` in Emacs 23 versions 1 through 3 freezes unless
it is initialized. Defadvice-based *Ido* initialization is defined
for these Emacs versions.
Bug fix for saved searches with newlines in them
Split lines confuse `notmuch count --batch`, so we remove embedded
newlines before calling notmuch count.
Bug fixes for sender identities
Previously, Emacs would rewrite some sender identities in unexpected
and undesirable ways. Now it will use identities exactly as
configured in `notmuch-identities`.
Replies to encrypted messages will be encrypted by default
In the interest of maintaining confidentiality of communications,
the Notmuch Emacs interface now automatically adds the mml tag to
encrypt replies to encrypted messages. This should make it less
likely to accidentally reply to encrypted messages in plain text.
Reply pushes mark before signature
We push mark and set point on reply so that the user can easily cut
the quoted text. The mark is now pushed before the signature, if
any, instead of end of buffer so the signature is preserved.
Message piping uses the originating buffer's working directory
`notmuch-show-pipe-message` now uses the originating buffer's
current default directory instead of that of the `*notmuch-pipe*`
buffer's.
nmbug
-----
nmbug adds a `clone` command for setting up the initial repository and
uses `@{upstream}` instead of `FETCH_HEAD` to track upstream changes.
The `@{upstream}` change reduces ambiguity when fetching multiple
branches, but requires existing users update their `NMBGIT`
repository (usually `~/.nmbug`) to distinguish between local and
remote-tracking branches. The easiest way to do this is:
1. If you have any purely local commits (i.e. they aren't in the
nmbug repository on nmbug.tethera.net), push them to a remote
repository. We'll restore them from the backup in step 4.
2. Remove your `NMBGIT` repository (e.g. `mv .nmbug .nmbug.bak`).
3. Use the new `clone` command to create a fresh clone:
nmbug clone http://nmbug.tethera.net/git/nmbug-tags.git
4. If you had local commits in step 1, add a remote for that
repository and fetch them into the new repository.
Notmuch 0.17 (2013-12-30)
=========================

2
README
View file

@ -42,7 +42,7 @@ the libnotmuch library.
Notmuch installs a full-featured email interface for use within
emacs. To use this, first add the following line to your .emacs file:
(require 'notmuch)
(autoload 'notmuch "notmuch" "Notmuch mail" t)
Then, either run "emacs -f notmuch" or execute the command "M-x
notmuch" from within a running emacs.

View file

@ -140,7 +140,7 @@ html_theme = 'default'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = ['../html']
html_static_path = []
# If not '', a 'Last updated on:' timestamp is inserted at every page bottom,
# using the given strftime format.

View file

@ -1,2 +1,2 @@
# this file should be kept in sync with ../../../version
__VERSION__ = '0.17'
__VERSION__ = '0.18.1'

View file

@ -111,7 +111,7 @@ notmuch_rb_message_get_filename (VALUE self)
}
/*
* call-seq: MESSAGE.filanames => FILENAMES
* call-seq: MESSAGE.filenames => FILENAMES
*
* Get all filenames for the email corresponding to MESSAGE.
*/

View file

@ -129,14 +129,20 @@ parse_option (const char *arg,
const notmuch_opt_desc_t *try;
for (try = options; try->opt_type != NOTMUCH_OPT_END; try++) {
if (try->name && strncmp (arg, try->name, strlen (try->name)) == 0) {
char next = arg[strlen (try->name)];
const char *value= arg+strlen(try->name)+1;
if (! try->name)
continue;
/* If we have not reached the end of the argument
(i.e. the next character is not a space or delimiter)
then the argument could still match a longer option
name later in the option table.
if (strncmp (arg, try->name, strlen (try->name)) != 0)
continue;
char next = arg[strlen (try->name)];
const char *value = arg + strlen(try->name) + 1;
/*
* If we have not reached the end of the argument (i.e. the
* next character is not a space or delimiter) then the
* argument could still match a longer option name later in
* the option table.
*/
if (next != '=' && next != ':' && next != '\0')
continue;
@ -147,16 +153,12 @@ parse_option (const char *arg,
switch (try->opt_type) {
case NOTMUCH_OPT_KEYWORD:
return _process_keyword_arg (try, next, value);
break;
case NOTMUCH_OPT_BOOLEAN:
return _process_boolean_arg (try, next, value);
break;
case NOTMUCH_OPT_INT:
return _process_int_arg (try, next, value);
break;
case NOTMUCH_OPT_STRING:
return _process_string_arg (try, next, value);
break;
case NOTMUCH_OPT_POSITION:
case NOTMUCH_OPT_END:
default:
@ -164,7 +166,6 @@ parse_option (const char *arg,
/*UNREACHED*/
}
}
}
fprintf (stderr, "Unrecognized option: --%s\n", arg);
return FALSE;
}

1
compat/.gitignore vendored Normal file
View file

@ -0,0 +1 @@
zlib.pc

View file

@ -5,6 +5,10 @@ extra_cflags += -I$(srcdir)/$(dir)
notmuch_compat_srcs :=
ifneq ($(HAVE_CANONICALIZE_FILE_NAME),1)
notmuch_compat_srcs += $(dir)/canonicalize_file_name.c
endif
ifneq ($(HAVE_GETLINE),1)
notmuch_compat_srcs += $(dir)/getline.c $(dir)/getdelim.c
endif

View file

@ -0,0 +1,18 @@
#include "compat.h"
#include <limits.h>
#undef _GNU_SOURCE
#include <stdlib.h>
char *
canonicalize_file_name (const char * path)
{
#ifdef PATH_MAX
char *resolved_path = malloc (PATH_MAX+1);
if (resolved_path == NULL)
return NULL;
return realpath (path, resolved_path);
#else
#error undefined PATH_MAX _and_ missing canonicalize_file_name not supported
#endif
}

View file

@ -37,6 +37,14 @@ extern "C" {
#define _POSIX_PTHREAD_SEMANTICS 1
#endif
#if !HAVE_CANONICALIZE_FILE_NAME
/* we only call this function from C, and this makes testing easier */
#ifndef __cplusplus
char *
canonicalize_file_name (const char *path);
#endif
#endif
#if !HAVE_GETLINE
#include <stdio.h>
#include <unistd.h>

18
compat/gen_zlib_pc.c Normal file
View file

@ -0,0 +1,18 @@
#include <stdio.h>
#include <zlib.h>
static const char *template =
"prefix=/usr\n"
"exec_prefix=${prefix}\n"
"libdir=${exec_prefix}/lib\n"
"\n"
"Name: zlib\n"
"Description: zlib compression library\n"
"Version: %s\n"
"Libs: -lz\n";
int main(void)
{
printf(template, ZLIB_VERSION);
return 0;
}

View file

@ -0,0 +1,10 @@
#define _GNU_SOURCE
#include <stdlib.h>
int main()
{
char *found;
char *string;
found = canonicalize_file_name (string);
}

10
compat/have_d_type.c Normal file
View file

@ -0,0 +1,10 @@
#include <dirent.h>
int main()
{
struct dirent ent;
(void) ent.d_type;
return 0;
}

View file

@ -49,8 +49,19 @@ _notmuch_search_terms()
from:*)
COMPREPLY=( $(compgen -P "from:" -W "`_notmuch_user_emails`" -- ${cur##from:}) )
;;
path:*)
local path=`notmuch config get database.path`
compopt -o nospace
COMPREPLY=( $(compgen -d "$path/${cur##path:}" | sed "s|^$path/||" ) )
;;
folder:*)
local path=`notmuch config get database.path`
compopt -o nospace
COMPREPLY=( $(compgen -d "$path/${cur##folder:}" | \
sed "s|^$path/||" | grep -v "\(^\|/\)\(cur\|new\|tmp\)$" ) )
;;
*)
local search_terms="from: to: subject: attachment: tag: id: thread: folder: date:"
local search_terms="from: to: subject: attachment: tag: id: thread: folder: path: date:"
compopt -o nospace
COMPREPLY=( $(compgen -W "${search_terms}" -- ${cur}) )
;;
@ -67,7 +78,7 @@ _notmuch_compact()
$split &&
case "${prev}" in
--backup)
_filedir
_filedir -d
return
;;
esac
@ -96,7 +107,7 @@ _notmuch_config()
;;
# these will also complete on config get, but we don't care
database.path)
_filedir
_filedir -d
;;
maildir.synchronize_flags)
COMPREPLY=( $(compgen -W "true false" -- ${cur}) )
@ -208,7 +219,7 @@ _notmuch_new()
case "${cur}" in
-*)
local options="--no-hooks"
local options="--no-hooks --quiet"
COMPREPLY=( $(compgen -W "${options}" -- ${cur}) )
;;
esac

116
configure vendored
View file

@ -19,11 +19,14 @@ readonly DEFAULT_IFS="$IFS"
srcdir=$(dirname "$0")
subdirs="util compat lib parse-time-string completion doc emacs"
subdirs="${subdirs} performance-test test test/test-databases"
# For a non-srcdir configure invocation (such as ../configure), create
# the directory structure and copy Makefiles.
if [ "$srcdir" != "." ]; then
for dir in . $(grep "^subdirs *=" "$srcdir"/Makefile | sed -e "s/subdirs *= *//"); do
for dir in . ${subdirs}; do
mkdir -p "$dir"
cp "$srcdir"/"$dir"/Makefile.local "$dir"
cp "$srcdir"/"$dir"/Makefile "$dir"
@ -337,6 +340,27 @@ else
errors=$((errors + 1))
fi
if ! pkg-config --exists zlib; then
${CC} ${zlib_cflags} -o compat/gen_zlib_pc \
"$srcdir"/compat/gen_zlib_pc.c ${zlib_ldflags} > /dev/null 2>&1 &&
compat/gen_zlib_pc > compat/zlib.pc &&
PKG_CONFIG_PATH="$PKG_CONFIG_PATH":compat &&
export PKG_CONFIG_PATH
rm -f compat/gen_zlib_pc
fi
printf "Checking for zlib (>= 1.2.5.2)... "
have_zlib=0
if pkg-config --atleast-version=1.2.5.2 zlib; then
printf "Yes.\n"
have_zlib=1
zlib_cflags=$(pkg-config --cflags zlib)
zlib_ldflags=$(pkg-config --libs zlib)
else
printf "No.\n"
errors=$((errors + 1))
fi
printf "Checking for talloc development files... "
if pkg-config --exists talloc; then
printf "Yes.\n"
@ -360,6 +384,14 @@ else
have_valgrind=0
fi
printf "Checking for bash-completion (>= 1.90)... "
if pkg-config --atleast-version=1.90 bash-completion; then
printf "Yes.\n"
else
printf "No (will not install bash completion).\n"
WITH_BASH=0
fi
if [ -z "${EMACSLISPDIR}" ]; then
if pkg-config --exists emacs; then
EMACSLISPDIR=$(pkg-config emacs --variable sitepkglispdir)
@ -385,6 +417,25 @@ else
have_emacs=0
fi
printf "Checking if sphinx is available and supports nroff output... "
if hash sphinx-build > /dev/null 2>&1 && python -m sphinx.writers.manpage > /dev/null 2>&1 ; then
printf "Yes.\n"
have_sphinx=1
have_rst2man=0
else
printf "No (falling back to rst2man).\n"
have_sphinx=0
printf "Checking if rst2man is available... "
if rst2man -V > /dev/null 2>&1; then
printf "Yes.\n"
have_rst2man=1
else
printf "No (so will not install man pages).\n"
have_rst2man=0
fi
fi
libdir_in_ldconfig=0
printf "Checking which platform we are on... "
@ -466,6 +517,11 @@ EOF
echo " Xapian library (including development files such as headers)"
echo " http://xapian.org/"
fi
if [ $have_zlib -eq 0 ]; then
echo " zlib library (>= version 1.2.5.2, including development files such as headers)"
echo " http://zlib.net/"
echo
fi
if [ $have_gmime -eq 0 ]; then
echo " Either GMime 2.4 library" $GMIME_24_VERSION_CTR "or GMime 2.6 library" $GMIME_26_VERSION_CTR
echo " (including development files such as headers)"
@ -489,11 +545,11 @@ case a simple command will install everything you need. For example:
On Debian and similar systems:
sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev
sudo apt-get install libxapian-dev libgmime-2.6-dev libtalloc-dev zlib1g-dev
Or on Fedora and similar systems:
sudo yum install xapian-core-devel gmime-devel libtalloc-devel
sudo yum install xapian-core-devel gmime-devel libtalloc-devel zlib-devel
On other systems, similar commands can be used, but the details of the
package names may be different.
@ -526,6 +582,18 @@ EOF
exit 1
fi
printf "Checking for canonicalize_file_name... "
if ${CC} -o compat/have_canonicalize_file_name "$srcdir"/compat/have_canonicalize_file_name.c > /dev/null 2>&1
then
printf "Yes.\n"
have_canonicalize_file_name=1
else
printf "No (will use our own instead).\n"
have_canonicalize_file_name=0
fi
rm -f compat/have_canonicalize_file_name
printf "Checking for getline... "
if ${CC} -o compat/have_getline "$srcdir"/compat/have_getline.c > /dev/null 2>&1
then
@ -570,6 +638,17 @@ else
fi
rm -f compat/have_timegm
printf "Checking for dirent.d_type... "
if ${CC} -o compat/have_d_type "$srcdir"/compat/have_d_type.c > /dev/null 2>&1
then
printf "Yes.\n"
have_d_type="1"
else
printf "No (will use stat instead).\n"
have_d_type="0"
fi
rm -f compat/have_d_type
printf "Checking for standard version of getpwuid_r... "
if ${CC} -o compat/check_getpwuid "$srcdir"/compat/check_getpwuid.c > /dev/null 2>&1
then
@ -660,6 +739,9 @@ cat > Makefile.config <<EOF
# directory (the current directory at the time configure was run).
srcdir = ${srcdir}
# subdirectories to build
subdirs = ${subdirs}
configure_options = $@
# We use vpath directives (rather than the VPATH variable) since the
@ -675,8 +757,9 @@ configure_options = $@
# files, (which is quite ugly).
vpath %.c \$(srcdir)
vpath %.cc \$(srcdir)
vpath %.1 \$(srcdir)
vpath Makefile.% \$(srcdir)
vpath %.py \$(srcdir)
vpath %.rst \$(srcdir)
# The C compiler to use
CC = ${CC}
@ -740,6 +823,12 @@ emacsetcdir=${EMACSETCDIR}
# Whether there's an emacs binary available for byte-compiling
HAVE_EMACS = ${have_emacs}
# Whether there's a sphinx-build binary available for building documentation
HAVE_SPHINX=${have_sphinx}
# Whether there's a rst2man binary available for building documentation
HAVE_RST2MAN=${have_rst2man}
# The directory to which desktop files should be installed
desktop_dir = \$(prefix)/share/applications
@ -749,6 +838,10 @@ bash_completion_dir = ${BASHCOMPLETIONDIR:=\$(sysconfdir)/bash_completion.d}
# The directory to which zsh completions files should be installed
zsh_completion_dir = ${ZSHCOMLETIONDIR:=\$(prefix)/share/zsh/functions/Completion/Unix}
# Whether the canonicalize_file_name function is available (if not, then notmuch will
# build its own version)
HAVE_CANONICALIZE_FILE_NAME = ${have_canonicalize_file_name}
# Whether the getline function is available (if not, then notmuch will
# build its own version)
HAVE_GETLINE = ${have_getline}
@ -761,6 +854,9 @@ HAVE_STRCASESTR = ${have_strcasestr}
# build its own version)
HAVE_STRSEP = ${have_strsep}
# Whether struct dirent has d_type (if not, then notmuch will use stat)
HAVE_D_TYPE = ${have_d_type}
# Whether the Xapian version in use supports compaction
HAVE_XAPIAN_COMPACT = ${have_xapian_compact}
@ -790,6 +886,10 @@ XAPIAN_LDFLAGS = ${xapian_ldflags}
GMIME_CFLAGS = ${gmime_cflags}
GMIME_LDFLAGS = ${gmime_ldflags}
# Flags needed to compile and link against zlib
ZLIB_CFLAGS = ${zlib_cflags}
ZLIB_LDFLAGS = ${zlib_ldflags}
# Flags needed to compile and link against talloc
TALLOC_CFLAGS = ${talloc_cflags}
TALLOC_LDFLAGS = ${talloc_ldflags}
@ -817,24 +917,30 @@ WITH_ZSH = ${WITH_ZSH}
# Combined flags for compiling and linking against all of the above
CONFIGURE_CFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
-DHAVE_CANONICALIZE_FILE_NAME=\$(HAVE_CANONICALIZE_FILE_NAME) \\
\$(ZLIB_CFLAGS) \\
\$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\
\$(VALGRIND_CFLAGS) \\
-DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\
-DHAVE_STRSEP=\$(HAVE_STRSEP) \\
-DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\
-DSTD_GETPWUID=\$(STD_GETPWUID) \\
-DSTD_ASCTIME=\$(STD_ASCTIME) \\
-DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\
-DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)
CONFIGURE_CXXFLAGS = -DHAVE_GETLINE=\$(HAVE_GETLINE) \$(GMIME_CFLAGS) \\
-DHAVE_CANONICALIZE_FILE_NAME=\$(HAVE_CANONICALIZE_FILE_NAME) \\
\$(ZLIB_CFLAGS) \\
\$(TALLOC_CFLAGS) -DHAVE_VALGRIND=\$(HAVE_VALGRIND) \\
\$(VALGRIND_CFLAGS) \$(XAPIAN_CXXFLAGS) \\
-DHAVE_STRCASESTR=\$(HAVE_STRCASESTR) \\
-DHAVE_STRSEP=\$(HAVE_STRSEP) \\
-DHAVE_D_TYPE=\$(HAVE_D_TYPE) \\
-DSTD_GETPWUID=\$(STD_GETPWUID) \\
-DSTD_ASCTIME=\$(STD_ASCTIME) \\
-DHAVE_XAPIAN_COMPACT=\$(HAVE_XAPIAN_COMPACT) \\
-DUTIL_BYTE_ORDER=\$(UTIL_BYTE_ORDER)
CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(XAPIAN_LDFLAGS)
CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS)
EOF

10
debian/NEWS vendored
View file

@ -1,3 +1,13 @@
notmuch (0.18~rc0-1) experimental; urgency=low
* This release of notmuch requires a non-reversable database upgrade
to support the new path: and updated folder: prefixes. Notmuch
will backup your tags for your before doing the upgrade, but it
never hurts to make your own backup with notmuch dump before
next running 'notmuch new'
-- David Bremner <bremner@debian.org> Tue, 22 Apr 2014 09:32:11 +0900
notmuch (0.17-1) unstable; urgency=low
* Previously on big endian architectures like sparc and powerpc the

99
debian/changelog vendored
View file

@ -1,8 +1,100 @@
notmuch (0.17-3~bpo70+1) wheezy-backports; urgency=medium
notmuch (0.18.1-2) unstable; urgency=medium
* Rebuild for wheezy-backports.
* Update build-deps to use emacs24 on buildd (Closes: #756085)
* Disable gdb atomicity test for arm64 as gdb is currently broken on
the (unofficial) buildds
* Re-enable atomicity test on armhf; upstream fix seems to have
worked.
-- David Bremner <bremner@debian.org> Wed, 29 Jan 2014 20:20:56 -0400
-- David Bremner <bremner@debian.org> Sat, 09 Aug 2014 11:48:10 -0300
notmuch (0.18.1-1) unstable; urgency=medium
* New upstream bug fix release
- Re-enable support for single-message mbox files
- Fix for phrase indexing
- Make tagging empty query in Emacs harmless
-- David Bremner <bremner@debian.org> Wed, 25 Jun 2014 07:20:45 -0300
notmuch (0.18.1~rc0-1) experimental; urgency=medium
* New upstream bug fix release (candidate)
* Tighten dependence of python packages on libnotmuch
(Closes: #749881).
* Redo emacsen-install script from sample in emacsen-common
(Closes: #739839).
-- David Bremner <bremner@debian.org> Sat, 14 Jun 2014 07:50:28 -0300
notmuch (0.18-3) unstable; urgency=medium
* Disable atomicity tests on armel.
-- David Bremner <bremner@debian.org> Thu, 08 May 2014 14:26:45 +0900
notmuch (0.18-2) unstable; urgency=medium
* Disable atomicity tests on armhf. These should be re-enabled when
upstream relases a fix for this (in progress).
-- David Bremner <bremner@debian.org> Thu, 08 May 2014 08:28:33 +0900
notmuch (0.18-1) unstable; urgency=medium
* New upstream release. For detailed release notes see
see /usr/share/doc/notmuch/NEWS.gz. Some highlights:
- Changes/enhancements to searching for messages by filesystem
location ('folder:' and 'path:' prefixes).
- Saved searches in Emacs have also been enhanced to allow
distinct search orders for each one.
- Another enhancement to the Emacs interface is that replies to
encrypted messages are now encrypted, reducing the risk of
unintentional information disclosure.
- The default dump output format has changed to the more robust
'batch-tag' format.
- The previously deprecated parsing of single message mboxes has
been removed.
-- David Bremner <bremner@debian.org> Tue, 06 May 2014 16:20:39 +0900
notmuch (0.18~rc1-1) experimental; urgency=low
* Upstream release candidate
- include encoding fix for vim client.
-- David Bremner <bremner@debian.org> Sun, 04 May 2014 07:29:51 +0900
notmuch (0.18~rc0-1) experimental; urgency=low
* Upstream release candidate.
* Bug fix: "insufficient sanitization of arguments to notmuch CLI",
thanks to Antoine Beaupré (Closes: #737496).
* Bug fix: "notmuch(1) manpage: typo: int -> in", thanks to Jakub
Wilk (Closes: #739556).
* Bug fix: "get a quiet option", thanks to Joerg Jaspert (Closes:
#666027).
* Bug fix: "Please remove me from Uploaders", thanks to martin f
krafft (Closes: #719100).
* Bug fix: "M-x notmuch-show-reply on an encrypted message should
insert encryption tags into the mml buffer", thanks to Daniel Kahn
Gillmor (Closes: #704648).
-- David Bremner <bremner@debian.org> Tue, 22 Apr 2014 09:27:29 +0900
notmuch (0.17-5) unstable; urgency=medium
* Bug fix: "unowned directory after purge: /0755/", thanks to
Andreas Beckmann (Closes: #740325).
-- David Bremner <bremner@debian.org> Mon, 03 Mar 2014 07:29:06 -0400
notmuch (0.17-4) unstable; urgency=medium
* Bug fix: "Please update ruby binary extension install path",
thanks to Christian Hofstaedtler (Closes: #739120).
-- David Bremner <bremner@debian.org> Tue, 18 Feb 2014 21:37:44 -0400
notmuch (0.17-3) unstable; urgency=medium
@ -59,6 +151,7 @@ notmuch (0.16-1~bpo70+1) wheezy-backports; urgency=low
* Rebuild for wheezy-backports.
-- David Bremner <bremner@debian.org> Sun, 01 Sep 2013 19:04:32 -0300
notmuch (0.16-1) unstable; urgency=low
* New upstream feature release

20
debian/control vendored
View file

@ -4,8 +4,8 @@ Priority: optional
Maintainer: Carl Worth <cworth@debian.org>
Uploaders:
Jameson Graef Rollins <jrollins@finestructure.net>,
martin f. krafft <madduck@debian.org>,
David Bremner <bremner@debian.org>
Build-Conflicts: ruby1.8
Build-Depends:
debhelper (>= 9),
pkg-config,
@ -15,11 +15,13 @@ Build-Depends:
libz-dev,
python-all (>= 2.6.6-3~),
python3-all (>= 3.1.2-7~),
python-sphinx (>= 1.0),
ruby, ruby-dev (>>1:1.9.3~),
emacs23-nox | emacs23 (>=23~) | emacs23-lucid (>=23~) |
emacs24-nox | emacs24 (>=24~) | emacs24-lucid (>=24~),
gdb [!s390x !ia64],
dtach (>= 0.8)
emacs24-nox | emacs24 (>=24~) | emacs24-lucid (>=24~) |
emacs23-nox | emacs23 (>=23~) | emacs23-lucid (>=23~),
gdb [!s390x !ia64 !armel !arm64],
dtach (>= 0.8),
bash-completion (>=1.9.0~)
Standards-Version: 3.9.4
Homepage: http://notmuchmail.org/
Vcs-Git: git://notmuchmail.org/git/notmuch
@ -67,7 +69,7 @@ Description: thread-based email index, search and tagging (development)
Package: python-notmuch
Architecture: all
Section: python
Depends: ${misc:Depends}, ${python:Depends}, libnotmuch3
Depends: ${misc:Depends}, ${python:Depends}, libnotmuch3 (>= ${source:Version})
Description: python interface to the notmuch mail search and index library
Notmuch is a system for indexing, searching, reading, and tagging
large collections of email messages in maildir or mh format. It uses
@ -80,7 +82,7 @@ Description: python interface to the notmuch mail search and index library
Package: python3-notmuch
Architecture: all
Section: python
Depends: ${misc:Depends}, ${python3:Depends}, libnotmuch3
Depends: ${misc:Depends}, ${python3:Depends}, libnotmuch3 (>= ${source:Version})
Description: Python 3 interface to the notmuch mail search and index library
Notmuch is a system for indexing, searching, reading, and tagging
large collections of email messages in maildir or mh format. It uses
@ -108,10 +110,10 @@ Architecture: all
Section: mail
Breaks: notmuch (<<0.6~254~)
Replaces: notmuch (<<0.6~254~)
Conflicts: emacsen-common (<< 2.0.0)
Depends: ${misc:Depends}, notmuch (>= ${source:Version}),
emacs23 (>= 23~) | emacs23-nox (>=23~) | emacs23-lucid (>=23~) |
emacs24 (>= 24~) | emacs24-nox (>=24~) | emacs24-lucid (>=24~)
emacs24 (>= 24~) | emacs24-nox (>=24~) | emacs24-lucid (>=24~),
emacsen-common (>= 2.0.8)
Description: thread-based email index, search and tagging (emacs interface)
Notmuch is a system for indexing, searching, reading, and tagging
large collections of email messages in maildir or mh format. It uses

View file

@ -1,45 +1,48 @@
#! /bin/sh -e
#!/bin/sh
# /usr/lib/emacsen-common/packages/install/notmuch-emacs
# Written by Jim Van Zandt <jrv@debian.org>, borrowing heavily
# from the install scripts for gettext by Santiago Vila
# <sanvila@ctv.es> and octave by Dirk Eddelbuettel <edd@debian.org>.
set -e
FLAVOR=$1
PACKAGE=notmuch
# We know that the notmuch emacs code doesn't work with emacs before emacs23
if [ ${FLAVOR} = emacs21 ]; then exit 0; fi
if [ ${FLAVOR} = emacs22 ]; then exit 0; fi
if [ ${FLAVOR} = xemacs21 ]; then exit 0; fi
if [ ${FLAVOR} = xemacs22 ]; then exit 0; fi
case "${FLAVOR}" in
emacs)
return 0
;;
xemacs*|emacs2[12])
# patches welcome.
echo install/${PACKAGE}: skipping install for unsupported emacsen flavor ${FLAVOR}
exit 0
;;
*)
echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR}
esac
echo install/${PACKAGE}: Handling install for emacsen flavor ${FLAVOR}
#FLAVORTEST=`echo $FLAVOR | cut -c-6`
#if [ ${FLAVORTEST} = xemacs ] ; then
# SITEFLAG="-no-site-file"
#else
# SITEFLAG="--no-site-file"
#fi
#FLAGS="${SITEFLAG} -q -batch -l path.el -f batch-byte-compile"
FLAGS="--no-site-file -q -batch -l path.el -f batch-byte-compile"
elc_dir=/usr/share/${FLAVOR}/site-lisp/${PACKAGE}
el_dir=/usr/share/emacs/site-lisp/${PACKAGE}
ELDIR=/usr/share/emacs/site-lisp/${PACKAGE}
ELCDIR=/usr/share/${FLAVOR}/site-lisp/${PACKAGE}
byte_compile_options="--quick --directory=${el_dir} -batch -f batch-byte-compile"
install -m 755 -d ${ELCDIR}
cd ${ELDIR}
FILES=`echo *.el`
cd ${ELCDIR}
for file in ${FILES}; do
ln -sf ${ELDIR}/${file} .
done
echo install/${PACKAGE}: byte-compiling for ${FLAVOR}
cat << EOF > path.el
(setq load-path (cons "." load-path) byte-compile-warnings nil)
EOF
${FLAVOR} ${FLAGS} ${FILES}
rm -f *.el
[ -d ${elc_dir} ] || mkdir ${elc_dir}
exit 0
# Create symlinks to the .el files (see section 6E in debian-emacs
# polcy). This makes complation easy, and also allows find-function
# and find-library to work properly.
(cd ${elc_dir} && cp -s ${el_dir}/*.el .)
# Byte compile them
(cd ${elc_dir}
set +e
${FLAVOR} ${byte_compile_options} *.el > Install.log 2>&1
if test $? -ne 0
then
cat Install.log
exit 1
fi
set -e
gzip -9 Install.log)
exit 0;

View file

@ -1,8 +1,34 @@
#!/bin/sh -e
# /usr/lib/emacsen-common/packages/remove/notmuch-emacs
#!/bin/sh
# /usr/lib/emacsen-common/packages/remove/notmuch
set -e
FLAVOR=$1
PACKAGE=notmuch
elc_dir=/usr/share/${FLAVOR}/site-lisp/${PACKAGE}
echo remove/${PACKAGE}: purging byte-compiled files for ${FLAVOR}
rm -rf /usr/share/${FLAVOR}/site-lisp/${PACKAGE}
case "${FLAVOR}" in
emacs)
return 0
;;
xemacs*|emacs2[12])
# patches welcome.
echo install/${PACKAGE}: skipping removal for unsupported emacsen flavor ${FLAVOR}
exit 0
;;
*)
echo remove/${PACKAGE}: Handling removal for emacsen flavor ${FLAVOR}
esac
echo remove/${PACKAGE}: Handling removal of emacsen flavor ${FLAVOR}
echo emacsen-common: purging byte-compiled files for ${FLAVOR}
rm -f ${elc_dir}/*.elc
rm -f ${elc_dir}/*.el
rm -f ${elc_dir}/Install.log*
if test -e "${elc_dir}"
then
rmdir --ignore-fail-on-non-empty "${elc_dir}"
fi
exit 0;

View file

@ -1,4 +1,7 @@
dir="/var/lib/emacsen-common/state/package/installed"
mkdir -p 0755 ${dir}
mkdir -p -m 0755 ${dir}
touch ${dir}/notmuch-emacs
#DEBHELPER#
if [ -d /0755 ]; then
rmdir /0755 || true
fi

View file

@ -1 +1 @@
usr/lib/ruby/vendor_ruby/*/*/notmuch.so
usr/lib/*/*ruby/*/*/notmuch.so

View file

@ -1 +1,3 @@
single-debian-patch
tar-ignore
tar-ignore=performance-test/download/*.tar.xz

131
devel/gen-testdb.sh Executable file
View file

@ -0,0 +1,131 @@
#!/usr/bin/env bash
#
# NAME
# gen-testdb.sh - generate test databases
#
# SYNOPSIS
# gen-testdb.sh -v NOTMUCH-VERSION [-c CORPUS-PATH] [-s TAR-SUFFIX]
#
# DESCRIPTION
# Generate a tarball containing the specified test corpus and
# the corresponding notmuch database, indexed using a specific
# version of notmuch, resulting in a specific version of the
# database.
#
# The specific version of notmuch will be built on the fly.
# Therefore the script must be run within a git repository to be
# able to build the old versions of notmuch.
#
# This script reuses the test infrastructure, and the script
# must be run from within the test directory.
#
# The output tarballs, named database-<TAR-SUFFIX>.tar.gz, are
# placed in the test/test-databases directory.
#
# OPTIONS
# -v NOTMUCH-VERSION
# Notmuch version in terms of a git tag or commit to use
# for generating the database. Required.
#
# -c CORPUS-PATH
# Path to a corpus to use for generating the
# database. Due to CWD changes within the test
# infrastructure, use absolute paths. Defaults to the
# test corpus.
#
# -s TAR-SUFFIX
# Suffix for the tarball basename. Empty by default.
#
# EXAMPLE
#
# Generate a database indexed with notmuch 0.17. Use the default
# test corpus. Name the tarball database-v1.tar.gz to reflect
# the fact that notmuch 0.17 used database version 1.
#
# $ cd test
# $ ../devel/gen-testdb.sh -v 0.17 -s v1
#
# CAVEATS
# Test infrastructure options won't work.
#
# Any existing databases with the same name will be overwritten.
#
# It may not be possible to build old versions of notmuch with
# the set of dependencies that satisfy building the current
# version of notmuch.
#
# AUTHOR
# Jani Nikula <jani@nikula.org>
#
# LICENSE
# Same as notmuch test infrastructure (GPLv2+).
#
test_description="database generation abusing test infrastructure"
# immediate exit on subtest failure; see test_failure_ in test-lib.sh
immediate=t
VERSION=
CORPUS=
SUFFIX=
while getopts v:c:s: opt; do
case "$opt" in
v) VERSION="$OPTARG";;
c) CORPUS="$OPTARG";;
s) SUFFIX="-$OPTARG";;
esac
done
shift `expr $OPTIND - 1`
. ./test-lib.sh
SHORT_CORPUS=$(basename ${CORPUS:-database})
DBNAME=${SHORT_CORPUS}${SUFFIX}
TARBALLNAME=${DBNAME}.tar.xz
CORPUS=${CORPUS:-${TEST_DIRECTORY}/corpus}
test_expect_code 0 "notmuch version specified on the command line" \
"test -n ${VERSION}"
test_expect_code 0 "the specified version ${VERSION} refers to a commit" \
"git show ${VERSION} >/dev/null 2>&1"
BUILD_DIR="notmuch-${VERSION}"
test_expect_code 0 "generate snapshot of notmuch version ${VERSION}" \
"git -C $TEST_DIRECTORY/.. archive --prefix=${BUILD_DIR}/ --format=tar ${VERSION} | tar x"
# force version string
git describe --match '[0-9.]*' ${VERSION} > ${BUILD_DIR}/version
test_expect_code 0 "configure and build notmuch version ${VERSION}" \
"make -C ${BUILD_DIR}"
# use the newly built notmuch
export PATH=./${BUILD_DIR}:$PATH
test_begin_subtest "verify the newly built notmuch version"
test_expect_equal "`notmuch --version`" "notmuch `cat ${BUILD_DIR}/version`"
# replace the existing mails, if any, with the specified corpus
rm -rf ${MAIL_DIR}
cp -a ${CORPUS} ${MAIL_DIR}
test_expect_code 0 "index the corpus" \
"notmuch new"
# wrap the resulting mail store and database in a tarball
cp -a ${MAIL_DIR} ${TMP_DIRECTORY}/${DBNAME}
tar Jcf ${TMP_DIRECTORY}/${TARBALLNAME} -C ${TMP_DIRECTORY} ${DBNAME}
mkdir -p ${TEST_DIRECTORY}/test-databases
cp -a ${TMP_DIRECTORY}/${TARBALLNAME} ${TEST_DIRECTORY}/test-databases
test_expect_code 0 "create the output tarball ${TARBALLNAME}" \
"test -f ${TEST_DIRECTORY}/test-databases/${TARBALLNAME}"
# generate a checksum file
test_expect_code 0 "compute checksum" \
"(cd ${TEST_DIRECTORY}/test-databases/ && sha256sum ${TARBALLNAME} > ${TARBALLNAME}.sha256)"
test_done

View file

@ -26,6 +26,7 @@ my $ESCAPED_RX = qr{$ESCAPE_CHAR([A-Fa-f0-9]{2})};
my %command = (
archive => \&do_archive,
checkout => \&do_checkout,
clone => \&do_clone,
commit => \&do_commit,
fetch => \&do_fetch,
help => \&do_help,
@ -125,6 +126,16 @@ sub do_archive {
system ('git', "--git-dir=$NMBGIT", 'archive', 'HEAD');
}
sub do_clone {
my $repository = shift;
my $tempwork = tempdir ('/tmp/nmbug-clone.XXXXXX', CLEANUP => 1);
system ('git', 'clone', '--no-checkout', '--separate-git-dir', $NMBGIT,
$repository, $tempwork) == 0
or die "'git clone' exited with nonzero value\n";
git ('config', '--unset', 'core.worktree');
git ('config', 'core.bare', 'true');
}
sub is_committed {
my $status = shift;
@ -332,21 +343,24 @@ To discard your changes, run 'nmbug checkout'
sub do_pull {
my $remote = shift || 'origin';
my $branch = shift || 'master';
git ( 'fetch', $remote);
do_merge ();
do_merge ("$remote/$branch");
}
sub do_merge {
my $commit = shift || '@{upstream}';
insist_committed ();
my $tempwork = tempdir ('/tmp/nmbug-merge.XXXXXX', CLEANUP => 1);
git ( { GIT_WORK_TREE => $tempwork }, 'checkout', '-f', 'HEAD');
git ( { GIT_WORK_TREE => $tempwork }, 'merge', 'FETCH_HEAD');
git ( { GIT_WORK_TREE => $tempwork }, 'merge', $commit);
do_checkout ();
}
@ -407,11 +421,10 @@ sub do_status {
sub is_unmerged {
my $commit = shift || '@{upstream}';
return 0 if (! -f $NMBGIT.'/FETCH_HEAD');
my $fetch_head = git ('rev-parse', 'FETCH_HEAD');
my $base = git ( 'merge-base', 'HEAD', 'FETCH_HEAD');
my $fetch_head = git ('rev-parse', $commit);
my $base = git ( 'merge-base', 'HEAD', $commit);
return ($base ne $fetch_head);
@ -473,7 +486,7 @@ sub diff_index {
sub diff_refs {
my $filter = shift;
my $ref1 = shift || 'HEAD';
my $ref2 = shift || 'FETCH_HEAD';
my $ref2 = shift || '@{upstream}';
my $fh= git_pipe ( 'diff', "--diff-filter=$filter", '--name-only',
$ref1, $ref2);
@ -561,10 +574,11 @@ git. Any extra arguments are used (one per line) as a commit message.
push local nmbug git state to remote repo
=item B<pull> [remote]
=item B<pull> [remote] [branch]
pull (merge) remote repo changes to notmuch. B<pull> is equivalent to
B<fetch> followed by B<merge>.
B<fetch> followed by B<merge>. The default remote is C<origin>, and
the default branch is C<master>.
=back
@ -572,6 +586,12 @@ B<fetch> followed by B<merge>.
=over 8
=item B<clone> repository
Create a local nmbug repository from a remote source. This wraps
C<git clone>, adding some options to avoid creating a working tree
while preserving remote-tracking branches and upstreams.
=item B<checkout>
Update the notmuch database from git. This is mainly useful to discard
@ -589,12 +609,12 @@ print help [for subcommand]
=item B<log> [parameters]
A simple wrapper for git log. After running C<nmbug fetch>, you can
inspect the changes with C<nmbug log HEAD..FETCH_HEAD>
inspect the changes with C<nmbug log HEAD..@{upstream}>
=item B<merge>
=item B<merge> [commit]
Merge changes from FETCH_HEAD into HEAD, and load the result into
notmuch.
Merge changes from C<commit> into HEAD, and load the result into
notmuch. The default commit is C<@{upstream}>.
=item B<status>

View file

@ -6,185 +6,330 @@
# - python 2.6 for json
# - argparse; either python 2.7, or install separately
from __future__ import print_function
from __future__ import unicode_literals
import codecs
import collections
import datetime
import rfc822
import urllib
import email.utils
try: # Python 3
from urllib.parse import quote
except ImportError: # Python 2
from urllib import quote
import json
import argparse
import os
import re
import sys
import subprocess
import xml.sax.saxutils
# parse command line arguments
_ENCODING = 'UTF-8'
_PAGES = {}
if not hasattr(collections, 'OrderedDict'): # Python 2.6 or earlier
class _OrderedDict (dict):
"Just enough of a stub to get through Page._get_threads"
def __init__(self, *args, **kwargs):
super(_OrderedDict, self).__init__(*args, **kwargs)
self._keys = [] # record key order
def __setitem__(self, key, value):
super(_OrderedDict, self).__setitem__(key, value)
self._keys.append(key)
def values(self):
for key in self._keys:
yield self[key]
collections.OrderedDict = _OrderedDict
def read_config(path=None, encoding=None):
"Read config from json file"
if not encoding:
encoding = _ENCODING
if path:
fp = open(path)
else:
nmbhome = os.getenv('NMBGIT', os.path.expanduser('~/.nmbug'))
# read only the first line from the pipe
sha1_bytes = subprocess.Popen(
['git', '--git-dir', nmbhome, 'show-ref', '-s', 'config'],
stdout=subprocess.PIPE).stdout.readline()
sha1 = sha1_bytes.decode(encoding).rstrip()
fp_byte_stream = subprocess.Popen(
['git', '--git-dir', nmbhome, 'cat-file', 'blob',
sha1+':status-config.json'],
stdout=subprocess.PIPE).stdout
fp = codecs.getreader(encoding=encoding)(stream=fp_byte_stream)
return json.load(fp)
class Thread (list):
def __init__(self):
self.running_data = {}
class Page (object):
def __init__(self, header=None, footer=None):
self.header = header
self.footer = footer
def write(self, database, views, stream=None):
if not stream:
try: # Python 3
byte_stream = sys.stdout.buffer
except AttributeError: # Python 2
byte_stream = sys.stdout
stream = codecs.getwriter(encoding=_ENCODING)(stream=byte_stream)
self._write_header(views=views, stream=stream)
for view in views:
self._write_view(database=database, view=view, stream=stream)
self._write_footer(views=views, stream=stream)
def _write_header(self, views, stream):
if self.header:
stream.write(self.header)
def _write_footer(self, views, stream):
if self.footer:
stream.write(self.footer)
def _write_view(self, database, view, stream):
if 'query-string' not in view:
query = view['query']
view['query-string'] = ' and '.join(query)
q = notmuch.Query(database, view['query-string'])
q.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
threads = self._get_threads(messages=q.search_messages())
self._write_view_header(view=view, stream=stream)
self._write_threads(threads=threads, stream=stream)
def _get_threads(self, messages):
threads = collections.OrderedDict()
for message in messages:
thread_id = message.get_thread_id()
if thread_id in threads:
thread = threads[thread_id]
else:
thread = Thread()
threads[thread_id] = thread
thread.running_data, display_data = self._message_display_data(
running_data=thread.running_data, message=message)
thread.append(display_data)
return list(threads.values())
def _write_view_header(self, view, stream):
pass
def _write_threads(self, threads, stream):
for thread in threads:
for message_display_data in thread:
stream.write(
('{date:10.10s} {from:20.20s} {subject:40.40s}\n'
'{message-id-term:>72}\n'
).format(**message_display_data))
if thread != threads[-1]:
stream.write('\n')
def _message_display_data(self, running_data, message):
headers = ('thread-id', 'message-id', 'date', 'from', 'subject')
data = {}
for header in headers:
if header == 'thread-id':
value = message.get_thread_id()
elif header == 'message-id':
value = message.get_message_id()
data['message-id-term'] = 'id:"{0}"'.format(value)
elif header == 'date':
value = str(datetime.datetime.utcfromtimestamp(
message.get_date()).date())
else:
value = message.get_header(header)
if header == 'from':
(value, addr) = email.utils.parseaddr(value)
if not value:
value = addr.split('@')[0]
data[header] = value
next_running_data = data.copy()
for header, value in data.items():
if header in ['message-id', 'subject']:
continue
if value == running_data.get(header, None):
data[header] = ''
return (next_running_data, data)
class HtmlPage (Page):
_slug_regexp = re.compile('\W+')
def _write_header(self, views, stream):
super(HtmlPage, self)._write_header(views=views, stream=stream)
stream.write('<ul>\n')
for view in views:
if 'id' not in view:
view['id'] = self._slug(view['title'])
stream.write(
'<li><a href="#{id}">{title}</a></li>\n'.format(**view))
stream.write('</ul>\n')
def _write_view_header(self, view, stream):
stream.write('<h3 id="{id}">{title}</h3>\n'.format(**view))
stream.write('<p>\n')
if 'comment' in view:
stream.write(view['comment'])
stream.write('\n')
for line in [
'The view is generated from the following query:',
'</p>',
'<p>',
' <code>',
view['query-string'],
' </code>',
'</p>',
]:
stream.write(line)
stream.write('\n')
def _write_threads(self, threads, stream):
if not threads:
return
stream.write('<table>\n')
for thread in threads:
stream.write(' <tbody>\n')
for message_display_data in thread:
stream.write((
' <tr class="message-first">\n'
' <td>{date}</td>\n'
' <td><code>{message-id-term}</code></td>\n'
' </tr>\n'
' <tr class="message-last">\n'
' <td>{from}</td>\n'
' <td>{subject}</td>\n'
' </tr>\n'
).format(**message_display_data))
stream.write(' </tbody>\n')
if thread != threads[-1]:
stream.write(
' <tbody><tr><td colspan="2"><br /></td></tr></tbody>\n')
stream.write('</table>\n')
def _message_display_data(self, *args, **kwargs):
running_data, display_data = super(
HtmlPage, self)._message_display_data(
*args, **kwargs)
if 'subject' in display_data and 'message-id' in display_data:
d = {
'message-id': quote(display_data['message-id']),
'subject': xml.sax.saxutils.escape(display_data['subject']),
}
display_data['subject'] = (
'<a href="http://mid.gmane.org/{message-id}">{subject}</a>'
).format(**d)
for key in ['message-id', 'from']:
if key in display_data:
display_data[key] = xml.sax.saxutils.escape(display_data[key])
return (running_data, display_data)
def _slug(self, string):
return self._slug_regexp.sub('-', string)
parser = argparse.ArgumentParser()
parser.add_argument('--text', help='output plain text format',
action='store_true')
parser.add_argument('--config', help='load config from given file')
parser.add_argument('--config', help='load config from given file',
metavar='PATH')
parser.add_argument('--list-views', help='list views',
action='store_true')
parser.add_argument('--get-query', help='get query for view')
parser.add_argument('--get-query', help='get query for view',
metavar='VIEW')
args = parser.parse_args()
# read config from json file
config = read_config(path=args.config)
if args.config != None:
fp = open(args.config)
else:
nmbhome = os.getenv('NMBGIT', os.path.expanduser('~/.nmbug'))
# read only the first line from the pipe
sha1 = subprocess.Popen(['git', '--git-dir', nmbhome,
'show-ref', '-s', 'config'],
stdout=subprocess.PIPE).stdout.readline()
sha1 = sha1.rstrip()
fp = subprocess.Popen(['git', '--git-dir', nmbhome,
'cat-file', 'blob', sha1+':status-config.json'],
stdout=subprocess.PIPE).stdout
config = json.load(fp)
_PAGES['text'] = Page()
_PAGES['html'] = HtmlPage(
header='''<!DOCTYPE html>
<html lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset={encoding}" />
<title>{title}</title>
<style media="screen" type="text/css">
table {{
border-spacing: 0;
}}
tr.message-first td {{
padding-top: {inter_message_padding};
}}
tr.message-last td {{
padding-bottom: {inter_message_padding};
}}
td {{
padding-left: {border_radius};
padding-right: {border_radius};
}}
tr:first-child td:first-child {{
border-top-left-radius: {border_radius};
}}
tr:first-child td:last-child {{
border-top-right-radius: {border_radius};
}}
tr:last-child td:first-child {{
border-bottom-left-radius: {border_radius};
}}
tr:last-child td:last-child {{
border-bottom-right-radius: {border_radius};
}}
tbody:nth-child(4n+1) tr td {{
background-color: #ffd96e;
}}
tbody:nth-child(4n+3) tr td {{
background-color: #bce;
}}
</style>
</head>
<body>
<h2>{title}</h2>
<p>
Generated: {date}<br />
{blurb}
</p>
<h3>Views</h3>
'''.format(date=datetime.datetime.utcnow().date(),
title=config['meta']['title'],
blurb=config['meta']['blurb'],
encoding=_ENCODING,
inter_message_padding='0.25em',
border_radius='0.5em'),
footer='</body>\n</html>\n',
)
if args.list_views:
for view in config['views']:
print view['title']
print(view['title'])
sys.exit(0)
elif args.get_query != None:
for view in config['views']:
if args.get_query == view['title']:
print ' and '.join(view['query'])
print(' and '.join(view['query']))
sys.exit(0)
else:
# only import notmuch if needed
import notmuch
if args.text:
output_format = 'text'
page = _PAGES['text']
else:
output_format = 'html'
page = _PAGES['html']
class Thread:
def __init__(self, last, lines):
self.last = last
self.lines = lines
def join_utf8_with_newlines(self):
return '\n'.join( (line.encode('utf-8') for line in self.lines) )
def output_with_separator(threadlist, sep):
outputs = (thread.join_utf8_with_newlines() for thread in threadlist)
print sep.join(outputs)
headers = ['date', 'from', 'subject']
def print_view(title, query, comment):
query_string = ' and '.join(query)
q_new = notmuch.Query(db, query_string)
q_new.set_sort(notmuch.Query.SORT.OLDEST_FIRST)
last_thread_id = ''
threads = {}
threadlist = []
out = {}
last = None
lines = None
if output_format == 'html':
print '<h3><a name="%s" />%s</h3>' % (title, title)
print comment
print 'The view is generated from the following query:'
print '<blockquote>'
print query_string
print '</blockquote>'
print '<table>\n'
for m in q_new.search_messages():
thread_id = m.get_thread_id()
if thread_id != last_thread_id:
if threads.has_key(thread_id):
last = threads[thread_id].last
lines = threads[thread_id].lines
else:
last = {}
lines = []
thread = Thread(last, lines)
threads[thread_id] = thread
for h in headers:
last[h] = ''
threadlist.append(thread)
last_thread_id = thread_id
for header in headers:
val = m.get_header(header)
if header == 'date':
val = str.join(' ', val.split(None)[1:4])
val = str(datetime.datetime.strptime(val, '%d %b %Y').date())
elif header == 'from':
(val, addr) = rfc822.parseaddr(val)
if val == '':
val = addr.split('@')[0]
if header != 'subject' and last[header] == val:
out[header] = ''
else:
out[header] = val
last[header] = val
mid = m.get_message_id()
out['id'] = 'id:"%s"' % mid
if output_format == 'html':
out['subject'] = '<a href="http://mid.gmane.org/%s">%s</a>' \
% (urllib.quote(mid), out['subject'])
lines.append(' <tr><td>%s' % out['date'])
lines.append('</td><td>%s' % out['id'])
lines.append('</td></tr>')
lines.append(' <tr><td>%s' % out['from'])
lines.append('</td><td>%s' % out['subject'])
lines.append('</td></tr>')
else:
lines.append('%(date)-10.10s %(from)-20.20s %(subject)-40.40s\n%(id)72s' % out)
if output_format == 'html':
output_with_separator(threadlist,
'\n<tr><td colspan="2"><br /></td></tr>\n')
print '</table>'
else:
output_with_separator(threadlist, '\n\n')
# main program
db = notmuch.Database(mode=notmuch.Database.MODE.READ_WRITE)
if output_format == 'html':
print '''<?xml version="1.0" encoding="utf-8" ?>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
<title>Notmuch Patches</title>
</head>
<body>'''
print '<h2>Notmuch Patches</h2>'
print 'Generated: %s<br />' % datetime.datetime.utcnow().date()
print 'For more infomation see <a href="http://notmuchmail.org/nmbug">nmbug</a>'
print '<h3>Views</h3>'
print '<ul>'
for view in config['views']:
print '<li><a href="#%(title)s">%(title)s</a></li>' % view
print '</ul>'
for view in config['views']:
print_view(**view)
if output_format == 'html':
print '</body>\n</html>'
db = notmuch.Database(mode=notmuch.Database.MODE.READ_ONLY)
page.write(database=db, views=config['views'])

View file

@ -1,4 +1,9 @@
{
"meta": {
"title": "Notmuch Patches",
"blurb": "For more information see <a href=\"http://notmuchmail.org/nmbug\">nmbug</a>"
},
"views": [
{
"comment": "Unresolved bugs (or just need tag updating).",

View file

@ -68,7 +68,7 @@ verfail ()
echo -n "Checking that '$VERSION' is good with digits and periods... "
case $VERSION in
*[^0-9.]*)
*[!0-9.]*)
verfail "'$VERSION' contains other characters than digits and periods" ;;
.*) verfail "'$VERSION' begins with a period" ;;
*.) verfail "'$VERSION' ends with a period" ;;
@ -196,46 +196,6 @@ case $news_date in
append_emsg "Date '$news_date' in NEWS file is not in format (yyyy-mm-dd)"
esac
readonly DATE=${news_date//[()]/} # bash feature
manthdata ()
{
set x $*
if [ $# != 7 ]
then
append_emsg "'$mp' has too many '.TH' lines"
man_mismatch=1
fi
man_date=${5-} man_version=${7-}
}
echo -n "Checking that manual page dates and versions are $DATE and $VERSION... "
manfiles=`find man -type f | sort`
man_pages_ok=Yes
for mp in $manfiles
do
case $mp in
*.[0-9]) ;; # fall below this 'case ... esac'
*/Makefile.local | */Makefile ) continue ;;
*/.gitignore) continue ;;
*.bak) continue ;;
*) append_emsg "'$mp': extra file"
man_pages_ok=No
continue
esac
manthdata `sed -n '/^[.]TH NOTMUCH/ { y/"/ /; p; }' "$mp"`
if [ "$man_version" != "$VERSION" ]
then append_emsg "Version '$man_version' is not '$VERSION' in $mp"
mman_pages_ok=No
fi
if [ "$man_date" != "$DATE" ]
then append_emsg "DATE '$man_date' is not '$DATE' in $mp"
man_pages_ok=No
fi
done
echo $man_pages_ok.
if [ -n "$emsgs" ]
then
echo

2
doc/.gitignore vendored Normal file
View file

@ -0,0 +1,2 @@
docdeps.mk
_build

24
doc/INSTALL Normal file
View file

@ -0,0 +1,24 @@
This file contains some more detailed information about building and
installing the documentation.
Building with sphinx.
---------------------
- You need sphinx at least version 1.0.
- You can build build and install man pages with 'make install-man'
- You can build man, info, html, and pdf versions of the docs
(currently only the man pages) with
'make install-{man|info|html|pdf}'
Building the man pages
----------------------
- You can build the man pages with rst2man (from python-docutils) with
'make rst2man'.
- Currently there is no support to automagically install the resulting
nroff files, but it should work to modify the target install-man
in doc/Makefile.local.

81
doc/Makefile.local Normal file
View file

@ -0,0 +1,81 @@
# -*- makefile -*-
dir := doc
# You can set these variables from the command line.
SPHINXOPTS := -q
SPHINXBUILD = sphinx-build
DOCBUILDDIR := $(dir)/_build
prerst2man := python $(srcdir)/$(dir)/prerst2man.py
mkdocdeps := python $(srcdir)/$(dir)/mkdocdeps.py
# Internal variables.
ALLSPHINXOPTS := -d $(DOCBUILDDIR)/doctrees $(SPHINXOPTS) $(srcdir)/$(dir)
.PHONY: sphinx-html sphinx-texinfo sphinx-info
.PHONY: install-man build-man
%.gz: %
rm -f $@ && gzip --stdout $^ > $@
sphinx-html:
$(SPHINXBUILD) -b html $(ALLSPHINXOPTS) $(DOCBUILDDIR)/html
sphinx-texinfo:
$(SPHINXBUILD) -b texinfo $(ALLSPHINXOPTS) $(DOCBUILDDIR)/texinfo
sphinx-info: sphinx-texinfo
make -C $(DOCBUILDDIR)/texinfo info
-include $(dir)/docdeps.mk
MAN_GZIP_FILES := $(addsuffix .gz,${MAN_ROFF_FILES})
# Use the man page converter that is available. We should never depend
# on MAN_ROFF_FILES if a converter is not available.
${MAN_ROFF_FILES}: $(DOCBUILDDIR)/.roff.stamp
# By using $(DOCBUILDDIR)/.roff.stamp instead of ${MAN_ROFF_FILES}, we
# convey to make that a single invocation of this recipe builds all
# of the roff files. This prevents parallel make from starting an
# instance of this recipe for each roff file.
$(DOCBUILDDIR)/.roff.stamp: ${MAN_RST_FILES}
ifeq ($(HAVE_SPHINX),1)
$(SPHINXBUILD) -b man $(ALLSPHINXOPTS) $(DOCBUILDDIR)/man
for section in 1 5 7; do \
mkdir -p $(DOCBUILDDIR)/man/man$${section}; \
mv $(DOCBUILDDIR)/man/*.$${section} $(DOCBUILDDIR)/man/man$${section}; \
done
else ifeq ($(HAVE_RST2MAN),1)
$(prerst2man) $(srcdir)/doc $(DOCBUILDDIR)/man
else
@echo "Fatal: build dependency fail."
@false
endif
touch ${MAN_ROFF_FILES} $@
# Do not try to build or install man pages if a man page converter is
# not available.
ifeq ($(HAVE_SPHINX)$(HAVE_RST2MAN),00)
build-man:
install-man:
@echo "No sphinx or rst2man, will not install man pages."
else
build-man: ${MAN_GZIP_FILES}
install-man: ${MAN_GZIP_FILES}
mkdir -p "$(DESTDIR)$(mandir)/man1"
mkdir -p "$(DESTDIR)$(mandir)/man5"
mkdir -p "$(DESTDIR)$(mandir)/man7"
install -m0644 $(DOCBUILDDIR)/man/man1/*.1.gz $(DESTDIR)/$(mandir)/man1
install -m0644 $(DOCBUILDDIR)/man/man5/*.5.gz $(DESTDIR)/$(mandir)/man5
install -m0644 $(DOCBUILDDIR)/man/man7/*.7.gz $(DESTDIR)/$(mandir)/man7
cd $(DESTDIR)/$(mandir)/man1 && ln -sf notmuch.1.gz notmuch-setup.1.gz
endif
$(dir)/docdeps.mk: $(dir)/conf.py $(dir)/mkdocdeps.py
$(mkdocdeps) $(srcdir)/doc $(DOCBUILDDIR) $@
CLEAN := $(CLEAN) $(DOCBUILDDIR) $(dir)/docdeps.mk $(DOCBUILDDIR)/.roff.stamp
CLEAN := $(CLEAN) $(MAN_GZIP_FILES) $(MAN_ROFF_FILES) $(dir)/conf.pyc

172
doc/conf.py Normal file
View file

@ -0,0 +1,172 @@
# -*- coding: utf-8 -*-
import sys
import os
# The suffix of source filenames.
source_suffix = '.rst'
# The master toctree document.
master_doc = 'index'
# General information about the project.
project = u'notmuch'
copyright = u'2014, Carl Worth and many others'
location = os.path.dirname(__file__)
for pathdir in ['.', '..']:
version_file = os.path.join(location,pathdir,'version')
if os.path.exists(version_file):
with open(version_file,'r') as infile:
version=infile.read().replace('\n','')
# The full version, including alpha/beta/rc tags.
release = version
# List of patterns, relative to source directory, that match files and
# directories to ignore when looking for source files.
exclude_patterns = ['_build', 'notmuch-emacs.rst']
# The name of the Pygments (syntax highlighting) style to use.
pygments_style = 'sphinx'
# -- Options for HTML output ----------------------------------------------
# The theme to use for HTML and HTML Help pages. See the documentation for
# a list of builtin themes.
html_theme = 'default'
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
# so a file named "default.css" will overwrite the builtin "default.css".
html_static_path = []
# Output file base name for HTML help builder.
htmlhelp_basename = 'notmuchdoc'
# -- Options for manual page output ---------------------------------------
# One entry per manual page. List of tuples
# (source start file, name, description, authors, manual section).
man_pages = [
('man1/notmuch','notmuch',
u'thread-based email index, search, and tagging',
[u'Carl Worth and many others'], 1),
('man1/notmuch-compact','notmuch-compact',
u'compact the notmuch database',
[u'Carl Worth and many others'], 1),
('man1/notmuch-config','notmuch-config',
u'access notmuch configuration file',
[u'Carl Worth and many others'], 1),
('man1/notmuch-count','notmuch-count',
u'count messages matching the given search terms',
[u'Carl Worth and many others'], 1),
('man1/notmuch-dump','notmuch-dump',
u'creates a plain-text dump of the tags of each message',
[u'Carl Worth and many others'], 1),
('man5/notmuch-hooks','notmuch-hooks',
u'hooks for notmuch',
[u'Carl Worth and many others'], 5),
('man1/notmuch-insert','notmuch-insert',
u'add a message to the maildir and notmuch database',
[u'Carl Worth and many others'], 1),
('man1/notmuch-new','notmuch-new',
u'incorporate new mail into the notmuch database',
[u'Carl Worth and many others'], 1),
('man1/notmuch-reply','notmuch-reply',
u'constructs a reply template for a set of messages',
[u'Carl Worth and many others'], 1),
('man1/notmuch-restore','notmuch-restore',
u'restores the tags from the given file (see notmuch dump)',
[u'Carl Worth and many others'], 1),
('man1/notmuch-search','notmuch-search',
u'search for messages matching the given search terms',
[u'Carl Worth and many others'], 1),
('man7/notmuch-search-terms','notmuch-search-terms',
u'syntax for notmuch queries',
[u'Carl Worth and many others'], 7),
('man1/notmuch-show','notmuch-show',
u'show messages matching the given search terms',
[u'Carl Worth and many others'], 1),
('man1/notmuch-tag','notmuch-tag',
u'add/remove tags for all messages matching the search terms',
[u'Carl Worth and many others'], 1),
]
# If true, show URL addresses after external links.
#man_show_urls = False
# -- Options for Texinfo output -------------------------------------------
# Grouping the document tree into Texinfo files. List of tuples
# (source start file, target name, title, author,
# dir menu entry, description, category)
# If true, do not generate a @detailmenu in the "Top" node's menu.
texinfo_no_detailmenu = True
texinfo_documents = [
('notmuch-emacs', 'notmuch-emacs', u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-emacs',
'emacs based front-end for notmuch', 'Miscellaneous'),
('man1/notmuch','notmuch',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch',
'thread-based email index, search, and tagging','Miscellaneous'),
('man1/notmuch-compact','notmuch-compact',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-compact',
'compact the notmuch database','Miscellaneous'),
('man1/notmuch-config','notmuch-config',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-config',
'access notmuch configuration file','Miscellaneous'),
('man1/notmuch-count','notmuch-count',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-count',
'count messages matching the given search terms','Miscellaneous'),
('man1/notmuch-dump','notmuch-dump',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-dump',
'creates a plain-text dump of the tags of each message','Miscellaneous'),
('man5/notmuch-hooks','notmuch-hooks',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-hooks',
'hooks for notmuch','Miscellaneous'),
('man1/notmuch-insert','notmuch-insert',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-insert',
'add a message to the maildir and notmuch database','Miscellaneous'),
('man1/notmuch-new','notmuch-new',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-new',
'incorporate new mail into the notmuch database','Miscellaneous'),
('man1/notmuch-reply','notmuch-reply',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-reply',
'constructs a reply template for a set of messages','Miscellaneous'),
('man1/notmuch-restore','notmuch-restore',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-restore',
'restores the tags from the given file (see notmuch dump)','Miscellaneous'),
('man1/notmuch-search','notmuch-search',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-search',
'search for messages matching the given search terms','Miscellaneous'),
('man7/notmuch-search-terms','notmuch-search-terms',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-search-terms',
'syntax for notmuch queries','Miscellaneous'),
('man1/notmuch-show','notmuch-show',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-show',
'show messages matching the given search terms','Miscellaneous'),
('man1/notmuch-tag','notmuch-tag',u'notmuch Documentation',
u'Carl Worth and many others', 'notmuch-tag',
'add/remove tags for all messages matching the search terms','Miscellaneous'),
]

304
doc/doxygen.cfg Normal file
View file

@ -0,0 +1,304 @@
# Doxyfile 1.8.4
#---------------------------------------------------------------------------
# Project related configuration options
#---------------------------------------------------------------------------
DOXYFILE_ENCODING = UTF-8
PROJECT_NAME = "Notmuch 0.18"
PROJECT_NUMBER =
PROJECT_BRIEF =
PROJECT_LOGO =
OUTPUT_DIRECTORY =
CREATE_SUBDIRS = NO
OUTPUT_LANGUAGE = English
BRIEF_MEMBER_DESC = YES
REPEAT_BRIEF = YES
ABBREVIATE_BRIEF =
ALWAYS_DETAILED_SEC = NO
INLINE_INHERITED_MEMB = NO
FULL_PATH_NAMES = NO
STRIP_FROM_PATH =
STRIP_FROM_INC_PATH =
SHORT_NAMES = NO
JAVADOC_AUTOBRIEF = YES
QT_AUTOBRIEF = NO
MULTILINE_CPP_IS_BRIEF = NO
INHERIT_DOCS = YES
SEPARATE_MEMBER_PAGES = NO
TAB_SIZE = 8
ALIASES =
TCL_SUBST =
OPTIMIZE_OUTPUT_FOR_C = YES
OPTIMIZE_OUTPUT_JAVA = NO
OPTIMIZE_FOR_FORTRAN = NO
OPTIMIZE_OUTPUT_VHDL = NO
EXTENSION_MAPPING =
MARKDOWN_SUPPORT = YES
AUTOLINK_SUPPORT = YES
BUILTIN_STL_SUPPORT = NO
CPP_CLI_SUPPORT = NO
SIP_SUPPORT = NO
IDL_PROPERTY_SUPPORT = YES
DISTRIBUTE_GROUP_DOC = NO
SUBGROUPING = YES
INLINE_GROUPED_CLASSES = NO
INLINE_SIMPLE_STRUCTS = NO
TYPEDEF_HIDES_STRUCT = YES
LOOKUP_CACHE_SIZE = 0
#---------------------------------------------------------------------------
# Build related configuration options
#---------------------------------------------------------------------------
EXTRACT_ALL = NO
EXTRACT_PRIVATE = NO
EXTRACT_PACKAGE = NO
EXTRACT_STATIC = NO
EXTRACT_LOCAL_CLASSES = YES
EXTRACT_LOCAL_METHODS = NO
EXTRACT_ANON_NSPACES = NO
HIDE_UNDOC_MEMBERS = NO
HIDE_UNDOC_CLASSES = NO
HIDE_FRIEND_COMPOUNDS = NO
HIDE_IN_BODY_DOCS = NO
INTERNAL_DOCS = NO
CASE_SENSE_NAMES = YES
HIDE_SCOPE_NAMES = NO
SHOW_INCLUDE_FILES = NO
FORCE_LOCAL_INCLUDES = NO
INLINE_INFO = YES
SORT_MEMBER_DOCS = NO
SORT_BRIEF_DOCS = NO
SORT_MEMBERS_CTORS_1ST = NO
SORT_GROUP_NAMES = NO
SORT_BY_SCOPE_NAME = NO
STRICT_PROTO_MATCHING = NO
GENERATE_TODOLIST = NO
GENERATE_TESTLIST = NO
GENERATE_BUGLIST = NO
GENERATE_DEPRECATEDLIST= NO
ENABLED_SECTIONS =
MAX_INITIALIZER_LINES = 30
SHOW_USED_FILES = NO
SHOW_FILES = NO
SHOW_NAMESPACES = NO
FILE_VERSION_FILTER =
LAYOUT_FILE =
CITE_BIB_FILES =
#---------------------------------------------------------------------------
# configuration options related to warning and progress messages
#---------------------------------------------------------------------------
QUIET = YES
WARNINGS = YES
WARN_IF_UNDOCUMENTED = YES
WARN_IF_DOC_ERROR = YES
WARN_NO_PARAMDOC = NO
WARN_FORMAT = "$file:$line: $text"
WARN_LOGFILE =
#---------------------------------------------------------------------------
# configuration options related to the input files
#---------------------------------------------------------------------------
INPUT = lib/notmuch.h
INPUT_ENCODING = UTF-8
FILE_PATTERNS =
RECURSIVE = NO
EXCLUDE =
EXCLUDE_SYMLINKS = NO
EXCLUDE_PATTERNS =
EXCLUDE_SYMBOLS =
EXAMPLE_PATH =
EXAMPLE_PATTERNS =
EXAMPLE_RECURSIVE = NO
IMAGE_PATH =
INPUT_FILTER =
FILTER_PATTERNS =
FILTER_SOURCE_FILES = NO
FILTER_SOURCE_PATTERNS =
USE_MDFILE_AS_MAINPAGE =
#---------------------------------------------------------------------------
# configuration options related to source browsing
#---------------------------------------------------------------------------
SOURCE_BROWSER = NO
INLINE_SOURCES = NO
STRIP_CODE_COMMENTS = YES
REFERENCED_BY_RELATION = NO
REFERENCES_RELATION = NO
REFERENCES_LINK_SOURCE = YES
USE_HTAGS = NO
VERBATIM_HEADERS = NO
#---------------------------------------------------------------------------
# configuration options related to the alphabetical class index
#---------------------------------------------------------------------------
ALPHABETICAL_INDEX = NO
COLS_IN_ALPHA_INDEX = 5
IGNORE_PREFIX =
#---------------------------------------------------------------------------
# configuration options related to the HTML output
#---------------------------------------------------------------------------
GENERATE_HTML = NO
HTML_OUTPUT = html
HTML_FILE_EXTENSION = .html
HTML_HEADER =
HTML_FOOTER =
HTML_STYLESHEET =
HTML_EXTRA_STYLESHEET =
HTML_EXTRA_FILES =
HTML_COLORSTYLE_HUE = 220
HTML_COLORSTYLE_SAT = 100
HTML_COLORSTYLE_GAMMA = 80
HTML_TIMESTAMP = YES
HTML_DYNAMIC_SECTIONS = NO
HTML_INDEX_NUM_ENTRIES = 100
GENERATE_DOCSET = NO
DOCSET_FEEDNAME = "Doxygen generated docs"
DOCSET_BUNDLE_ID = org.doxygen.Project
DOCSET_PUBLISHER_ID = org.doxygen.Publisher
DOCSET_PUBLISHER_NAME = Publisher
GENERATE_HTMLHELP = NO
CHM_FILE =
HHC_LOCATION =
GENERATE_CHI = NO
CHM_INDEX_ENCODING =
BINARY_TOC = NO
TOC_EXPAND = NO
GENERATE_QHP = NO
QCH_FILE =
QHP_NAMESPACE = org.doxygen.Project
QHP_VIRTUAL_FOLDER = doc
QHP_CUST_FILTER_NAME =
QHP_CUST_FILTER_ATTRS =
QHP_SECT_FILTER_ATTRS =
QHG_LOCATION =
GENERATE_ECLIPSEHELP = NO
ECLIPSE_DOC_ID = org.doxygen.Project
DISABLE_INDEX = NO
GENERATE_TREEVIEW = NO
ENUM_VALUES_PER_LINE = 4
TREEVIEW_WIDTH = 250
EXT_LINKS_IN_WINDOW = NO
FORMULA_FONTSIZE = 10
FORMULA_TRANSPARENT = YES
USE_MATHJAX = NO
MATHJAX_FORMAT = HTML-CSS
MATHJAX_RELPATH = http://cdn.mathjax.org/mathjax/latest
MATHJAX_EXTENSIONS =
MATHJAX_CODEFILE =
SEARCHENGINE = YES
SERVER_BASED_SEARCH = NO
EXTERNAL_SEARCH = NO
SEARCHENGINE_URL =
SEARCHDATA_FILE = searchdata.xml
EXTERNAL_SEARCH_ID =
EXTRA_SEARCH_MAPPINGS =
#---------------------------------------------------------------------------
# configuration options related to the LaTeX output
#---------------------------------------------------------------------------
GENERATE_LATEX = NO
LATEX_OUTPUT = latex
LATEX_CMD_NAME = latex
MAKEINDEX_CMD_NAME = makeindex
COMPACT_LATEX = NO
PAPER_TYPE = a4
EXTRA_PACKAGES =
LATEX_HEADER =
LATEX_FOOTER =
LATEX_EXTRA_FILES =
PDF_HYPERLINKS = YES
USE_PDFLATEX = YES
LATEX_BATCHMODE = NO
LATEX_HIDE_INDICES = NO
LATEX_SOURCE_CODE = NO
LATEX_BIB_STYLE = plain
#---------------------------------------------------------------------------
# configuration options related to the RTF output
#---------------------------------------------------------------------------
GENERATE_RTF = NO
RTF_OUTPUT = rtf
COMPACT_RTF = NO
RTF_HYPERLINKS = NO
RTF_STYLESHEET_FILE =
RTF_EXTENSIONS_FILE =
#---------------------------------------------------------------------------
# configuration options related to the man page output
#---------------------------------------------------------------------------
GENERATE_MAN = YES
MAN_OUTPUT = man
MAN_EXTENSION = .3
MAN_LINKS = NO
#---------------------------------------------------------------------------
# configuration options related to the XML output
#---------------------------------------------------------------------------
GENERATE_XML = NO
XML_OUTPUT = xml
XML_SCHEMA =
XML_DTD =
XML_PROGRAMLISTING = YES
#---------------------------------------------------------------------------
# configuration options related to the DOCBOOK output
#---------------------------------------------------------------------------
GENERATE_DOCBOOK = NO
DOCBOOK_OUTPUT = docbook
#---------------------------------------------------------------------------
# configuration options for the AutoGen Definitions output
#---------------------------------------------------------------------------
GENERATE_AUTOGEN_DEF = NO
#---------------------------------------------------------------------------
# configuration options related to the Perl module output
#---------------------------------------------------------------------------
GENERATE_PERLMOD = NO
PERLMOD_LATEX = NO
PERLMOD_PRETTY = YES
PERLMOD_MAKEVAR_PREFIX =
#---------------------------------------------------------------------------
# Configuration options related to the preprocessor
#---------------------------------------------------------------------------
ENABLE_PREPROCESSING = YES
MACRO_EXPANSION = NO
EXPAND_ONLY_PREDEF = NO
SEARCH_INCLUDES = NO
INCLUDE_PATH =
INCLUDE_FILE_PATTERNS =
PREDEFINED = __DOXYGEN__
EXPAND_AS_DEFINED =
SKIP_FUNCTION_MACROS = YES
#---------------------------------------------------------------------------
# Configuration::additions related to external references
#---------------------------------------------------------------------------
TAGFILES =
GENERATE_TAGFILE =
ALLEXTERNALS = NO
EXTERNAL_GROUPS = NO
EXTERNAL_PAGES = NO
PERL_PATH = /usr/bin/perl
#---------------------------------------------------------------------------
# Configuration options related to the dot tool
#---------------------------------------------------------------------------
CLASS_DIAGRAMS = NO
MSCGEN_PATH =
HIDE_UNDOC_RELATIONS = YES
HAVE_DOT = NO
DOT_NUM_THREADS = 0
DOT_FONTNAME = Helvetica
DOT_FONTSIZE = 10
DOT_FONTPATH =
CLASS_GRAPH = YES
COLLABORATION_GRAPH = YES
GROUP_GRAPHS = YES
UML_LOOK = NO
UML_LIMIT_NUM_FIELDS = 10
TEMPLATE_RELATIONS = NO
INCLUDE_GRAPH = NO
INCLUDED_BY_GRAPH = NO
CALL_GRAPH = NO
CALLER_GRAPH = NO
GRAPHICAL_HIERARCHY = NO
DIRECTORY_GRAPH = NO
DOT_IMAGE_FORMAT = png
INTERACTIVE_SVG = NO
DOT_PATH =
DOTFILE_DIRS =
MSCFILE_DIRS =
DOT_GRAPH_MAX_NODES = 50
MAX_DOT_GRAPH_DEPTH = 0
DOT_TRANSPARENT = NO
DOT_MULTI_TARGETS = YES
GENERATE_LEGEND = NO
DOT_CLEANUP = YES

30
doc/index.rst Normal file
View file

@ -0,0 +1,30 @@
Welcome to notmuch's documentation!
===================================
Contents:
.. toctree::
:titlesonly:
man1/notmuch
man1/notmuch-compact
man1/notmuch-config
man1/notmuch-count
man1/notmuch-dump
man5/notmuch-hooks
man1/notmuch-insert
man1/notmuch-new
man1/notmuch-reply
man1/notmuch-restore
man1/notmuch-search
man7/notmuch-search-terms
man1/notmuch-show
man1/notmuch-tag
Indices and tables
==================
* :ref:`genindex`
* :ref:`modindex`
* :ref:`search`

View file

@ -0,0 +1,52 @@
===============
notmuch-compact
===============
SYNOPSIS
========
**notmuch** **compact** [--quiet] [--backup=<*directory*>]
DESCRIPTION
===========
The **compact** command can be used to compact the notmuch database.
This can both reduce the space required by the database and improve
lookup performance.
The compacted database is built in a temporary directory and is later
moved into the place of the origin database. The original uncompacted
database is discarded, unless the ``--backup=``\ <directory> option is
used.
Note that the database write lock will be held during the compaction
process (which may be quite long) to protect data integrity.
Supported options for **compact** include
``--backup=``\ <directory>
Save the current database to the given directory before
replacing it with the compacted database. The backup directory
must not exist and it must reside on the same mounted filesystem
as the current database.
``--quiet``
Do not report database compaction progress to stdout.
ENVIRONMENT
===========
The following environment variables can be used to control the behavior
of notmuch.
**NOTMUCH\_CONFIG**
Specifies the location of the notmuch configuration file. Notmuch
will use ${HOME}/.notmuch-config if this variable is not set.
SEE ALSO
========
**notmuch(1)**, **notmuch-count(1)**, **notmuch-dump(1)**,
**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

123
doc/man1/notmuch-config.rst Normal file
View file

@ -0,0 +1,123 @@
==============
notmuch-config
==============
SYNOPSIS
========
**notmuch** **config** **get** <*section*>.<*item*>
**notmuch** **config** **set** <*section*>.<*item*> [*value* ...]
**notmuch** **config** **list**
DESCRIPTION
===========
The **config** command can be used to get or set settings in the notmuch
configuration file.
**get**
The value of the specified configuration item is printed to
stdout. If the item has multiple values (it is a list), each
value is separated by a newline character.
**set**
The specified configuration item is set to the given value. To
specify a multiple-value item (a list), 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.
**list**
Every configuration item is printed to stdout, each on a
separate line of the form:
*section*.\ *item*\ =\ *value*
No additional whitespace surrounds the dot or equals sign
characters. In a multiple-value item (a list), the values are
separated by semicolon characters.
The available configuration items are described below.
**database.path**
The top-level directory where your mail currently exists and to
where mail will be delivered in the future. Files should be
individual email messages. Notmuch will store its database
within a sub-directory of the path configured here named
``.notmuch``.
**user.name**
Your full name.
**user.primary\_email**
Your primary email address.
**user.other\_email**
A list of other email addresses at which you receive email.
**new.tags**
A list of tags that will be added to all messages incorporated
by **notmuch new**.
**new.ignore**
A list of file and directory names, without path, that will not
be searched for messages by **notmuch new**. All the files and
directories matching any of the names specified here will be
ignored, regardless of the location in the mail store directory
hierarchy.
**search.exclude\_tags**
A list of tags that will be excluded from search results by
default. Using an excluded tag in a query will override that
exclusion.
**maildir.synchronize\_flags**
If true, then the following maildir flags (in message filenames)
will be synchronized with the corresponding notmuch tags:
+--------+-----------------------------------------------+
| Flag | Tag |
+========+===============================================+
| D | draft |
+--------+-----------------------------------------------+
| F | flagged |
+--------+-----------------------------------------------+
| P | passed |
+--------+-----------------------------------------------+
| R | replied |
+--------+-----------------------------------------------+
| S | unread (added when 'S' flag is not present) |
+--------+-----------------------------------------------+
The **notmuch new** command will notice flag changes in
filenames and update tags, while the **notmuch tag** and
**notmuch restore** commands will notice tag changes and update
flags in filenames.
If there have been any changes in the maildir (new messages
added, old ones removed or renamed, maildir flags changed,
etc.), it is advisable to run **notmuch new** before **notmuch
tag** or **notmuch restore** commands to ensure the tag changes
are properly synchronized to the maildir flags, as the commands
expect the database and maildir to be in sync.
ENVIRONMENT
===========
The following environment variables can be used to control the behavior
of notmuch.
**NOTMUCH\_CONFIG**
Specifies the location of the notmuch configuration file. Notmuch
will use ${HOME}/.notmuch-config if this variable is not set.
SEE ALSO
========
**notmuch(1)**, **notmuch-count(1)**, **notmuch-dump(1)**,
**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

View file

@ -0,0 +1,60 @@
=============
notmuch-count
=============
SYNOPSIS
========
**notmuch** **count** [*option* ...] <*search-term*> ...
DESCRIPTION
===========
Count messages matching the search terms.
The number of matching messages (or threads) is output to stdout.
With no search terms, a count of all messages (or threads) in the
database will be displayed.
See **notmuch-search-terms(7)** for details of the supported syntax for
<search-terms>.
Supported options for **count** include
``--output=(messages|threads|files)``
**messages**
Output the number of matching messages. This is the default.
**threads**
Output the number of matching threads.
**files**
Output the number of files associated with matching
messages. This may be bigger than the number of matching
messages due to duplicates (i.e. multiple files having the
same message-id).
``--exclude=(true|false)``
Specify whether to omit messages matching search.tag\_exclude
from the count (the default) or not.
``--batch``
Read queries from a file (stdin by default), one per line, and
output the number of matching messages (or threads) to stdout,
one per line. On an empty input line the count of all messages
(or threads) in the database will be output. This option is not
compatible with specifying search terms on the command line.
``--input=``\ <filename>
Read input from given file, instead of from stdin. Implies
``--batch``.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-dump(1)**,
**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

75
doc/man1/notmuch-dump.rst Normal file
View file

@ -0,0 +1,75 @@
============
notmuch-dump
============
SYNOPSIS
========
**notmuch** **dump** [--format=(batch-tag|sup)] [--] [--output=<*file*>] [--] [<*search-term*> ...]
DESCRIPTION
===========
Dump tags for messages matching the given search terms.
Output is to the given filename, if any, or to stdout.
These tags are the only data in the notmuch database that can't be
recreated from the messages themselves. The output of notmuch dump is
therefore the only critical thing to backup (and much more friendly to
incremental backup than the native database files.)
``--gzip``
Compress the output in a format compatible with **gzip(1)**.
``--format=(sup|batch-tag)``
Notmuch restore supports two plain text dump formats, both with one
message-id per line, followed by a list of tags.
**batch-tag**
The default **batch-tag** dump format is intended to more robust
against malformed message-ids and tags containing whitespace or
non-\ **ascii(7)** characters. Each line has the form
+<*encoded-tag*\ > +<*encoded-tag*\ > ... --
id:<*quoted-message-id*\ >
Tags are hex-encoded by replacing every byte not matching the
regex **[A-Za-z0-9@=.,\_+-]** with **%nn** where nn is the two
digit hex encoding. The message ID is a valid Xapian query,
quoted using Xapian boolean term quoting rules: if the ID
contains whitespace or a close paren or starts with a double
quote, it must be enclosed in double quotes and double quotes
inside the ID must be doubled. The astute reader will notice
this is a special case of the batch input format for
**notmuch-tag(1)**; note that the single message-id query is
mandatory for **notmuch-restore(1)**.
**sup**
The **sup** dump file format is specifically chosen to be
compatible with the format of files produced by sup-dump. So if
you've previously been using sup for mail, then the **notmuch
restore** command provides you a way to import all of your tags
(or labels as sup calls them). Each line has the following form
<*message-id*\ > **(** <*tag*\ > ... **)**
with zero or more tags are separated by spaces. Note that
(malformed) message-ids may contain arbitrary non-null
characters. Note also that tags with spaces will not be
correctly restored with this format.
With no search terms, a dump of all messages in the database will be
generated. A "--" argument instructs notmuch that the remaining
arguments are search terms.
See **notmuch-search-terms(7)** for details of the supported syntax
for <search-terms>.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

View file

@ -0,0 +1,58 @@
==============
notmuch-insert
==============
SYNOPSIS
========
**notmuch** **insert** [option ...] [+<*tag*>|-<*tag*> ...]
DESCRIPTION
===========
**notmuch insert** reads a message from standard input and delivers it
into the maildir directory given by configuration option
**database.path**, then incorporates the message into the notmuch
database. It is an alternative to using a separate tool to deliver the
message then running **notmuch new** afterwards.
The new message will be tagged with the tags specified by the
**new.tags** configuration option, then by operations specified on the
command-line: tags prefixed by '+' are added while those prefixed by '-'
are removed.
If the new message is a duplicate of an existing message in the database
(it has same Message-ID), it will be added to the maildir folder and
notmuch database, but the tags will not be changed.
Option arguments must appear before any tag operation arguments.
Supported options for **insert** include
``--folder=<``\ folder\ **>**
Deliver the message to the specified folder, relative to the
top-level directory given by the value of **database.path**. The
default is to deliver to the top-level directory.
``--create-folder``
Try to create the folder named by the ``--folder`` option, if it
does not exist. Otherwise the folder must already exist for mail
delivery to succeed.
EXIT STATUS
===========
This command returns exit status 0 if the message was successfully added
to the mail directory, even if the message could not be indexed and
added to the notmuch database. In the latter case, a warning will be
printed to standard error but the message file will be left on disk.
If the message could not be written to disk then a non-zero exit status
is returned.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-reply(1)**,
**notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

52
doc/man1/notmuch-new.rst Normal file
View file

@ -0,0 +1,52 @@
===========
notmuch-new
===========
SYNOPSIS
========
**notmuch** **new** [options]
DESCRIPTION
===========
Find and import any new messages to the database.
The **new** command scans all sub-directories of the database,
performing full-text indexing on new messages that are found. Each new
message will automatically be tagged with both the **inbox** and
**unread** tags.
You should run **notmuch new** once after first running **notmuch
setup** to create the initial database. The first run may take a long
time if you have a significant amount of mail (several hundred thousand
messages or more). Subsequently, you should run **notmuch new** whenever
new mail is delivered and you wish to incorporate it into the database.
These subsequent runs will be much quicker than the initial run.
Invoking ``notmuch`` with no command argument will run **new** if
**notmuch setup** has previously been completed, but **notmuch new** has
not previously been run.
**notmuch new** updates tags according to maildir flag changes if the
**maildir.synchronize\_flags** configuration option is enabled. See
**notmuch-config(1)** for details.
The **new** command supports hooks. See **notmuch-hooks(5)** for more
details on hooks.
Supported options for **new** include
``--no-hooks``
Prevents hooks from being run.
``--quiet``
Do not print progress or results.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

112
doc/man1/notmuch-reply.rst Normal file
View file

@ -0,0 +1,112 @@
=============
notmuch-reply
=============
SYNOPSIS
========
**notmuch** **reply** [option ...] <*search-term*> ...
DESCRIPTION
===========
Constructs a reply template for a set of messages.
To make replying to email easier, **notmuch reply** takes an existing
set of messages and constructs a suitable mail template. The Reply-to:
header (if any, otherwise From:) is used for the To: address. Unless
``--reply-to=sender`` is specified, values from the To: and Cc: headers
are copied, but not including any of the current user's email addresses
(as configured in primary\_mail or other\_email in the .notmuch-config
file) in the recipient list.
It also builds a suitable new subject, including Re: at the front (if
not already present), and adding the message IDs of the messages being
replied to to the References list and setting the In-Reply-To: field
correctly.
Finally, the original contents of the emails are quoted by prefixing
each line with '> ' and included in the body.
The resulting message template is output to stdout.
Supported options for **reply** include
``--format=``\ (**default**\ \|\ **json**\ \|\ **sexp**\ \|\ **headers-only**)
**default**
Includes subject and quoted message body as an RFC 2822
message.
**json**
Produces JSON output containing headers for a reply message
and the contents of the original message. This output can be
used by a client to create a reply message intelligently.
**sexp**
Produces S-Expression output containing headers for a reply
message and the contents of the original message. This
output can be used by a client to create a reply message
intelligently.
**headers-only**
Only produces In-Reply-To, References, To, Cc, and Bcc
headers.
``--format-version=N``
Use the specified structured output format version. This is
intended for programs that invoke **notmuch(1)** internally. If
omitted, the latest supported version will be used.
``--reply-to=``\ (**all**\ \|\ **sender**)
**all** (default)
Replies to all addresses.
**sender**
Replies only to the sender. If replying to user's own
message (Reply-to: or From: header is one of the user's
configured email addresses), try To:, Cc:, and Bcc: headers
in this order, and copy values from the first that contains
something other than only the user's addresses.
``--decrypt``
Decrypt any MIME encrypted parts found in the selected content
(ie. "multipart/encrypted" parts). Status of the decryption will
be reported (currently only supported with --format=json and
--format=sexp) and on successful decryption the
multipart/encrypted part will be replaced by the decrypted
content.
Decryption expects a functioning **gpg-agent(1)** to provide any
needed credentials. Without one, the decryption will fail.
See **notmuch-search-terms(7)** for details of the supported syntax for
<search-terms>.
Note: It is most common to use **notmuch reply** with a search string
matching a single message, (such as id:<message-id>), but it can be
useful to reply to several messages at once. For example, when a series
of patches are sent in a single thread, replying to the entire thread
allows for the reply to comment on issues found in multiple patches. The
default format supports replying to multiple messages at once, but the
JSON and S-Expression formats do not.
EXIT STATUS
===========
This command supports the following special exit status codes
``20``
The requested format version is too old.
``21``
The requested format version is too new.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

View file

@ -0,0 +1,67 @@
===============
notmuch-restore
===============
SYNOPSIS
========
**notmuch** **restore** [--accumulate] [--format=(auto|batch-tag|sup)] [--input=<*filename*>]
DESCRIPTION
===========
Restores the tags from the given file (see **notmuch dump**).
The input is read from the given filename, if any, or from stdin.
Supported options for **restore** include
``--accumulate``
The union of the existing and new tags is applied, instead of
replacing each message's tags as they are read in from the dump
file.
``--format=(sup|batch-tag|auto)``
Notmuch restore supports two plain text dump formats, with each
line specifying a message-id and a set of tags. For details of
the actual formats, see **notmuch-dump(1)**.
**sup**
The **sup** dump file format is specifically chosen to be
compatible with the format of files produced by sup-dump. So
if you've previously been using sup for mail, then the
**notmuch restore** command provides you a way to import all
of your tags (or labels as sup calls them).
**batch-tag**
The **batch-tag** dump format is intended to more robust
against malformed message-ids and tags containing whitespace
or non-\ **ascii(7)** characters. See **notmuch-dump(1)**
for details on this format.
**notmuch restore** updates the maildir flags according to
tag changes if the **maildir.synchronize\_flags**
configuration option is enabled. See **notmuch-config(1)**
for details.
**auto**
This option (the default) tries to guess the format from the
input. For correctly formed input in either supported
format, this heuristic, based the fact that batch-tag format
contains no parentheses, should be accurate.
GZIPPED INPUT
=============
\ **notmuch restore** will detect if the input is compressed in
**gzip(1)** format and automatically decompress it while reading. This
detection does not depend on file naming and in particular works for
standard input.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

151
doc/man1/notmuch-search.rst Normal file
View file

@ -0,0 +1,151 @@
==============
notmuch-search
==============
SYNOPSIS
========
**notmuch** **search** [*option* ...] <*search-term*> ...
DESCRIPTION
===========
Search for messages matching the given search terms, and display as
results the threads containing the matched messages.
The output consists of one line per thread, giving a thread ID, the date
of the newest (or oldest, depending on the sort option) matched message
in the thread, the number of matched messages and total messages in the
thread, the names of all participants in the thread, and the subject of
the newest (or oldest) message.
See **notmuch-search-terms(7)** for details of the supported syntax for
<search-terms>.
Supported options for **search** include
``--format=``\ (**json**\ \|\ **sexp**\ \|\ **text**\ \|\ **text0**)
Presents the results in either JSON, S-Expressions, newline
character separated plain-text (default), or null character
separated plain-text (compatible with **xargs(1)** -0 option
where available).
``--format-version=N``
Use the specified structured output format version. This is
intended for programs that invoke **notmuch(1)** internally. If
omitted, the latest supported version will be used.
``--output=(summary|threads|messages|files|tags)``
**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.
**threads**
Output the thread IDs of all threads with any message
matching the search terms, either one per line
(--format=text), separated by null characters
(--format=text0), as a JSON array (--format=json), or an
S-Expression list (--format=sexp).
**messages**
Output the message IDs of all messages matching the search
terms, either one per line (--format=text), separated by
null characters (--format=text0), as a JSON array
(--format=json), or as an S-Expression list (--format=sexp).
**files**
Output the filenames of all messages matching the search
terms, either one per line (--format=text), separated by
null characters (--format=text0), as a JSON array
(--format=json), or as an S-Expression list (--format=sexp).
Note that each message may have multiple filenames
associated with it. All of them are included in the output
(unless limited with the --duplicate=N option). This may
be particularly confusing for **folder:** or **path:**
searches in a specified directory, as the messages may
have duplicates in other directories that are included in
the output, although these files alone would not match the
search.
**tags**
Output all tags that appear on any message matching the
search terms, either one per line (--format=text), separated
by null characters (--format=text0), as a JSON array
(--format=json), or as an S-Expression list (--format=sexp).
``--sort=``\ (**newest-first**\ \|\ **oldest-first**)
This option can be used to present results in either
chronological order (**oldest-first**) or reverse chronological
order (**newest-first**).
Note: The thread order will be distinct between these two
options (beyond being simply reversed). When sorting by
**oldest-first** the threads will be sorted by the oldest
message in each thread, but when sorting by **newest-first** the
threads will be sorted by the newest message in each thread.
By default, results will be displayed in reverse chronological
order, (that is, the newest results will be displayed first).
``--offset=[-]N``
Skip displaying the first N results. With the leading '-', start
at the Nth result from the end.
``--limit=N``
Limit the number of displayed results to N.
``--exclude=(true|false|all|flag)``
A message is called "excluded" if it matches at least one tag in
search.tag\_exclude that does not appear explicitly in the
search terms. This option specifies whether to omit excluded
messages in the search process.
The default value, **true**, prevents excluded messages from
matching the search terms.
**all** additionally prevents excluded messages from appearing
in displayed results, in effect behaving as though the excluded
messages do not exist.
**false** allows excluded messages to match search terms and
appear in displayed results. Excluded messages are still marked
in the relevant outputs.
**flag** only has an effect when ``--output=summary``. The
output is almost identical to **false**, but the "match count"
is the number of matching non-excluded messages in the thread,
rather than the number of matching messages.
``--duplicate=N``
Effective with ``--output=files``, output the Nth filename
associated with each message matching the query (N is 1-based).
If N is greater than the number of files associated with the
message, don't print anything.
Note that this option is orthogonal with the **folder:** search
prefix. The prefix matches messages based on filenames. This
option filters filenames of the matching messages.
EXIT STATUS
===========
This command supports the following special exit status codes
``20``
The requested format version is too old.
``21``
The requested format version is too new.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

182
doc/man1/notmuch-show.rst Normal file
View file

@ -0,0 +1,182 @@
============
notmuch-show
============
SYNOPSIS
========
**notmuch** **show** [*option* ...] <*search-term*> ...
DESCRIPTION
===========
Shows all messages matching the search terms.
See **notmuch-search-terms(7)** for details of the supported syntax for
<search-terms>.
The messages will be grouped and sorted based on the threading (all
replies to a particular message will appear immediately after that
message in date order). The output is not indented by default, but depth
tags are printed so that proper indentation can be performed by a
post-processor (such as the emacs interface to notmuch).
Supported options for **show** include
``--entire-thread=(true|false)``
If true, **notmuch show** outputs all messages in the thread of
any message matching the search terms; if false, it outputs only
the matching messages. For ``--format=json`` and
``--format=sexp`` this defaults to true. For other formats, this
defaults to false.
``--format=(text|json|sexp|mbox|raw)``
**text** (default for messages)
The default plain-text format has all text-content MIME
parts decoded. Various components in the output,
(**message**, **header**, **body**, **attachment**, and MIME
**part**), will 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. For a multipart MIME message, these parts will be
nested.
**json**
The output is formatted with Javascript Object Notation
(JSON). This format is more robust than the text format for
automated processing. The nested structure of multipart MIME
messages is reflected in nested JSON output. By default JSON
output includes all messages in a matching thread; that is,
by default,
``--format=json`` sets ``--entire-thread``. The caller can
disable this behaviour by setting ``--entire-thread=false``.
The JSON output is always encoded as UTF-8 and any message
content included in the output will be charset-converted to
UTF-8.
**sexp**
The output is formatted as the Lisp s-expression (sexp)
equivalent of the JSON format above. Objects are formatted
as property lists whose keys are keywords (symbols preceded
by a colon). True is formatted as ``t`` and both false and
null are formatted as ``nil``. As for JSON, the s-expression
output is always encoded as UTF-8.
**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
**raw** (default if --part is given)
Write the raw bytes of the given MIME part of a message to
standard out. For this format, it is an error to specify a
query that matches more than one message.
If the specified part is a leaf part, this outputs the
body of the part after performing content transfer
decoding (but no charset conversion). This is suitable for
saving attachments, for example.
For a multipart or message part, the output includes the
part headers as well as the body (including all child
parts). No decoding is performed because multipart and
message parts cannot have non-trivial content transfer
encoding. Consumers of this may need to implement MIME
decoding and similar functions.
``--format-version=N``
Use the specified structured output format version. This is
intended for programs that invoke **notmuch(1)** internally. If
omitted, the latest supported version will be used.
``--part=N``
Output the single decoded MIME part N of a single message. The
search terms must match only a single message. Message parts are
numbered in a depth-first walk of the message MIME structure,
and are identified in the 'json', 'sexp' or 'text' output
formats.
Note that even a message with no MIME structure or a single
body part still has two MIME parts: part 0 is the whole
message (headers and body) and part 1 is just the body.
``--verify``
Compute and report the validity of any MIME cryptographic
signatures found in the selected content (ie. "multipart/signed"
parts). Status of the signature will be reported (currently only
supported with --format=json and --format=sexp), and the
multipart/signed part will be replaced by the signed data.
``--decrypt``
Decrypt any MIME encrypted parts found in the selected content
(ie. "multipart/encrypted" parts). Status of the decryption will
be reported (currently only supported with --format=json and
--format=sexp) and on successful decryption the
multipart/encrypted part will be replaced by the decrypted
content.
Decryption expects a functioning **gpg-agent(1)** to provide any
needed credentials. Without one, the decryption will fail.
Implies --verify.
``--exclude=(true|false)``
Specify whether to omit threads only matching
search.tag\_exclude from the search results (the default) or
not. In either case the excluded message will be marked with the
exclude flag (except when output=mbox when there is nowhere to
put the flag).
If --entire-thread is specified then complete threads are
returned regardless (with the excluded flag being set when
appropriate) but threads that only match in an excluded message
are not returned when ``--exclude=true.``
The default is ``--exclude=true.``
``--body=(true|false)``
If true (the default) **notmuch show** includes the bodies of
the messages in the output; if false, bodies are omitted.
``--body=false`` is only implemented for the json and sexp
formats and it is incompatible with ``--part > 0.``
This is useful if the caller only needs the headers as body-less
output is much faster and substantially smaller.
``--include-html``
Include "text/html" parts as part of the output (currently only
supported with --format=json and --format=sexp). By default,
unless ``--part=N`` is used to select a specific part or
``--include-html`` is used to include all "text/html" parts, no
part with content type "text/html" is included in the output.
A common use of **notmuch show** is to display a single thread of email
messages. For this, use a search term of "thread:<thread-id>" as can be
seen in the first column of output from the **notmuch search** command.
EXIT STATUS
===========
This command supports the following special exit status codes
``20``
The requested format version is too old.
``21``
The requested format version is too new.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
**notmuch-search(1)**, **notmuch-search-terms(7)**, **notmuch-tag(1)**

107
doc/man1/notmuch-tag.rst Normal file
View file

@ -0,0 +1,107 @@
===========
notmuch-tag
===========
SYNOPSIS
========
**notmuch** **tag** [options ...] +<*tag*>|-<*tag*> [--] <*search-term*> ...
**notmuch** **tag** **--batch** [--input=<*filename*>]
DESCRIPTION
===========
Add/remove tags for all messages matching the search terms.
See **notmuch-search-terms(7)** for details of the supported syntax for
<*search-term*\ >.
Tags prefixed by '+' are added while those prefixed by '-' are removed.
For each message, tag changes are applied in the order they appear on
the command line.
The beginning of the search terms is recognized by the first argument
that begins with neither '+' nor '-'. Support for an initial search term
beginning with '+' or '-' is provided by allowing the user to specify a
"--" argument to separate the tags from the search terms.
**notmuch tag** updates the maildir flags according to tag changes if
the **maildir.synchronize\_flags** configuration option is enabled. See
**notmuch-config(1)** for details.
Supported options for **tag** include
``--remove-all``
Remove all tags from each message matching the search terms
before applying the tag changes appearing on the command line.
This means setting the tags of each message to the tags to be
added. If there are no tags to be added, the messages will have
no tags.
``--batch``
Read batch tagging operations from a file (stdin by default).
This is more efficient than repeated **notmuch tag**
invocations. See `TAG FILE FORMAT <#tag_file_format>`__ below
for the input format. This option is not compatible with
specifying tagging on the command line.
``--input=``\ <filename>
Read input from given file, instead of from stdin. Implies
``--batch``.
TAG FILE FORMAT
===============
The input must consist of lines of the format:
+<*tag*\ >\|-<*tag*\ > [...] [--] <*query*\ >
Each line is interpreted similarly to **notmuch tag** command line
arguments. The delimiter is one or more spaces ' '. Any characters in
<*tag*\ > **may** be hex-encoded with %NN where NN is the hexadecimal
value of the character. To hex-encode a character with a multi-byte
UTF-8 encoding, hex-encode each byte. Any spaces in <tag> **must** be
hex-encoded as %20. Any characters that are not part of <*tag*\ > **must
not** be hex-encoded.
In the future tag:"tag with spaces" style quoting may be supported for
<*tag*\ > as well; for this reason all double quote characters in
<*tag*\ > **should** be hex-encoded.
The <*query*\ > should be quoted using Xapian boolean term quoting
rules: if a term contains whitespace or a close paren or starts with a
double quote, it must be enclosed in double quotes (not including any
prefix) and double quotes inside the term must be doubled (see below for
examples).
Leading and trailing space ' ' is ignored. Empty lines and lines
beginning with '#' are ignored.
EXAMPLE
-------
The following shows a valid input to batch tagging. Note that only the
isolated '\*' acts as a wildcard. Also note the two different quotings
of the tag **space in tags**
::
+winner *
+foo::bar%25 -- (One and Two) or (One and tag:winner)
+found::it -- tag:foo::bar%
# ignore this line and the next
+space%20in%20tags -- Two
# add tag '(tags)', among other stunts.
+crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
+match*crazy -- tag:crazy{
+some_tag -- id:"this is ""nauty)"""
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
**notmuch-search(1)**, **notmuch-search-terms(7)**, **notmuch-show(1)**,

143
doc/man1/notmuch.rst Normal file
View file

@ -0,0 +1,143 @@
=======
notmuch
=======
SYNOPSIS
========
**notmuch** [option ...] **command** [arg ...]
DESCRIPTION
===========
Notmuch is a command-line based program for indexing, searching,
reading, and tagging large collections of email messages.
This page describes how to get started using notmuch from the command
line, and gives a brief overview of the commands available. For more
information on e.g. **notmuch show** consult the **notmuch-show(1)** man
page, also accessible via **notmuch help show**
The quickest way to get started with Notmuch is to simply invoke the
``notmuch`` command with no arguments, which will interactively guide
you through the process of indexing your mail.
NOTE
====
While the command-line program ``notmuch`` provides powerful
functionality, it does not provide the most convenient interface for
that functionality. More sophisticated interfaces are expected to be
built on top of either the command-line interface, or more likely, on
top of the notmuch library interface. See http://notmuchmail.org for
more about alternate interfaces to notmuch. The emacs-based interface to
notmuch (available under **emacs/** in the Notmuch source distribution)
is probably the most widely used at this time.
OPTIONS
=======
Supported global options for ``notmuch`` include
``--help``
Print a synopsis of available commands and exit.
``--version``
Print the installed version of notmuch, and exit.
``--config=FILE``
Specify the configuration file to use. This overrides any
configuration file specified by ${NOTMUCH\_CONFIG}.
COMMANDS
========
SETUP
-----
The **notmuch setup** command is used to configure Notmuch for first
use, (or to reconfigure it later).
The setup command will prompt for your full name, your primary email
address, any alternate email addresses you use, and the directory
containing your email archives. Your answers will be written to a
configuration file in ${NOTMUCH\_CONFIG} (if set) or
${HOME}/.notmuch-config . This configuration file will be created with
descriptive comments, making it easy to edit by hand later to change the
configuration. Or you can run **notmuch setup** again to change the
configuration.
The mail directory you specify can contain any number of sub-directories
and should 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.
Mail storage that uses mbox format, (where one mbox file contains many
messages), will not work with notmuch. If that's how your mail is
currently stored, it is recommended you first convert it to maildir
format with a utility such as mb2md before running **notmuch setup .**
Invoking ``notmuch`` with no command argument will run **setup** if the
setup command has not previously been completed.
OTHER COMMANDS
--------------
Several of the notmuch commands accept search terms with a common
syntax. See **notmuch-search-terms**\ (7) for more details on the
supported syntax.
The **search**, **show** and **count** commands are used to query the
email database.
The **reply** command is useful for preparing a template for an email
reply.
The **tag** command is the only command available for manipulating
database contents.
The **dump** and **restore** commands can be used to create a textual
dump of email tags for backup purposes, and to restore from that dump.
The **config** command can be used to get or set settings in the notmuch
configuration file.
ENVIRONMENT
===========
The following environment variables can be used to control the behavior
of notmuch.
**NOTMUCH\_CONFIG**
Specifies the location of the notmuch configuration file. Notmuch
will use ${HOME}/.notmuch-config if this variable is not set.
**NOTMUCH\_TALLOC\_REPORT**
Location to write a talloc memory usage report. See
**talloc\_enable\_leak\_report\_full** in **talloc(3)** for more
information.
**NOTMUCH\_DEBUG\_QUERY**
If set to a non-empty value, the notmuch library will print (to
stderr) Xapian queries it constructs.
SEE ALSO
========
**notmuch-config(1)**, **notmuch-count(1)**, **notmuch-dump(1)**,
**notmuch-hooks(5)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**
The notmuch website: **http://notmuchmail.org**
CONTACT
=======
Feel free to send questions, comments, or kudos to the notmuch mailing
list <notmuch@notmuchmail.org> . Subscription is not required before
posting, but is available from the notmuchmail.org website.
Real-time interaction with the Notmuch community is available via IRC
(server: irc.freenode.net, channel: #notmuch).

View file

@ -0,0 +1,44 @@
=============
notmuch-hooks
=============
SYNOPSIS
========
$DATABASEDIR/.notmuch/hooks/*
DESCRIPTION
===========
Hooks are scripts (or arbitrary executables or symlinks to such) that
notmuch invokes before and after certain actions. These scripts reside
in the .notmuch/hooks directory within the database directory and must
have executable permissions.
The currently available hooks are described below.
**pre-new**
This hook is invoked by the **new** command before scanning or
importing new messages into the database. If this hook exits
with a non-zero status, notmuch will abort further processing of
the **new** command.
Typically this hook is used for fetching or delivering new mail
to be imported into the database.
**post-new**
This hook is invoked by the **new** command after new messages
have been imported into the database and initial tags have been
applied. The hook will not be run if there have been any errors
during the scan or import.
Typically this hook is used to perform additional query-based
tagging on the imported messages.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-insert(1)**, **notmuch-new(1)**,
**notmuch-reply(1)**, **notmuch-restore(1)**, **notmuch-search(1)**,
**notmuch-search-terms(7)**, **notmuch-show(1)**, **notmuch-tag(1)**

View file

@ -0,0 +1,252 @@
====================
notmuch-search-terms
====================
SYNOPSIS
========
**notmuch** **count** [option ...] <*search-term*> ...
**notmuch** **dump** [--format=(batch-tag|sup)] [--] [--output=<*file*>] [--] [<*search-term*> ...]
**notmuch** **search** [option ...] <*search-term*> ...
**notmuch** **show** [option ...] <*search-term*> ...
**notmuch** **tag** +<*tag*> ... -<*tag*> [--] <*search-term*> ...
DESCRIPTION
===========
Several notmuch commands accept a common syntax for search terms.
The search terms can consist of free-form text (and quoted phrases)
which will match all messages that contain all of the given
terms/phrases in the body, the subject, or any of the sender or
recipient headers.
As a special case, a search string consisting of exactly a single
asterisk ("\*") will match all messages.
In addition to free text, the following prefixes can be used to force
terms to match against specific portions of an email, (where <brackets>
indicate user-supplied values):
- from:<name-or-address>
- to:<name-or-address>
- subject:<word-or-quoted-phrase>
- attachment:<word>
- tag:<tag> (or is:<tag>)
- id:<message-id>
- thread:<thread-id>
- folder:<maildir-folder>
- path:<directory-path> or path:<directory-path>/**
- date:<since>..<until>
The **from:** prefix is used to match the name or address of the sender
of an email message.
The **to:** prefix is used to match the names or addresses of any
recipient of an email message, (whether To, Cc, or Bcc).
Any term prefixed with **subject:** will match only text from the
subject of an email. Searching for a phrase in the subject is supported
by including quotation marks around the phrase, immediately following
**subject:**.
The **attachment:** prefix can be used to search for specific filenames
(or extensions) of attachments to email messages.
For **tag:** and **is:** valid tag values include **inbox** and
**unread** by default for new messages added by **notmuch new** as well
as any other tag values added manually with **notmuch tag**.
For **id:**, message ID values are the literal contents of the
Message-ID: header of email messages, but without the '<', '>'
delimiters.
The **thread:** prefix can be used with the thread ID values that are
generated internally by notmuch (and do not appear in email messages).
These thread ID values can be seen in the first column of output from
**notmuch search**
The **path:** prefix searches for email messages that are in
particular directories within the mail store. The directory must be
specified relative to the top-level maildir (and without the leading
slash). By default, **path:** matches messages in the specified
directory only. The "/\*\*" suffix can be used to match messages in
the specified directory and all its subdirectories recursively.
**path:""** matches messages in the root of the mail store and,
likewise, **path:\*\*** matches all messages.
The **folder:** prefix searches for email messages by maildir or MH
folder. For MH-style folders, this is equivalent to **path:**. For
maildir, this includes messages in the "new" and "cur"
subdirectories. The exact syntax for maildir folders depends on your
mail configuration. For maildir++, **folder:""** matches the inbox
folder (which is the root in maildir++), other folder names always
start with ".", and nested folders are separated by "."s, such as
**folder:.classes.topology**. For "file system" maildir, the inbox is
typically **folder:INBOX** and nested folders are separated by
slashes, such as **folder:classes/topology**.
Both **path:** and **folder:** will find a message if *any* copy of
that message is in the specific directory/folder.
The **date:** prefix can be used to restrict the results to only
messages within a particular time range (based on the Date: header) with
a range syntax of:
date:<since>..<until>
See **DATE AND TIME SEARCH** below for details on the range expression,
and supported syntax for <since> and <until> date and time expressions.
The time range can also be specified using timestamps with a syntax of:
<initial-timestamp>..<final-timestamp>
Each timestamp is a number representing the number of seconds since
1970-01-01 00:00:00 UTC.
In addition to individual terms, multiple terms can be combined with
Boolean operators ( **and**, **or**, **not** , etc.). Each term in the
query will be implicitly connected by a logical AND if no explicit
operator is provided, (except that terms with a common prefix will be
implicitly combined with OR until we get Xapian defect #402 fixed).
Parentheses can also be used to control the combination of the Boolean
operators, but will have to be protected from interpretation by the
shell, (such as by putting quotation marks around any parenthesized
expression).
DATE AND TIME SEARCH
====================
notmuch understands a variety of standard and natural ways of expressing
dates and times, both in absolute terms ("2012-10-24") and in relative
terms ("yesterday"). Any number of relative terms can be combined ("1
hour 25 minutes") and an absolute date/time can be combined with
relative terms to further adjust it. A non-exhaustive description of the
syntax supported for absolute and relative terms is given below.
The range expression
--------------------
date:<since>..<until>
The above expression restricts the results to only messages from <since>
to <until>, based on the Date: header.
<since> and <until> can describe imprecise times, such as "yesterday".
In this case, <since> is taken as the earliest time it could describe
(the beginning of yesterday) and <until> is taken as the latest time it
could describe (the end of yesterday). Similarly, date:january..february
matches from the beginning of January to the end of February.
Currently, we do not support spaces in range expressions. You can
replace the spaces with '\_', or (in most cases) '-', or (in some cases)
leave the spaces out altogether. Examples in this man page use spaces
for clarity.
Open-ended ranges are supported (since Xapian 1.2.1), i.e. it's possible
to specify date:..<until> or date:<since>.. to not limit the start or
end time, respectively. Pre-1.2.1 Xapian does not report an error on
open ended ranges, but it does not work as expected either.
Entering date:expr without ".." (for example date:yesterday) won't work,
as it's not interpreted as a range expression at all. You can achieve
the expected result by duplicating the expr both sides of ".." (for
example date:yesterday..yesterday).
Relative date and time
----------------------
[N\|number]
(years\|months\|weeks\|days\|hours\|hrs\|minutes\|mins\|seconds\|secs)
[...]
All refer to past, can be repeated and will be accumulated.
Units can be abbreviated to any length, with the otherwise ambiguous
single m being m for minutes and M for months.
Number can also be written out one, two, ..., ten, dozen, hundred.
Additionally, the unit may be preceded by "last" or "this" (e.g., "last
week" or "this month").
When combined with absolute date and time, the relative date and time
specification will be relative from the specified absolute date and
time.
Examples: 5M2d, two weeks
Supported absolute time formats
-------------------------------
- H[H]:MM[:SS] [(am\|a.m.\|pm\|p.m.)]
- H[H] (am\|a.m.\|pm\|p.m.)
- HHMMSS
- now
- noon
- midnight
- Examples: 17:05, 5pm
Supported absolute date formats
-------------------------------
- YYYY-MM[-DD]
- DD-MM[-[YY]YY]
- MM-YYYY
- M[M]/D[D][/[YY]YY]
- M[M]/YYYY
- D[D].M[M][.[YY]YY]
- D[D][(st\|nd\|rd\|th)] Mon[thname] [YYYY]
- Mon[thname] D[D][(st\|nd\|rd\|th)] [YYYY]
- Wee[kday]
Month names can be abbreviated at three or more characters.
Weekday names can be abbreviated at three or more characters.
Examples: 2012-07-31, 31-07-2012, 7/31/2012, August 3
Time zones
----------
- (+\|-)HH:MM
- (+\|-)HH[MM]
Some time zone codes, e.g. UTC, EET.
SEE ALSO
========
**notmuch(1)**, **notmuch-config(1)**, **notmuch-count(1)**,
**notmuch-dump(1)**, **notmuch-hooks(5)**, **notmuch-insert(1)**,
**notmuch-new(1)**, **notmuch-reply(1)**, **notmuch-restore(1)**,
**notmuch-search(1)**, **notmuch-show(1)**, **notmuch-tag(1)**

18
doc/mkdocdeps.py Normal file
View file

@ -0,0 +1,18 @@
import sys
srcdir = sys.argv[1]
builddir = sys.argv[2]
outfile = sys.argv[3]
sys.path.insert(0, srcdir)
import conf
roff_files = []
rst_files = []
for page in conf.man_pages:
rst_files = rst_files + ["{0:s}/{1:s}.rst".format(srcdir,page[0])]
roff_files = roff_files + ["{0:s}/man/{1:s}.{2:d}".format(builddir,page[0],page[4])]
with open(outfile, 'w') as out:
out.write('MAN_ROFF_FILES := ' + ' \\\n\t'.join(roff_files) + '\n')
out.write('MAN_RST_FILES := ' + ' \\\n\t'.join(rst_files) + '\n')

204
doc/notmuch-emacs.rst Normal file
View file

@ -0,0 +1,204 @@
=============
notmuch-emacs
=============
About this Manual
=================
This manual covers only the Emacs interface to Notmuch. For information
on the command line interface, see See section “Description” in Notmuch
Manual Pager. To save typing, we will sometimes use *notmuch* in this
manual to refer to the Emacs interface to Notmuch. If the distinction
should every be important, well refer to the Emacs interface as
*notmuch-emacs*.
Notmuch-emacs is highly customizable via the the Emacs customization
framework (or just by setting the appropriate variables). We try to
point out relevant variables in this manual, but in order to avoid
duplication of information, but you can usually find the most detailed
description in the variables docstring.
notmuch-hello
=============
.. index::
single: notmuch-hello
single: notmuch
``notmuch-hello`` is the main entry point for Notmuch. You can start it
with ``M-x notmuch`` or ``M-x notmuch-hello``. The startup screen looks
something like the following. There are some hints at the bottom of the
screen. There are three main parts to the notmuch-hello screen,
discussed below. The **bold** text indicates buttons you can click with
a mouse or by positioning the cursor and pressing ``<return>``
| Welcome to **notmuch** You have 52 messages.
|
| Saved searches: **[edit]**
|
| 52 **inbox** 52 **unread**
|
| Search: ____________________________________
|
| All tags: **[show]**
|
| 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.
| ``=`` to refresh this screen. ``s`` to search messages. ``q`` to quit.
| **Customize** this page.
You can change the overall appearance of the notmuch-hello screen by
customizing the variable :index:`notmuch-hello-sections`.
notmuch-hello key bindings
--------------------------
``<tab>``
Move to the next widget (button or text entry field)
``<backspace>``
Move to the previous widget.
``<return>``
Activate the current widget.
``=``
Refresh the buffer; mainly update the counts of messages for various
saved searches.
``G``
Import mail, See :ref:`importing`
``m``
Compose a message
``s``
Search the notmuch database using :ref:`notmuch-search`
``v``
Print notmuch version
``q``
Quit
.. _saved-searches:
Saved Searches
--------------
Notmuch replaces the static assignment of messages with the more dynamic
notion of searching. Notmuch-hello presents the user with a customizable
set of saved searches. The initial defaults are ``tag:inbox`` and
``tag:unread``, but you can customize the following variables
:index:`notmuch-saved-searches`
A list of cons pairs, the first being the name to display, the
second being a query string for Notmuch. See section “Description”
in Notmuch Query Syntax.
:index:`notmuch-saved-searches-sort-function`
This variable controls how saved searches should be sorted. A value
of ``nil`` displays the saved searches in the order they are stored
in notmuch-saved-searches.
:index:`notmuch-column-control`
Controls the number of columns for displaying saved-searches/tags
Search Box
----------
The search box lets the user enter a Notmuch query. See section
“Description” in Notmuch Query Syntax, for more info on Notmuch query
syntax. A history of recent searches is also displayed by default. The
latter is controlled by the variable :index:`notmuch-hello-recent-searches-max`.
Known Tags
----------
One special kind of saved search provided by default is for each
individual tag defined in the database. This can be controlled via the
following variables.
:index:`notmuch-hello-tag-list-make-query`
Control how to construct a search (“virtual folder”) from a given
tag.
:index:`notmuch-hello-hide-tags`
Which tags not to display at all.
:index:`notmuch-column-control`
Controls the number of columns for displaying saved-searches/tags
.. _notmuch-search:
notmuch-search
==============
``notmuch-search-mode`` is used to display the results from executing
a query via ``notmuch-search``. The syntax for these queries is the
the same as :ref:`saved-searches`. For details of this syntax see
info:notmuch-search-terms
By default the output approximates that of the command line See section
“Description” in notmuch search command.
The main purpose of the ``notmuch-search-mode`` buffer is to act as a
menu of results that the user can explore further by pressing
``<return>`` on the appropriate line.
``n,C-n,<down>``
Move to next line
``p,C-p,<up>``
Move to previous line
``<return>``
Open thread on current line in :ref:`notmuch-show` mode
``?``
Display full set of key bindings
The presentation of results can be controlled by the following
variables.
:index:`notmuch-search-result-format`
Control how each thread of messages is presented in the
``notmuch-show-mode`` buffer
:index:`notmuch-search-oldest-first`
Display the oldest threads at the top of the buffer
.. _notmuch-show:
notmuch-show
============
notmuch-tree
============
Configuration
=============
.. _importing:
Importing Mail
--------------
:index:`notmuch-poll`
:index:`notmuch-poll-script`
Init File
---------
When Notmuch is loaded, it will read the ``notmuch-init-file``
(``~/.emacs.d/notmuch-config`` by default) file. This is normal Emacs Lisp
file and can be used to avoid cluttering your ``~/.emacs`` with Notmuch
stuff. If the file with ``.elc``, ``.elc.gz``, ``.el`` or ``.el.gz``
suffix exist it will be read instead (just one of these, chosen in this
order). Most often users create ``~/.emacs.d/notmuch-config.el`` and just
work with it. If Emacs was invoked with the ``-q`` or ``--no-init-file``
options, ``notmuch-init-file`` is not read.

63
doc/prerst2man.py Normal file
View file

@ -0,0 +1,63 @@
from sys import argv
from datetime import date
from os.path import dirname, isdir
from os import makedirs, system
import re
sourcedir = argv[1]
outdir = argv[2]
if not isdir(outdir):
makedirs(outdir, 0o755)
execfile(sourcedir + "/conf.py")
def header(file, startdocname, command, description, authors, section):
file.write("""
{0:s}
{1:s}
{2:s}
:Date: {3:s}
:Version: {4:s}
:Manual section: {5:d}
:Manual group: {6:s}
""".format(
'-' * len(description),
description,
'-' * len(description),
date.today().isoformat(), release, section, project))
blankre = re.compile("^\s*$")
for page in man_pages:
outdirname = outdir + '/' + dirname(page[0])
if not isdir(outdirname):
makedirs(outdirname, 0o755)
filename = outdir + '/' + page[0] + '.rst'
outfile = open(filename, 'w')
infile = open(sourcedir + '/' + page[0] + '.rst', 'r')
# this is a crude hack. We look for the first blank line, and
# insert the rst2man header there.
#
# XXX consider really parsing input
count = 0
lines = infile.readlines()
for line in lines:
outfile.write(line)
if (blankre.match(line)):
break
count = count + 1
del lines[0:count + 1]
header(outfile, *page)
outfile.write("".join(lines))
outfile.close()
system('set -x; rst2man {0} {1}/{2}.{3}'
.format(filename, outdir, page[0], page[4]))

View file

@ -1,13 +0,0 @@
#ifndef DUMP_RESTORE_PRIVATE_H
#define DUMP_RESTORE_PRIVATE_H
#include "hex-escape.h"
#include "command-line-arguments.h"
typedef enum dump_formats {
DUMP_FORMAT_AUTO,
DUMP_FORMAT_BATCH_TAG,
DUMP_FORMAT_SUP
} dump_format_t;
#endif

1
emacs/.gitignore vendored
View file

@ -1,2 +1,3 @@
.eldeps*
*.elc
notmuch-version.el

View file

@ -17,7 +17,14 @@ emacs_sources := \
$(dir)/notmuch-crypto.el \
$(dir)/notmuch-tag.el \
$(dir)/coolj.el \
$(dir)/notmuch-print.el
$(dir)/notmuch-print.el \
$(dir)/notmuch-version.el
$(dir)/notmuch-version.el: $(dir)/Makefile.local version.stamp
$(dir)/notmuch-version.el: $(srcdir)/$(dir)/notmuch-version.el.tmpl
@sed -e 's/%AG%/Generated file (from $(<F)) -- do not edit!/' \
-e 's/%VERSION%/"$(VERSION)"/' $< > $@
emacs_images := \
$(srcdir)/$(dir)/notmuch-logo.png
@ -29,26 +36,40 @@ emacs_bytecode = $(emacs_sources:.el=.elc)
# the byte compiler may load an old .elc file when processing a
# "require" or we may fail to rebuild a .elc that depended on a macro
# from an updated file.
ifeq ($(HAVE_EMACS),1)
$(dir)/.eldeps: $(dir)/Makefile.local $(dir)/make-deps.el $(emacs_sources)
$(call quiet,EMACS) --directory emacs -batch -l make-deps.el \
-f batch-make-deps $(emacs_sources) > $@.tmp && \
(cmp -s $@.tmp $@ || mv $@.tmp $@)
-include $(dir)/.eldeps
CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp
mv $@.tmp $@
# We could include .eldeps directly, but that would cause a make
# restart whenever any .el file was modified, even if dependencies
# didn't change, because the mtime of .eldeps will change. Instead,
# we include a second file, .eldeps.x, which we ensure always has the
# same content as .eldeps, but its mtime only changes when dependency
# information changes, in which case a make restart is necessary
# anyway.
$(dir)/.eldeps.x: $(dir)/.eldeps
@cmp -s $^ $@ || cp $^ $@
-include $(dir)/.eldeps.x
endif
CLEAN+=$(dir)/.eldeps $(dir)/.eldeps.tmp $(dir)/.eldeps.x
ifeq ($(HAVE_EMACS),1)
%.elc: %.el $(global_deps)
$(call quiet,EMACS) --directory emacs -batch -f batch-byte-compile $<
endif
ifeq ($(WITH_EMACS),1)
ifeq ($(HAVE_EMACS),1)
all: $(emacs_bytecode)
install-emacs: $(emacs_bytecode)
endif
install: install-emacs
endif
.PHONY: install-emacs
install-emacs:
install-emacs: $(emacs_sources) $(emacs_images)
mkdir -p "$(DESTDIR)$(emacslispdir)"
install -m0644 $(emacs_sources) "$(DESTDIR)$(emacslispdir)"
ifeq ($(HAVE_EMACS),1)
@ -57,4 +78,4 @@ endif
mkdir -p "$(DESTDIR)$(emacsetcdir)"
install -m0644 $(emacs_images) "$(DESTDIR)$(emacsetcdir)"
CLEAN := $(CLEAN) $(emacs_bytecode)
CLEAN := $(CLEAN) $(emacs_bytecode) $(dir)/notmuch-version.el

View file

@ -29,6 +29,96 @@
(declare-function notmuch-search "notmuch" (&optional query oldest-first target-thread target-line continuation))
(declare-function notmuch-poll "notmuch" ())
(defun notmuch-saved-search-get (saved-search field)
"Get FIELD from SAVED-SEARCH.
If SAVED-SEARCH is a plist, this is just `plist-get', but for
backwards compatibility, this also deals with the two other
possible formats for SAVED-SEARCH: cons cells (NAME . QUERY) and
lists (NAME QUERY COUNT-QUERY)."
(cond
((keywordp (car saved-search))
(plist-get saved-search field))
;; It is not a plist so it is an old-style entry.
((consp (cdr saved-search)) ;; It is a list (NAME QUERY COUNT-QUERY)
(case field
(:name (first saved-search))
(:query (second saved-search))
(:count-query (third saved-search))
(t nil)))
(t ;; It is a cons-cell (NAME . QUERY)
(case field
(:name (car saved-search))
(:query (cdr saved-search))
(t nil)))))
(defun notmuch-hello-saved-search-to-plist (saved-search)
"Return a copy of SAVED-SEARCH in plist form.
If saved search is a plist then just return a copy. In other
cases, for backwards compatibility, convert to plist form and
return that."
(if (keywordp (car saved-search))
(copy-seq saved-search)
(let ((fields (list :name :query :count-query))
plist-search)
(dolist (field fields plist-search)
(let ((string (notmuch-saved-search-get saved-search field)))
(when string
(setq plist-search (append plist-search (list field string)))))))))
(defun notmuch-hello--saved-searches-to-plist (symbol)
"Extract a saved-search variable into plist form.
The new style saved search is just a plist, but for backwards
compatibility we use this function to extract old style saved
searches so they still work in customize."
(let ((saved-searches (default-value symbol)))
(mapcar #'notmuch-hello-saved-search-to-plist saved-searches)))
(define-widget 'notmuch-saved-search-plist 'list
"A single saved search property list."
:tag "Saved Search"
:args '((list :inline t
:format "%v"
(group :format "%v" :inline t (const :format " Name: " :name) (string :format "%v"))
(group :format "%v" :inline t (const :format " Query: " :query) (string :format "%v")))
(checklist :inline t
:format "%v"
(group :format "%v" :inline t (const :format "Count-Query: " :count-query) (string :format "%v"))
(group :format "%v" :inline t (const :format "" :sort-order)
(choice :tag " Sort Order"
(const :tag "Default" nil)
(const :tag "Oldest-first" oldest-first)
(const :tag "Newest-first" newest-first))))))
(defcustom notmuch-saved-searches '((:name "inbox" :query "tag:inbox")
(:name "unread" :query "tag:unread"))
"A list of saved searches to display.
The saved search can be given in 3 forms. The preferred way is as
a plist. Supported properties are
:name Name of the search (required).
:query Search to run (required).
:count-query Optional extra query to generate the count
shown. If not present then the :query property
is used.
:sort-order Specify the sort order to be used for the search.
Possible values are 'oldest-first 'newest-first or
nil. Nil means use the default sort order.
Other accepted forms are a cons cell of the form (NAME . QUERY)
or a list of the form (NAME QUERY COUNT-QUERY)."
;; The saved-search format is also used by the all-tags notmuch-hello
;; section. This section generates its own saved-search list in one of
;; the latter two forms.
:get 'notmuch-hello--saved-searches-to-plist
:type '(repeat notmuch-saved-search-plist)
:tag "List of Saved Searches"
:group 'notmuch-hello)
(defcustom notmuch-hello-recent-searches-max 10
"The number of recent searches to display."
:type 'integer
@ -39,9 +129,12 @@
:type 'boolean
:group 'notmuch-hello)
(defun notmuch-sort-saved-searches (alist)
"Generate an alphabetically sorted saved searches alist."
(sort (copy-sequence alist) (lambda (a b) (string< (car a) (car b)))))
(defun notmuch-sort-saved-searches (saved-searches)
"Generate an alphabetically sorted saved searches list."
(sort (copy-sequence saved-searches)
(lambda (a b)
(string< (notmuch-saved-search-get a :name)
(notmuch-saved-search-get b :name)))))
(defcustom notmuch-saved-search-sort-function nil
"Function used to sort the saved searches for the notmuch-hello view.
@ -51,8 +144,10 @@ sorting (nil) displays the saved searches in the order they are
stored in `notmuch-saved-searches'. Sort alphabetically sorts the
saved searches in alphabetical order. Custom sort function should
be a function or a lambda expression that takes the saved
searches alist as a parameter, and returns a new saved searches
alist to be used."
searches list as a parameter, and returns a new saved searches
list to be used. For compatibility with the various saved-search
formats it should use notmuch-saved-search-get to access the
fields of the search."
:type '(choice (const :tag "No sorting" nil)
(const :tag "Sort alphabetically" notmuch-sort-saved-searches)
(function :tag "Custom sort function"
@ -280,12 +375,12 @@ afterwards.")
(setq notmuch-saved-searches
(loop for elem in notmuch-saved-searches
if (not (equal name
(car elem)))
(notmuch-saved-search-get elem :name)))
collect elem))
;; Add the new one.
(customize-save-variable 'notmuch-saved-searches
(add-to-list 'notmuch-saved-searches
(cons name search) t))
(list :name name :query search) t))
(message "Saved '%s' as '%s'." search name)
(notmuch-hello-update)))
@ -300,7 +395,7 @@ afterwards.")
(defun notmuch-hello-longest-label (searches-alist)
(or (loop for elem in searches-alist
maximize (length (car elem)))
maximize (length (notmuch-saved-search-get elem :name)))
0))
(defun notmuch-hello-reflect-generate-row (ncols nrows row list)
@ -325,7 +420,8 @@ diagonal."
(defun notmuch-hello-widget-search (widget &rest ignore)
(notmuch-search (widget-get widget
:notmuch-search-terms)
notmuch-search-oldest-first))
(widget-get widget
:notmuch-search-oldest-first)))
(defun notmuch-saved-search-count (search)
(car (process-lines notmuch-command "count" search)))
@ -379,29 +475,30 @@ Otherwise, FILTER is ignored.
(concat "(" query ") and (" filter ")"))
(t query)))
(defun notmuch-hello-query-counts (query-alist &rest options)
"Compute list of counts of matched messages from QUERY-ALIST.
(defun notmuch-hello-query-counts (query-list &rest options)
"Compute list of counts of matched messages from QUERY-LIST.
QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
or (NAME QUERY COUNT-QUERY). If the latter form is used,
COUNT-QUERY specifies an alternate query to be used to generate
the count for the associated query.
QUERY-LIST must be a list of saved-searches. Ideally each of
these is a plist but other options are available for backwards
compatibility: see `notmuch-saved-searches' for details.
The result is the list of elements of the form (NAME QUERY COUNT).
The result is a list of plists each of which includes the
properties :name NAME, :query QUERY and :count COUNT, together
with any properties in the original saved-search.
The values :show-empty-searches, :filter and :filter-count from
options will be handled as specified for
`notmuch-hello-insert-searches'."
(with-temp-buffer
(dolist (elem query-alist nil)
(let ((count-query (if (consp (cdr elem))
;; do we have a different query for the message count?
(third elem)
(cdr elem))))
(dolist (elem query-list nil)
(let ((count-query (or (notmuch-saved-search-get elem :count-query)
(notmuch-saved-search-get elem :query))))
(insert
(replace-regexp-in-string
"\n" " "
(notmuch-hello-filtered-query count-query
(or (plist-get options :filter-count)
(plist-get options :filter)))
(plist-get options :filter))))
"\n")))
(unless (= (call-process-region (point-min) (point-max) notmuch-command
@ -417,26 +514,26 @@ the CLI and emacs interface."))
#'identity
(mapcar
(lambda (elem)
(let ((name (car elem))
(search-query (if (consp (cdr elem))
;; do we have a different query for the message count?
(second elem)
(cdr elem)))
(let* ((elem-plist (notmuch-hello-saved-search-to-plist elem))
(search-query (plist-get elem-plist :query))
(filtered-query (notmuch-hello-filtered-query
search-query (plist-get options :filter)))
(message-count (prog1 (read (current-buffer))
(forward-line 1))))
(and (or (plist-get options :show-empty-searches) (> message-count 0))
(list name (notmuch-hello-filtered-query
search-query (plist-get options :filter))
message-count))))
query-alist))))
(when (and filtered-query (or (plist-get options :show-empty-searches) (> message-count 0)))
(setq elem-plist (plist-put elem-plist :query filtered-query))
(plist-put elem-plist :count message-count))))
query-list))))
(defun notmuch-hello-insert-buttons (searches)
"Insert buttons for SEARCHES.
SEARCHES must be a list containing lists of the form (NAME QUERY COUNT), where
QUERY is the query to start when the button for the corresponding entry is
activated. COUNT should be the number of messages matching the query.
Such a list can be computed with `notmuch-hello-query-counts'."
SEARCHES must be a list of plists each of which should contain at
least the properties :name NAME :query QUERY and :count COUNT,
where QUERY is the query to start when the button for the
corresponding entry is activated, and COUNT should be the number
of messages matching the query. Such a plist can be computed
with `notmuch-hello-query-counts'."
(let* ((widest (notmuch-hello-longest-label searches))
(tags-and-width (notmuch-hello-tags-per-line widest))
(tags-per-line (car tags-and-width))
@ -454,14 +551,19 @@ Such a list can be computed with `notmuch-hello-query-counts'."
(when elem
(if (> column-indent 0)
(widget-insert (make-string column-indent ? )))
(let* ((name (first elem))
(query (second elem))
(msg-count (third elem)))
(let* ((name (plist-get elem :name))
(query (plist-get elem :query))
(oldest-first (case (plist-get elem :sort-order)
(newest-first nil)
(oldest-first t)
(otherwise notmuch-search-oldest-first)))
(msg-count (plist-get elem :count)))
(widget-insert (format "%8s "
(notmuch-hello-nice-number msg-count)))
(widget-create 'push-button
:notify #'notmuch-hello-widget-search
:notmuch-search-terms query
:notmuch-search-oldest-first oldest-first
name)
(setq column-indent
(1+ (max 0 (- column-width (length name)))))))
@ -513,6 +615,18 @@ Such a list can be computed with `notmuch-hello-query-counts'."
(remove-hook 'window-configuration-change-hook
#'notmuch-hello-window-configuration-change))))
;; the following variable is defined as being defconst in notmuch-version.el
(defvar notmuch-emacs-version)
(defun notmuch-hello-versions ()
"Display the notmuch version(s)"
(interactive)
(let ((notmuch-cli-version (notmuch-version)))
(message "notmuch version %s"
(if (string= notmuch-emacs-version notmuch-cli-version)
notmuch-cli-version
(concat notmuch-cli-version
" (emacs mua version " notmuch-emacs-version ")")))))
(defvar notmuch-hello-mode-map
(let ((map (if (fboundp 'make-composed-keymap)
@ -523,8 +637,7 @@ Such a list can be computed with `notmuch-hello-query-counts'."
;; it's unlikely to change.
(copy-keymap widget-keymap))))
(set-keymap-parent map notmuch-common-keymap)
(define-key map "v" (lambda () "Display the notmuch version" (interactive)
(message "notmuch version %s" (notmuch-version))))
(define-key map "v" 'notmuch-hello-versions)
(define-key map (kbd "<C-tab>") 'widget-backward)
map)
"Keymap for \"notmuch hello\" buffers.")
@ -687,13 +800,15 @@ Complete list of currently available key bindings:
(indent-rigidly start (point) notmuch-hello-indent))
nil))
(defun notmuch-hello-insert-searches (title query-alist &rest options)
"Insert a section with TITLE showing a list of buttons made from QUERY-ALIST.
(defun notmuch-hello-insert-searches (title query-list &rest options)
"Insert a section with TITLE showing a list of buttons made from QUERY-LIST.
QUERY-ALIST must be a list containing elements of the form (NAME . QUERY)
or (NAME QUERY COUNT-QUERY). If the latter form is used,
COUNT-QUERY specifies an alternate query to be used to generate
the count for the associated item.
QUERY-LIST should ideally be a plist but for backwards
compatibility other forms are also accepted (see
`notmuch-saved-searches' for details). The plist should
contain keys :name and :query; if :count-query is also present
then it specifies an alternate query to be used to generate the
count for the associated search.
Supports the following entries in OPTIONS as a plist:
:initially-hidden - if non-nil, section will be hidden on startup
@ -727,7 +842,7 @@ Supports the following entries in OPTIONS as a plist:
"hide"))
(widget-insert "\n")
(when (not is-hidden)
(let ((searches (apply 'notmuch-hello-query-counts query-alist options)))
(let ((searches (apply 'notmuch-hello-query-counts query-list options)))
(when (or (not (plist-get options :hide-if-empty))
searches)
(widget-insert "\n")
@ -788,6 +903,7 @@ following:
"Run notmuch and display saved searches, known tags, etc."
(interactive)
(notmuch-assert-cli-sane)
;; This may cause a window configuration change, so if the
;; auto-refresh hook is already installed, avoid recursive refresh.
(let ((notmuch-hello-auto-refresh nil))

View file

@ -107,12 +107,6 @@ Note that the recommended way of achieving the same is using
(defvar notmuch-search-history nil
"Variable to store notmuch searches history.")
(defcustom notmuch-saved-searches '(("inbox" . "tag:inbox")
("unread" . "tag:unread"))
"A list of saved searches to display."
:type '(alist :key-type string :value-type string)
:group 'notmuch-hello)
(defcustom notmuch-archive-tags '("-inbox")
"List of tag changes to apply to a message or a thread when it is archived.
@ -168,6 +162,24 @@ Otherwise the output will be returned"
(notmuch-check-exit-status status (cons notmuch-command args) output)
output)))
(defvar notmuch--cli-sane-p nil
"Cache whether the CLI seems to be configured sanely.")
(defun notmuch-cli-sane-p ()
"Return t if the cli seems to be configured sanely."
(unless notmuch--cli-sane-p
(let ((status (call-process notmuch-command nil nil nil
"config" "get" "user.primary_email")))
(setq notmuch--cli-sane-p (= status 0))))
notmuch--cli-sane-p)
(defun notmuch-assert-cli-sane ()
(unless (notmuch-cli-sane-p)
(notmuch-logged-error
"notmuch cli seems misconfigured or unconfigured."
"Perhaps you haven't run \"notmuch setup\" yet? Try running this
on the command line, and then retry your notmuch command")))
(defun notmuch-version ()
"Return a string with the notmuch version number."
(let ((long-string
@ -180,8 +192,13 @@ Otherwise the output will be returned"
(defun notmuch-config-get (item)
"Return a value from the notmuch configuration."
;; Trim off the trailing newline
(substring (notmuch-command-to-string "config" "get" item) 0 -1))
(let* ((val (notmuch-command-to-string "config" "get" item))
(len (length val)))
;; Trim off the trailing newline (if the value is empty or not
;; configured, there will be no newline)
(if (and (> len 0) (= (aref val (- len 1)) ?\n))
(substring val 0 -1)
val)))
(defun notmuch-database-path ()
"Return the database.path value from the notmuch configuration."
@ -197,7 +214,7 @@ Otherwise the output will be returned"
(defun notmuch-user-other-email ()
"Return the user.other_email value (as a list) from the notmuch configuration."
(split-string (notmuch-config-get "user.other_email") "\n"))
(split-string (notmuch-config-get "user.other_email") "\n" t))
(defun notmuch-poll ()
"Run \"notmuch new\" or an external script to import mail.
@ -231,7 +248,8 @@ depending on the value of `notmuch-poll-script'."
"Given a prefix key code, return a human-readable string representation.
This is basically just `format-kbd-macro' but we also convert ESC to M-."
(let ((desc (format-kbd-macro (vector key))))
(let* ((key-vector (if (vectorp key) key (vector key)))
(desc (format-kbd-macro key-vector)))
(if (string= desc "ESC")
"M-"
(concat desc " "))))
@ -337,6 +355,28 @@ of its command symbol."
(set-buffer-modified-p nil)
(view-buffer (current-buffer) 'kill-buffer-if-not-modified))))
(defun notmuch-subkeymap-help ()
"Show help for a subkeymap."
(interactive)
(let* ((key (this-command-keys-vector))
(prefix (make-vector (1- (length key)) nil))
(i 0))
(while (< i (length prefix))
(aset prefix i (aref key i))
(setq i (1+ i)))
(let* ((subkeymap (key-binding prefix))
(ua-keys (where-is-internal 'universal-argument nil t))
(prefix-string (notmuch-prefix-key-description prefix))
(desc-alist (notmuch-describe-keymap subkeymap ua-keys subkeymap prefix-string))
(desc-list (mapcar (lambda (arg) (concat (car arg) "\t" (cdr arg))) desc-alist))
(desc (mapconcat #'identity desc-list "\n")))
(with-help-window (help-buffer)
(with-current-buffer standard-output
(insert "\nPress 'q' to quit this window.\n\n")
(insert desc)))
(pop-to-buffer (help-buffer)))))
(defvar notmuch-buffer-refresh-function nil
"Function to call to refresh the current buffer.")
(make-variable-buffer-local 'notmuch-buffer-refresh-function)
@ -380,7 +420,10 @@ user-friendly queries."
(save-match-data
(if (or (equal term "")
(string-match "[ ()]\\|^\"" term))
;; To be pessimistic, only pass through terms composed
;; entirely of ASCII printing characters other than ", (,
;; and ).
(string-match "[^!#-'*-~]" term))
;; Requires escaping
(concat "\"" (replace-regexp-in-string "\"" "\"\"" term t t) "\"")
term)))
@ -490,7 +533,8 @@ the given type."
(if (>= emacs-major-version 24)
(defadvice mm-shr (before load-gnus-arts activate)
(require 'gnus-art nil t)
(ad-disable-advice 'mm-shr 'before 'load-gnus-arts)))
(ad-disable-advice 'mm-shr 'before 'load-gnus-arts)
(ad-activate 'mm-shr)))
(defun notmuch-mm-display-part-inline (msg part nth content-type process-crypto)
"Use the mm-decode/mm-view functions to display a part in the
@ -531,23 +575,32 @@ single element face list."
face
(list face)))
(defun notmuch-combine-face-text-property (start end face &optional below object)
"Combine FACE into the 'face text property between START and END.
(defun notmuch-apply-face (object face &optional below start end)
"Combine FACE into the 'face text property of OBJECT between START and END.
This function combines FACE with any existing faces between START
and END in OBJECT (which defaults to the current buffer).
Attributes specified by FACE take precedence over existing
attributes unless BELOW is non-nil. FACE must be a face name (a
symbol or string), a property list of face attributes, or a list
of these. For convenience when applied to strings, this returns
OBJECT."
and END in OBJECT. Attributes specified by FACE take precedence
over existing attributes unless BELOW is non-nil.
OBJECT may be a string, a buffer, or nil (which means the current
buffer). If object is a string, START and END are 0-based;
otherwise they are buffer positions (integers or markers). FACE
must be a face name (a symbol or string), a property list of face
attributes, or a list of these. If START and/or END are omitted,
they default to the beginning/end of OBJECT. For convenience
when applied to strings, this returns OBJECT."
;; A face property can have three forms: a face name (a string or
;; symbol), a property list, or a list of these two forms. In the
;; list case, the faces will be combined, with the earlier faces
;; taking precedent. Here we canonicalize everything to list form
;; to make it easy to combine.
(let ((pos start)
(let ((pos (cond (start start)
((stringp object) 0)
(t 1)))
(end (cond (end end)
((stringp object) (length object))
(t (1+ (buffer-size object)))))
(face-list (notmuch-face-ensure-list-form face)))
(while (< pos end)
(let* ((cur (get-text-property pos 'face object))
@ -560,14 +613,6 @@ OBJECT."
(setq pos next))))
object)
(defun notmuch-combine-face-text-property-string (string face &optional below)
(notmuch-combine-face-text-property
0
(length string)
face
below
string))
(defun notmuch-map-text-property (start end prop func &optional object)
"Transform text property PROP using FUNC.

View file

@ -115,6 +115,14 @@ list."
(push header message-hidden-headers)))
notmuch-mua-hidden-headers))
(defun notmuch-mua-reply-crypto (parts)
"Add mml sign-encrypt flag if any part of original message is encrypted."
(loop for part in parts
if (notmuch-match-content-type (plist-get part :content-type) "multipart/encrypted")
do (mml-secure-message-sign-encrypt)
else if (notmuch-match-content-type (plist-get part :content-type) "multipart/*")
do (notmuch-mua-reply-crypto (plist-get part :content))))
(defun notmuch-mua-get-quotable-parts (parts)
(loop for part in parts
if (notmuch-match-content-type (plist-get part :content-type) "multipart/alternative")
@ -151,9 +159,10 @@ list."
(defun notmuch-mua-reply (query-string &optional sender reply-all)
(let ((args '("reply" "--format=sexp" "--format-version=1"))
(process-crypto notmuch-show-process-crypto)
reply
original)
(when notmuch-show-process-crypto
(when process-crypto
(setq args (append args '("--decrypt"))))
(if reply-all
@ -224,28 +233,21 @@ list."
(set-mark (point))
(goto-char start)
;; Quote the original message according to the user's configured style.
(message-cite-original))))
(message-cite-original)))
(goto-char (point-max))
;; Crypto processing based crypto content of the original message
(when process-crypto
(notmuch-mua-reply-crypto (plist-get original :body))))
;; Push mark right before signature, if any.
(message-goto-signature)
(unless (eobp)
(end-of-line -1))
(push-mark)
(message-goto-body)
(set-buffer-modified-p nil))
(defun notmuch-mua-forward-message ()
(funcall (notmuch-mua-get-switch-function) (current-buffer))
(message-forward)
(when notmuch-mua-user-agent-function
(let ((user-agent (funcall notmuch-mua-user-agent-function)))
(when (not (string= "" user-agent))
(message-add-header (format "User-Agent: %s" user-agent)))))
(message-sort-headers)
(message-hide-headers)
(set-buffer-modified-p nil)
(notmuch-mua-maybe-set-window-dedicated)
(message-goto-to))
(defun notmuch-mua-mail (&optional to subject other-headers &rest other-args)
"Invoke the notmuch mail composition window.
@ -287,31 +289,33 @@ the From: header is already filled in by notmuch."
(defvar notmuch-mua-sender-history nil)
;; Workaround: Running `ido-completing-read' in emacs 23.1, 23.2 and 23.3
;; without some explicit initialization fill freeze the operation.
;; Hence, we advice `ido-completing-read' to ensure required initialization
;; is done.
(if (and (= emacs-major-version 23) (< emacs-minor-version 4))
(defadvice ido-completing-read (before notmuch-ido-mode-init activate)
(ido-init-completion-maps)
(add-hook 'minibuffer-setup-hook 'ido-minibuffer-setup)
(add-hook 'choose-completion-string-functions
'ido-choose-completion-string)
(ad-disable-advice 'ido-completing-read 'before 'notmuch-ido-mode-init)
(ad-activate 'ido-completing-read)))
(defun notmuch-mua-prompt-for-sender ()
(interactive)
(let (name addresses one-name-only)
;; If notmuch-identities is non-nil, check if there is a fixed user name.
"Prompt for a sender from the user's configured identities."
(if notmuch-identities
(let ((components (mapcar 'mail-extract-address-components notmuch-identities)))
(setq name (caar components)
addresses (mapcar 'cadr components)
one-name-only (eval
(cons 'and
(mapcar (lambda (identity)
(string-equal name (car identity)))
components)))))
;; If notmuch-identities is nil, use values from the notmuch configuration file.
(setq name (notmuch-user-name)
addresses (cons (notmuch-user-primary-email) (notmuch-user-other-email))
one-name-only t))
;; Now prompt the user, either for an email address only or for a full identity.
(if one-name-only
(let ((address
(ido-completing-read (concat "Sender address for " name ": ") addresses
nil nil nil 'notmuch-mua-sender-history (car addresses))))
(concat name " <" address ">"))
(ido-completing-read "Send mail From: " notmuch-identities
nil nil nil 'notmuch-mua-sender-history (car notmuch-identities)))))
(ido-completing-read "Send mail from: " notmuch-identities
nil nil nil 'notmuch-mua-sender-history
(car notmuch-identities))
(let* ((name (notmuch-user-name))
(addrs (cons (notmuch-user-primary-email)
(notmuch-user-other-email)))
(address
(ido-completing-read (concat "Sender address for " name ": ") addrs
nil nil nil 'notmuch-mua-sender-history
(car addrs))))
(concat name " <" address ">"))))
(put 'notmuch-mua-new-mail 'notmuch-prefix-doc "... and prompt for sender")
(defun notmuch-mua-new-mail (&optional prompt-for-sender)
@ -332,13 +336,17 @@ The current buffer must contain an RFC2822 message to forward.
If PROMPT-FOR-SENDER is non-nil, the user will be prompted for
the From: address first."
(if (or prompt-for-sender notmuch-always-prompt-for-sender)
(let* ((sender (notmuch-mua-prompt-for-sender))
(address-components (mail-extract-address-components sender))
(user-full-name (car address-components))
(user-mail-address (cadr address-components)))
(notmuch-mua-forward-message))
(notmuch-mua-forward-message)))
(let* ((cur (current-buffer))
(message-forward-decoded-p nil)
(subject (message-make-forward-subject))
(other-headers
(when (or prompt-for-sender notmuch-always-prompt-for-sender)
(list (cons 'From (notmuch-mua-prompt-for-sender))))))
(notmuch-mua-mail nil subject other-headers nil (notmuch-mua-get-switch-function))
(message-forward-make-body cur)
;; `message-forward-make-body' shows the User-agent header. Hide
;; it again.
(message-hide-headers)))
(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all)
"Compose a reply to the message identified by QUERY-STRING.

View file

@ -171,7 +171,7 @@ each attachment handler is logged in buffers with names beginning
(defcustom notmuch-show-stash-mlarchive-link-alist
'(("Gmane" . "http://mid.gmane.org/")
("MARC" . "http://marc.info/?i=")
("Mail Archive, The" . "http://mail-archive.com/search?l=mid&q=")
("Mail Archive, The" . "http://mid.mail-archive.com/")
("LKML" . "http://lkml.kernel.org/r/")
;; FIXME: can these services be searched by `Message-Id' ?
;; ("MarkMail" . "http://markmail.org/")
@ -344,7 +344,7 @@ operation on the contents of the current buffer."
(if (re-search-forward "(\\([^()]*\\))$" (line-end-position) t)
(let ((inhibit-read-only t))
(replace-match (concat "("
(notmuch-tag-format-tags tags)
(notmuch-tag-format-tags tags (notmuch-show-get-prop :orig-tags))
")"))))))
(defun notmuch-clean-address (address)
@ -423,7 +423,7 @@ message at DEPTH in the current thread."
" ("
date
") ("
(notmuch-tag-format-tags tags)
(notmuch-tag-format-tags tags tags)
")\n")
(overlay-put (make-overlay start (point)) 'face 'notmuch-message-summary-face)))
@ -785,7 +785,10 @@ message at DEPTH in the current thread."
(while (and handlers
(not (condition-case err
(funcall (car handlers) msg part content-type nth depth button)
(error (progn
;; Specifying `debug' here lets the debugger
;; run if `debug-on-error' is non-nil.
((debug error)
(progn
(insert "!!! Bodypart insert error: ")
(insert (error-message-string err))
(insert " !!!\n") nil)))))
@ -1145,6 +1148,7 @@ function is used."
;; Don't track undo information for this buffer
(set 'buffer-undo-list t)
(notmuch-tag-clear-cache)
(erase-buffer)
(goto-char (point-min))
(save-excursion
@ -1167,6 +1171,8 @@ function is used."
(jit-lock-register #'notmuch-show-buttonise-links)
(notmuch-show-mapc (lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
;; Set the header line to the subject of the first message.
(setq header-line-format (notmuch-sanitize (notmuch-show-strip-re (notmuch-show-get-subject))))
@ -1241,6 +1247,7 @@ reset based on the original query."
(define-key map "t" 'notmuch-show-stash-to)
(define-key map "l" 'notmuch-show-stash-mlarchive-link)
(define-key map "L" 'notmuch-show-stash-mlarchive-link-and-go)
(define-key map "?" 'notmuch-subkeymap-help)
map)
"Submap for stash commands")
(fset 'notmuch-show-stash-map notmuch-show-stash-map)
@ -1251,6 +1258,7 @@ reset based on the original query."
(define-key map "v" 'notmuch-show-view-part)
(define-key map "o" 'notmuch-show-interactively-view-part)
(define-key map "|" 'notmuch-show-pipe-part)
(define-key map "?" 'notmuch-subkeymap-help)
map)
"Submap for part commands")
(fset 'notmuch-show-part-map notmuch-show-part-map)
@ -1779,10 +1787,14 @@ message."
(setq shell-command
(concat notmuch-command " show --format=raw "
(shell-quote-argument (notmuch-show-get-message-id)) " | " command)))
(let ((buf (get-buffer-create (concat "*notmuch-pipe*"))))
(let ((cwd default-directory)
(buf (get-buffer-create (concat "*notmuch-pipe*"))))
(with-current-buffer buf
(setq buffer-read-only nil)
(erase-buffer)
;; Use the originating buffer's working directory instead of
;; that of the pipe buffer.
(cd cwd)
(let ((exit-code (call-process-shell-command shell-command nil buf)))
(goto-char (point-max))
(set-buffer-modified-p nil)

View file

@ -28,35 +28,9 @@
(require 'crm)
(require 'notmuch-lib)
(defcustom notmuch-tag-formats
'(("unread" (propertize tag 'face '(:foreground "red")))
("flagged" (propertize tag 'face '(:foreground "blue"))
(notmuch-tag-format-image-data tag (notmuch-tag-star-icon))))
"Custom formats for individual tags.
This gives a list that maps from tag names to lists of formatting
expressions. The car of each element gives a tag name and the
cdr gives a list of Elisp expressions that modify the tag. If
the list is empty, the tag will simply be hidden. Otherwise,
each expression will be evaluated in order: for the first
expression, the variable `tag' will be bound to the tag name; for
each later expression, the variable `tag' will be bound to the
result of the previous expression. In this way, each expression
can build on the formatting performed by the previous expression.
The result of the last expression will displayed in place of the
tag.
For example, to replace a tag with another string, simply use
that string as a formatting expression. To change the foreground
of a tag to red, use the expression
(propertize tag 'face '(:foreground \"red\"))
See also `notmuch-tag-format-image', which can help replace tags
with images."
:group 'notmuch-search
:group 'notmuch-show
:type '(alist :key-type (string :tag "Tag")
(define-widget 'notmuch-tag-format-type 'lazy
"Customize widget for notmuch-tag-format and friends"
:type '(alist :key-type (regexp :tag "Tag")
:extra-offset -3
:value-type
(radio :format "%v"
@ -65,7 +39,7 @@ with images."
(string :tag "Display as")
(list :tag "Face" :extra-offset -4
(const :format "" :inline t
(propertize tag 'face))
(notmuch-apply-face tag))
(list :format "%v"
(const :format "" quote)
custom-face-edit))
@ -82,6 +56,83 @@ with images."
(string :tag "Custom")))
(sexp :tag "Custom")))))
(defcustom notmuch-tag-formats
'(("unread" (propertize tag 'face '(:foreground "red")))
("flagged" (propertize tag 'face '(:foreground "blue"))
(notmuch-tag-format-image-data tag (notmuch-tag-star-icon))))
"Custom formats for individual tags.
This is an association list that maps from tag name regexps to
lists of formatting expressions. The first entry whose car
regexp-matches a tag will be used to format that tag. The regexp
is implicitly anchored, so to match a literal tag name, just use
that tag name (if it contains special regexp characters like
\".\" or \"*\", these have to be escaped). The cdr of the
matching entry gives a list of Elisp expressions that modify the
tag. If the list is empty, the tag will simply be hidden.
Otherwise, each expression will be evaluated in order: for the
first expression, the variable `tag' will be bound to the tag
name; for each later expression, the variable `tag' will be bound
to the result of the previous expression. In this way, each
expression can build on the formatting performed by the previous
expression. The result of the last expression will displayed in
place of the tag.
For example, to replace a tag with another string, simply use
that string as a formatting expression. To change the foreground
of a tag to red, use the expression
(propertize tag 'face '(:foreground \"red\"))
See also `notmuch-tag-format-image', which can help replace tags
with images."
:group 'notmuch-search
:group 'notmuch-show
:group 'notmuch-faces
:type 'notmuch-tag-format-type)
(defcustom notmuch-tag-deleted-formats
'(("unread" (notmuch-apply-face bare-tag
(if (display-supports-face-attributes-p '(:strike-through "red"))
'(:strike-through "red")
'(:inverse-video t))))
(".*" (notmuch-apply-face tag
(if (display-supports-face-attributes-p '(:strike-through "red"))
'(:strike-through "red")
'(:inverse-video t)))))
"Custom formats for tags when deleted.
For deleted tags the formats in `notmuch-tag-formats` are applied
first and then these formats are applied on top; that is `tag'
passed to the function is the tag with all these previous
formattings applied. The formatted can access the original
unformatted tag as `bare-tag'.
By default this shows deleted tags with strike-through in red,
unless strike-through is not available (e.g., emacs is running in
a terminal) in which case it uses inverse video. To hide deleted
tags completely set this to
'((\".*\" nil))
See `notmuch-tag-formats' for full documentation."
:group 'notmuch-show
:group 'notmuch-faces
:type 'notmuch-tag-format-type)
(defcustom notmuch-tag-added-formats
'((".*" (notmuch-apply-face tag '(:underline "green"))))
"Custom formats for tags when added.
For added tags the formats in `notmuch-tag-formats` are applied
first and then these formats are applied on top.
To disable special formatting of added tags, set this variable to
nil.
See `notmuch-tag-formats' for full documentation."
:group 'notmuch-show
:group 'notmuch-faces
:type 'notmuch-tag-format-type)
(defun notmuch-tag-format-image-data (tag data)
"Replace TAG with image DATA, if available.
@ -135,28 +186,81 @@ This can be used with `notmuch-tag-format-image-data'."
</g>
</svg>")
(defun notmuch-tag-format-tag (tag)
"Format TAG by looking into `notmuch-tag-formats'."
(let ((formats (assoc tag notmuch-tag-formats)))
(cond
((null formats) ;; - Tag not in `notmuch-tag-formats',
tag) ;; the format is the tag itself.
(defvar notmuch-tag--format-cache (make-hash-table :test 'equal)
"Cache of tag format lookup. Internal to `notmuch-tag-format-tag'.")
(defun notmuch-tag-clear-cache ()
"Clear the internal cache of tag formats."
(clrhash notmuch-tag--format-cache))
(defun notmuch-tag--get-formats (tag format-alist)
"Find the first item whose car regexp-matches TAG."
(save-match-data
;; Don't use assoc-default since there's no way to distinguish a
;; missing key from a present key with a null cdr.
(assoc* tag format-alist
:test (lambda (tag key)
(and (eq (string-match key tag) 0)
(= (match-end 0) (length tag)))))))
(defun notmuch-tag--do-format (tag formatted-tag formats)
"Apply a tag-formats entry to TAG."
(cond ((null formats) ;; - Tag not in `formats',
formatted-tag) ;; the format is the tag itself.
((null (cdr formats)) ;; - Tag was deliberately hidden,
nil) ;; no format must be returned
(t ;; - Tag was found and has formats,
(let ((tag tag)) ;; we must apply all the formats.
(dolist (format (cdr formats) tag)
(setq tag (eval format))))))))
(t
;; Tag was found and has formats, we must apply all the
;; formats. TAG may be null so treat that as a special case.
(let ((bare-tag tag)
(tag (copy-sequence (or formatted-tag ""))))
(dolist (format (cdr formats))
(setq tag (eval format)))
(if (and (null formatted-tag) (equal tag ""))
nil
tag)))))
(defun notmuch-tag-format-tags (tags)
(defun notmuch-tag-format-tag (tags orig-tags tag)
"Format TAG according to `notmuch-tag-formats'.
TAGS and ORIG-TAGS are lists of the current tags and the original
tags; tags which have been deleted (i.e., are in ORIG-TAGS but
are not in TAGS) are shown using formats from
`notmuch-tag-deleted-formats'; tags which have been added (i.e.,
are in TAGS but are not in ORIG-TAGS) are shown using formats
from `notmuch-tag-added-formats' and tags which have not been
changed (the normal case) are shown using formats from
`notmuch-tag-formats'"
(let* ((tag-state (cond ((not (member tag tags)) 'deleted)
((not (member tag orig-tags)) 'added)))
(formatted-tag (gethash (cons tag tag-state) notmuch-tag--format-cache 'missing)))
(when (eq formatted-tag 'missing)
(let ((base (notmuch-tag--get-formats tag notmuch-tag-formats))
(over (case tag-state
(deleted (notmuch-tag--get-formats
tag notmuch-tag-deleted-formats))
(added (notmuch-tag--get-formats
tag notmuch-tag-added-formats))
(otherwise nil))))
(setq formatted-tag (notmuch-tag--do-format tag tag base))
(setq formatted-tag (notmuch-tag--do-format tag formatted-tag over))
(puthash (cons tag tag-state) formatted-tag notmuch-tag--format-cache)))
formatted-tag))
(defun notmuch-tag-format-tags (tags orig-tags &optional face)
"Return a string representing formatted TAGS."
(notmuch-combine-face-text-property-string
(let ((face (or face 'notmuch-tag-face))
(all-tags (sort (delete-dups (append tags orig-tags nil)) #'string<)))
(notmuch-apply-face
(mapconcat #'identity
;; nil indicated that the tag was deliberately hidden
(delq nil (mapcar #'notmuch-tag-format-tag tags))
(delq nil (mapcar
(apply-partially #'notmuch-tag-format-tag tags orig-tags)
all-tags))
" ")
'notmuch-tag-face
t))
face
t)))
(defcustom notmuch-before-tag-hook nil
"Hooks that are run before tags of a message are modified.
@ -283,6 +387,8 @@ notmuch-after-tag-hook will be run."
(unless (string-match-p "^[-+]\\S-+$" tag-change)
(error "Tag must be of the form `+this_tag' or `-that_tag'")))
tag-changes)
(unless query
(error "Nothing to tag!"))
(unless (null tag-changes)
(run-hooks 'notmuch-before-tag-hook)
(if (<= (length query) notmuch-tag-argument-limit)

View file

@ -70,8 +70,14 @@ Note the author string should not contain
:group 'notmuch-tree)
;; Faces for messages that match the query.
(defface notmuch-tree-match-date-face
(defface notmuch-tree-match-face
'((t :inherit default))
"Default face used in tree mode face for matching messages"
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-match-date-face
nil
"Face used in tree mode for the date in messages matching the query."
:group 'notmuch-tree
:group 'notmuch-faces)
@ -90,13 +96,13 @@ Note the author string should not contain
:group 'notmuch-faces)
(defface notmuch-tree-match-subject-face
'((t :inherit default))
nil
"Face used in tree mode for the subject in messages matching the query."
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-match-tree-face
'((t :inherit default))
nil
"Face used in tree mode for the thread tree block graphics in messages matching the query."
:group 'notmuch-tree
:group 'notmuch-faces)
@ -115,32 +121,38 @@ Note the author string should not contain
:group 'notmuch-faces)
;; Faces for messages that do not match the query.
(defface notmuch-tree-no-match-date-face
(defface notmuch-tree-no-match-face
'((t (:foreground "gray")))
"Default face used in tree mode face for non-matching messages"
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-no-match-date-face
nil
"Face used in tree mode for non-matching dates."
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-no-match-subject-face
'((t (:foreground "gray")))
nil
"Face used in tree mode for non-matching subjects."
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-no-match-tree-face
'((t (:foreground "gray")))
nil
"Face used in tree mode for the thread tree block graphics in messages matching the query."
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-no-match-author-face
'((t (:foreground "gray")))
nil
"Face used in tree mode for the date in messages matching the query."
:group 'notmuch-tree
:group 'notmuch-faces)
(defface notmuch-tree-no-match-tag-face
'((t (:foreground "gray")))
nil
"Face used in tree mode face for non-matching tags."
:group 'notmuch-tree
:group 'notmuch-faces)
@ -319,11 +331,13 @@ correct message properties."
"Return the tags of the current message."
(notmuch-tree-get-prop :tags))
(defun notmuch-tree-get-message-id ()
(defun notmuch-tree-get-message-id (&optional bare)
"Return the message id of the current message."
(let ((id (notmuch-tree-get-prop :id)))
(if id
(notmuch-id-to-query id)
(if bare
id
(notmuch-id-to-query id))
nil)))
(defun notmuch-tree-get-match ()
@ -687,20 +701,22 @@ unchanged ADDRESS if parsing fails."
((string-equal field "tags")
(let ((tags (plist-get msg :tags))
(orig-tags (plist-get msg :orig-tags))
(face (if match
'notmuch-tree-match-tag-face
'notmuch-tree-no-match-tag-face)))
(propertize (format format-string
(mapconcat #'identity tags ", "))
'face face))))))
(format format-string (notmuch-tag-format-tags tags orig-tags face)))))))
(defun notmuch-tree-format-field-list (field-list msg)
"Format fields of MSG according to FIELD-LIST and return string"
(let (result-string)
(let ((face (if (plist-get msg :match)
'notmuch-tree-match-face
'notmuch-tree-no-match-face))
(result-string))
(dolist (spec field-list result-string)
(let ((field-string (notmuch-tree-format-field (car spec) (cdr spec) msg)))
(setq result-string (concat result-string field-string))))))
(setq result-string (concat result-string field-string))))
(notmuch-apply-face result-string face t)))
(defun notmuch-tree-insert-msg (msg)
"Insert the message MSG according to notmuch-tree-result-format"
@ -751,8 +767,10 @@ message together with all its descendents."
(push "" tree-status)))
(push (concat (if replies "" "") "") tree-status)
(plist-put msg :first (and first (eq 0 depth)))
(notmuch-tree-goto-and-insert-msg (plist-put msg :tree-status tree-status))
(setq msg (plist-put msg :first (and first (eq 0 depth))))
(setq msg (plist-put msg :tree-status tree-status))
(setq msg (plist-put msg :orig-tags (plist-get msg :tags)))
(notmuch-tree-goto-and-insert-msg msg)
(pop tree-status)
(pop tree-status)
@ -866,6 +884,7 @@ the same as for the function notmuch-tree."
(message-arg "--entire-thread"))
(if (equal (car (process-lines notmuch-command "count" search-args)) "0")
(setq search-args basic-query))
(notmuch-tag-clear-cache)
(let ((proc (notmuch-start-notmuch
"notmuch-tree" (current-buffer) #'notmuch-tree-process-sentinel
"show" "--body=false" "--format=sexp"

View file

@ -0,0 +1,23 @@
;; -*- emacs-lisp -*-
;;
;; %AG%
;;
;; This file is part of Notmuch.
;;
;; Notmuch 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.
;;
;; Notmuch 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 Notmuch. If not, see <http://www.gnu.org/licenses/>.
(defconst notmuch-emacs-version %VERSION%
"Version of Notmuch Emacs MUA.")
(provide 'notmuch-version)

View file

@ -36,7 +36,7 @@
;;
;; Then, to actually run it, add:
;;
;; (require 'notmuch)
;; (autoload 'notmuch "notmuch" "Notmuch mail" t)
;;
;; to your ~/.emacs file, and then run "M-x notmuch" from within emacs,
;; or run:
@ -61,6 +61,10 @@
(require 'notmuch-message)
(require 'notmuch-parser)
(unless (require 'notmuch-version nil t)
(defconst notmuch-emacs-version "unknown"
"Placeholder variable when notmuch-version.el[c] is not available."))
(defcustom notmuch-search-result-format
`(("date" . "%12s ")
("count" . "%-7s ")
@ -81,6 +85,18 @@ To enter a line break in customize, press \\[quoted-insert] C-j."
:type '(alist :key-type (string) :value-type (string))
:group 'notmuch-search)
;; The name of this variable `notmuch-init-file' is consistent with the
;; convention used in e.g. emacs and gnus. The value, `notmuch-config[.el[c]]'
;; is consistent with notmuch cli configuration file `~/.notmuch-config'.
(defcustom notmuch-init-file (locate-user-emacs-file "notmuch-config")
"Your Notmuch Emacs-Lisp configuration file name.
If a file with one of the suffixes defined by `get-load-suffixes' exists,
it will be read instead.
This file is read once when notmuch is loaded; the notmuch hooks added
there will be called at other points of notmuch execution."
:type 'file
:group 'notmuch)
(defvar notmuch-query-history nil
"Variable to store minibuffer history for notmuch queries")
@ -165,6 +181,7 @@ To enter a line break in customize, press \\[quoted-insert] C-j."
(defvar notmuch-search-stash-map
(let ((map (make-sparse-keymap)))
(define-key map "i" 'notmuch-search-stash-thread-id)
(define-key map "?" 'notmuch-subkeymap-help)
map)
"Submap for stash commands")
(fset 'notmuch-search-stash-map notmuch-search-stash-map)
@ -411,14 +428,16 @@ matched and unmatched messages in the current thread."
"Return the stable query for the current region.
If ONLY-MATCHED is non-nil, include only matched messages. If it
is nil, include both matched and unmatched messages."
is nil, include both matched and unmatched messages. If there are
no messages in the region then return nil."
(let ((query-list nil) (all (not only-matched)))
(dolist (queries (notmuch-search-properties-in-region :query beg end))
(when (first queries)
(push (first queries) query-list))
(when (and all (second queries))
(push (second queries) query-list)))
(concat "(" (mapconcat 'identity query-list ") or (") ")")))
(when query-list
(concat "(" (mapconcat 'identity query-list ") or (") ")"))))
(defun notmuch-search-find-authors ()
"Return the authors for the current thread"
@ -648,7 +667,7 @@ foreground and blue background."
(let ((tag (car elem))
(attributes (cdr elem)))
(when (member tag line-tag-list)
(notmuch-combine-face-text-property start end attributes))))
(notmuch-apply-face nil attributes nil start end))))
;; Reverse the list so earlier entries take precedence
(reverse notmuch-search-line-faces)))
@ -752,24 +771,33 @@ non-authors is found, assume that all of the authors match."
format-string (notmuch-sanitize (plist-get result :authors))))
((string-equal field "tags")
(let ((tags (plist-get result :tags)))
(insert (format format-string (notmuch-tag-format-tags tags)))))))
(let ((tags (plist-get result :tags))
(orig-tags (plist-get result :orig-tags)))
(insert (format format-string (notmuch-tag-format-tags tags orig-tags)))))))
(defun notmuch-search-show-result (result &optional pos)
"Insert RESULT at POS or the end of the buffer if POS is null."
(defun notmuch-search-show-result (result pos)
"Insert RESULT at POS."
;; Ignore excluded matches
(unless (= (plist-get result :matched) 0)
(let ((beg (or pos (point-max))))
(save-excursion
(goto-char beg)
(goto-char pos)
(dolist (spec notmuch-search-result-format)
(notmuch-search-insert-field (car spec) (cdr spec) result))
(insert "\n")
(notmuch-search-color-line beg (point) (plist-get result :tags))
(put-text-property beg (point) 'notmuch-search-result result))
(notmuch-search-color-line pos (point) (plist-get result :tags))
(put-text-property pos (point) 'notmuch-search-result result))))
(defun notmuch-search-append-result (result)
"Insert RESULT at the end of the buffer.
This is only called when a result is first inserted so it also
sets the :orig-tag property."
(let ((new-result (plist-put result :orig-tags (plist-get result :tags)))
(pos (point-max)))
(notmuch-search-show-result new-result pos)
(when (string= (plist-get result :thread) notmuch-search-target-thread)
(setq notmuch-search-target-thread "found")
(goto-char beg)))))
(goto-char pos))))
(defun notmuch-search-process-filter (proc string)
"Process and filter the output of \"notmuch search\""
@ -783,7 +811,7 @@ non-authors is found, assume that all of the authors match."
(save-excursion
(goto-char (point-max))
(insert string))
(notmuch-sexp-parse-partial-list 'notmuch-search-show-result
(notmuch-sexp-parse-partial-list 'notmuch-search-append-result
results-buf)))))
(defun notmuch-search-tag-all (tag-changes)
@ -801,14 +829,14 @@ See `notmuch-tag' for information on the format of TAG-CHANGES."
(let (longest
(longest-length 0))
(loop for tuple in notmuch-saved-searches
if (let ((quoted-query (regexp-quote (cdr tuple))))
if (let ((quoted-query (regexp-quote (notmuch-saved-search-get tuple :query))))
(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)))
(saved-search-name (notmuch-saved-search-get saved-search :name))
(saved-search-query (notmuch-saved-search-get saved-search :query)))
(cond ((and saved-search (equal saved-search-query query))
;; Query is the same as saved search (ignoring case)
(concat "*notmuch-saved-search-" saved-search-name "*"))
@ -828,7 +856,7 @@ See `notmuch-tag' for information on the format of TAG-CHANGES."
PROMPT is the string to prompt with."
(lexical-let
((completions
(append (list "folder:" "thread:" "id:" "date:" "from:" "to:"
(append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:"
"subject:" "attachment:")
(mapcar (lambda (tag)
(concat "tag:" (notmuch-escape-boolean-term tag)))
@ -887,6 +915,7 @@ the configured default sort order."
(set 'notmuch-search-oldest-first oldest-first)
(set 'notmuch-search-target-thread target-thread)
(set 'notmuch-search-target-line target-line)
(notmuch-tag-clear-cache)
(let ((proc (get-buffer-process (current-buffer)))
(inhibit-read-only t))
(if proc
@ -1002,3 +1031,9 @@ notmuch buffers exist, run `notmuch'."
(setq mail-user-agent 'notmuch-user-agent)
(provide 'notmuch)
;; After provide to avoid loops if notmuch was require'd via notmuch-init-file.
(if init-file-user ; don't load init file if the -q option was used.
(let ((init-file (locate-file notmuch-init-file '("/")
(get-load-suffixes))))
(if init-file (load init-file nil t t))))

View file

@ -50,6 +50,9 @@ notmuch_run_hook (const char *db_path, const char *hook)
goto DONE;
}
/* Flush any buffered output before forking. */
fflush (stdout);
pid = fork();
if (pid == -1) {
fprintf (stderr, "Error: %s hook fork failed: %s\n", hook,

View file

@ -42,7 +42,7 @@ typedef struct {
const char *prefix;
} prefix_t;
#define NOTMUCH_DATABASE_VERSION 1
#define NOTMUCH_DATABASE_VERSION 2
#define STRINGIFY(s) _SUB_STRINGIFY(s)
#define _SUB_STRINGIFY(s) #s
@ -100,8 +100,8 @@ typedef struct {
* In addition, terms from the content of the message are added with
* "from", "to", "attachment", and "subject" prefixes for use by the
* user in searching. Similarly, terms from the path of the mail
* message are added with a "folder" prefix. But the database doesn't
* really care itself about any of these.
* message are added with "folder" and "path" prefixes. But the
* database doesn't really care itself about any of these.
*
* The data portion of a mail document is empty.
*
@ -208,7 +208,15 @@ static prefix_t BOOLEAN_PREFIX_EXTERNAL[] = {
{ "thread", "G" },
{ "tag", "K" },
{ "is", "K" },
{ "id", "Q" }
{ "id", "Q" },
{ "path", "P" },
/*
* Without the ":", since this is a multi-letter prefix, Xapian
* will add a colon itself if the first letter of the path is
* upper-case ASCII. Including the ":" forces there to always be a
* colon, which keeps our own logic simpler.
*/
{ "folder", "XFOLDER:" },
};
static prefix_t PROBABILISTIC_PREFIX[]= {
@ -216,7 +224,6 @@ static prefix_t PROBABILISTIC_PREFIX[]= {
{ "to", "XTO" },
{ "attachment", "XATTACHMENT" },
{ "subject", "XSUBJECT"},
{ "folder", "XFOLDER"}
};
const char *
@ -1167,6 +1174,40 @@ notmuch_database_upgrade (notmuch_database_t *notmuch,
}
}
/*
* Prior to version 2, the "folder:" prefix was probabilistic and
* stemmed. Change it to the current boolean prefix. Add "path:"
* prefixes while at it.
*/
if (version < 2) {
notmuch_query_t *query = notmuch_query_create (notmuch, "");
notmuch_messages_t *messages;
notmuch_message_t *message;
count = 0;
total = notmuch_query_count_messages (query);
for (messages = notmuch_query_search_messages (query);
notmuch_messages_valid (messages);
notmuch_messages_move_to_next (messages)) {
if (do_progress_notify) {
progress_notify (closure, (double) count / total);
do_progress_notify = 0;
}
message = notmuch_messages_get (messages);
_notmuch_message_upgrade_folder (message);
_notmuch_message_sync (message);
notmuch_message_destroy (message);
count++;
}
notmuch_query_destroy (query);
}
db->set_metadata ("version", STRINGIFY (NOTMUCH_DATABASE_VERSION));
db->flush ();
@ -1930,15 +1971,10 @@ notmuch_database_add_message (notmuch_database_t *notmuch,
if (ret)
goto DONE;
notmuch_message_file_restrict_headers (message_file,
"date",
"from",
"in-reply-to",
"message-id",
"references",
"subject",
"to",
(char *) NULL);
/* Parse message up front to get better error status. */
ret = _notmuch_message_file_parse (message_file);
if (ret)
goto DONE;
try {
/* Before we do any real work, (especially before doing a
@ -2025,7 +2061,7 @@ notmuch_database_add_message (notmuch_database_t *notmuch,
date = notmuch_message_file_get_header (message_file, "date");
_notmuch_message_set_header_values (message, date, from, subject);
ret = _notmuch_message_index_file (message, filename);
ret = _notmuch_message_index_file (message, message_file);
if (ret)
goto DONE;
} else {

View file

@ -23,6 +23,6 @@ while read sym; do
;;
esac
done
nm $* | awk '$1 ~ "^[0-9a-fA-F][0-9a-fA-F]*$" && $2 == "T" && $3 ~ "^get(line|delim)$" {print $3 ";"}'
nm $* | awk '$1 ~ "^[0-9a-fA-F][0-9a-fA-F]*$" && $2 == "T" && $3 ~ "^(getline|getdelim|canonicalize_file_name)$" {print $3 ";"}'
sed -n 's/^[[:space:]]*\(notmuch_[a-z_]*\)[[:space:]]*(.*/ \1;/p' $HEADER
printf "local: *;\n};\n"

View file

@ -231,26 +231,22 @@ _index_address_mailbox (notmuch_message_t *message,
InternetAddress *address)
{
InternetAddressMailbox *mailbox = INTERNET_ADDRESS_MAILBOX (address);
const char *name, *addr;
const char *name, *addr, *combined;
void *local = talloc_new (message);
name = internet_address_get_name (address);
addr = internet_address_mailbox_get_addr (mailbox);
/* In the absence of a name, we'll strip the part before the @
* from the address. */
if (! name) {
const char *at;
/* Combine the name and address and index them as a phrase. */
if (name && addr)
combined = talloc_asprintf (local, "%s %s", name, addr);
else if (name)
combined = name;
else
combined = addr;
at = strchr (addr, '@');
if (at)
name = talloc_strndup (local, addr, at - addr);
}
if (name)
_notmuch_message_gen_terms (message, prefix_name, name);
if (addr)
_notmuch_message_gen_terms (message, prefix_name, addr);
if (combined)
_notmuch_message_gen_terms (message, prefix_name, combined);
talloc_free (local);
}
@ -425,63 +421,17 @@ _index_mime_part (notmuch_message_t *message,
notmuch_status_t
_notmuch_message_index_file (notmuch_message_t *message,
const char *filename)
notmuch_message_file_t *message_file)
{
GMimeStream *stream = NULL;
GMimeParser *parser = NULL;
GMimeMessage *mime_message = NULL;
GMimeMessage *mime_message;
InternetAddressList *addresses;
FILE *file = NULL;
const char *from, *subject;
notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
static int initialized = 0;
char from_buf[5];
bool is_mbox = false;
static bool mbox_warning = false;
notmuch_status_t status;
if (! initialized) {
g_mime_init (GMIME_ENABLE_RFC2047_WORKAROUNDS);
initialized = 1;
}
file = fopen (filename, "r");
if (! file) {
fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
ret = NOTMUCH_STATUS_FILE_ERROR;
goto DONE;
}
/* Is this mbox? */
if (fread (from_buf, sizeof (from_buf), 1, file) == 1 &&
strncmp (from_buf, "From ", 5) == 0)
is_mbox = true;
rewind (file);
/* Evil GMime steals my FILE* here so I won't fclose it. */
stream = g_mime_stream_file_new (file);
parser = g_mime_parser_new_with_stream (stream);
g_mime_parser_set_scan_from (parser, is_mbox);
mime_message = g_mime_parser_construct_message (parser);
if (is_mbox) {
if (!g_mime_parser_eos (parser)) {
/* This is a multi-message mbox. */
ret = NOTMUCH_STATUS_FILE_NOT_EMAIL;
goto DONE;
}
/* For historical reasons, we support single-message mboxes,
* but this behavior is likely to change in the future, so
* warn. */
if (!mbox_warning) {
mbox_warning = true;
fprintf (stderr, "\
Warning: %s is an mbox containing a single message,\n\
likely caused by misconfigured mail delivery. Support for single-message\n\
mboxes is deprecated and may be removed in the future.\n", filename);
}
}
status = _notmuch_message_file_get_mime_message (message_file,
&mime_message);
if (status)
return status;
from = g_mime_message_get_sender (mime_message);
@ -502,15 +452,5 @@ mboxes is deprecated and may be removed in the future.\n", filename);
_index_mime_part (message, g_mime_message_get_mime_part (mime_message));
DONE:
if (mime_message)
g_object_unref (mime_message);
if (parser)
g_object_unref (parser);
if (stream)
g_object_unref (stream);
return ret;
return NOTMUCH_STATUS_SUCCESS;
}

View file

@ -26,30 +26,15 @@
#include <glib.h> /* GHashTable */
typedef struct {
char *str;
size_t size;
size_t len;
} header_value_closure_t;
struct _notmuch_message_file {
/* File object */
FILE *file;
char *filename;
/* Header storage */
int restrict_headers;
/* Cache for decoded headers */
GHashTable *headers;
int broken_headers;
int good_headers;
size_t header_size; /* Length of full message header in bytes. */
/* Parsing state */
char *line;
size_t line_size;
header_value_closure_t value;
int parsing_started;
int parsing_finished;
GMimeMessage *message;
};
static int
@ -76,15 +61,12 @@ strcase_hash (const void *ptr)
static int
_notmuch_message_file_destructor (notmuch_message_file_t *message)
{
if (message->line)
free (message->line);
if (message->value.size)
free (message->value.str);
if (message->headers)
g_hash_table_destroy (message->headers);
if (message->message)
g_object_unref (message->message);
if (message->file)
fclose (message->file);
@ -102,20 +84,17 @@ _notmuch_message_file_open_ctx (void *ctx, const char *filename)
if (unlikely (message == NULL))
return NULL;
/* Only needed for error messages during parsing. */
message->filename = talloc_strdup (message, filename);
if (message->filename == NULL)
goto FAIL;
talloc_set_destructor (message, _notmuch_message_file_destructor);
message->file = fopen (filename, "r");
if (message->file == NULL)
goto FAIL;
message->headers = g_hash_table_new_full (strcase_hash,
strcase_equal,
free,
g_free);
message->parsing_started = 0;
message->parsing_finished = 0;
return message;
FAIL:
@ -137,264 +116,222 @@ notmuch_message_file_close (notmuch_message_file_t *message)
talloc_free (message);
}
void
notmuch_message_file_restrict_headersv (notmuch_message_file_t *message,
va_list va_headers)
static notmuch_bool_t
_is_mbox (FILE *file)
{
char *header;
char from_buf[5];
notmuch_bool_t ret = FALSE;
if (message->parsing_started)
INTERNAL_ERROR ("notmuch_message_file_restrict_headers called after parsing has started");
/* Is this mbox? */
if (fread (from_buf, sizeof (from_buf), 1, file) == 1 &&
strncmp (from_buf, "From ", 5) == 0)
ret = TRUE;
while (1) {
header = va_arg (va_headers, char*);
if (header == NULL)
break;
g_hash_table_insert (message->headers,
xstrdup (header), NULL);
}
rewind (file);
message->restrict_headers = 1;
return ret;
}
void
notmuch_message_file_restrict_headers (notmuch_message_file_t *message, ...)
notmuch_status_t
_notmuch_message_file_parse (notmuch_message_file_t *message)
{
va_list va_headers;
va_start (va_headers, message);
notmuch_message_file_restrict_headersv (message, va_headers);
}
static void
copy_header_unfolding (header_value_closure_t *value,
const char *chunk)
{
char *last;
if (chunk == NULL)
return;
while (*chunk == ' ' || *chunk == '\t')
chunk++;
if (value->len + 1 + strlen (chunk) + 1 > value->size) {
unsigned int new_size = value->size;
if (value->size == 0)
new_size = strlen (chunk) + 1;
else
while (value->len + 1 + strlen (chunk) + 1 > new_size)
new_size *= 2;
value->str = xrealloc (value->str, new_size);
value->size = new_size;
}
last = value->str + value->len;
if (value->len) {
*last = ' ';
last++;
value->len++;
}
strcpy (last, chunk);
value->len += strlen (chunk);
last = value->str + value->len - 1;
if (*last == '\n') {
*last = '\0';
value->len--;
}
}
/* As a special-case, a value of NULL for header_desired will force
* the entire header to be parsed if it is not parsed already. This is
* used by the _notmuch_message_file_get_headers_end function.
* Another special case is the Received: header. For this header we
* want to concatenate all instances of the header instead of just
* hashing the first instance as we use this when analyzing the path
* the mail has taken from sender to recipient.
*/
const char *
notmuch_message_file_get_header (notmuch_message_file_t *message,
const char *header_desired)
{
int contains;
char *header, *decoded_value, *header_sofar, *combined_header;
const char *s, *colon;
int match, newhdr, hdrsofar, is_received;
GMimeStream *stream;
GMimeParser *parser;
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
static int initialized = 0;
notmuch_bool_t is_mbox;
is_received = (strcmp(header_desired,"received") == 0);
if (message->message)
return NOTMUCH_STATUS_SUCCESS;
is_mbox = _is_mbox (message->file);
if (! initialized) {
g_mime_init (GMIME_ENABLE_RFC2047_WORKAROUNDS);
initialized = 1;
}
message->parsing_started = 1;
message->headers = g_hash_table_new_full (strcase_hash, strcase_equal,
free, g_free);
if (! message->headers)
return NOTMUCH_STATUS_OUT_OF_MEMORY;
if (header_desired == NULL)
contains = 0;
else
contains = g_hash_table_lookup_extended (message->headers,
header_desired, NULL,
(gpointer *) &decoded_value);
stream = g_mime_stream_file_new (message->file);
if (contains && decoded_value)
return decoded_value;
/* We'll own and fclose the FILE* ourselves. */
g_mime_stream_file_set_owner (GMIME_STREAM_FILE (stream), FALSE);
if (message->parsing_finished)
return "";
parser = g_mime_parser_new_with_stream (stream);
g_mime_parser_set_scan_from (parser, is_mbox);
#define NEXT_HEADER_LINE(closure) \
while (1) { \
ssize_t bytes_read = getline (&message->line, \
&message->line_size, \
message->file); \
if (bytes_read == -1) { \
message->parsing_finished = 1; \
break; \
} \
if (*message->line == '\n') { \
message->parsing_finished = 1; \
break; \
} \
if (closure && \
(*message->line == ' ' || *message->line == '\t')) \
{ \
copy_header_unfolding ((closure), message->line); \
} \
if (*message->line == ' ' || *message->line == '\t') \
message->header_size += strlen (message->line); \
else \
break; \
message->message = g_mime_parser_construct_message (parser);
if (! message->message) {
status = NOTMUCH_STATUS_FILE_NOT_EMAIL;
goto DONE;
}
if (message->line == NULL)
NEXT_HEADER_LINE (NULL);
while (1) {
if (message->parsing_finished)
break;
colon = strchr (message->line, ':');
if (colon == NULL) {
message->broken_headers++;
/* A simple heuristic for giving up on things that just
* don't look like mail messages. */
if (message->broken_headers >= 10 &&
message->good_headers < 5)
{
message->parsing_finished = 1;
break;
if (is_mbox) {
if (! g_mime_parser_eos (parser)) {
/* This is a multi-message mbox. */
status = NOTMUCH_STATUS_FILE_NOT_EMAIL;
goto DONE;
}
NEXT_HEADER_LINE (NULL);
continue;
}
message->header_size += strlen (message->line);
message->good_headers++;
header = xstrndup (message->line, colon - message->line);
if (message->restrict_headers &&
! g_hash_table_lookup_extended (message->headers,
header, NULL, NULL))
{
free (header);
NEXT_HEADER_LINE (NULL);
continue;
}
s = colon + 1;
while (*s == ' ' || *s == '\t')
s++;
message->value.len = 0;
copy_header_unfolding (&message->value, s);
NEXT_HEADER_LINE (&message->value);
if (header_desired == NULL)
match = 0;
else
match = (strcasecmp (header, header_desired) == 0);
decoded_value = g_mime_utils_header_decode_text (message->value.str);
header_sofar = (char *)g_hash_table_lookup (message->headers, header);
/* we treat the Received: header special - we want to concat ALL of
* the Received: headers we encounter.
* for everything else we return the first instance of a header */
if (strcasecmp(header, "received") == 0) {
if (header_sofar == NULL) {
/* first Received: header we encountered; just add it */
g_hash_table_insert (message->headers, header, decoded_value);
} else {
/* we need to add the header to those we already collected */
newhdr = strlen(decoded_value);
hdrsofar = strlen(header_sofar);
combined_header = g_malloc(hdrsofar + newhdr + 2);
strncpy(combined_header,header_sofar,hdrsofar);
*(combined_header+hdrsofar) = ' ';
strncpy(combined_header+hdrsofar+1,decoded_value,newhdr+1);
g_free (decoded_value);
g_hash_table_insert (message->headers, header, combined_header);
}
} else {
if (header_sofar == NULL) {
/* Only insert if we don't have a value for this header, yet. */
g_hash_table_insert (message->headers, header, decoded_value);
} else {
free (header);
g_free (decoded_value);
decoded_value = header_sofar;
}
}
/* if we found a match we can bail - unless of course we are
* collecting all the Received: headers */
if (match && !is_received)
return decoded_value;
}
if (message->parsing_finished) {
fclose (message->file);
message->file = NULL;
}
if (message->line)
free (message->line);
message->line = NULL;
if (message->value.size) {
free (message->value.str);
message->value.str = NULL;
message->value.size = 0;
message->value.len = 0;
}
/* For the Received: header we actually might end up here even
* though we found the header (as we force continued parsing
* in that case). So let's check if that's the header we were
* looking for and return the value that we found (if any)
/*
* For historical reasons, we support single-message mboxes,
* but this behavior is likely to change in the future, so
* warn.
*/
if (is_received)
return (char *)g_hash_table_lookup (message->headers, "received");
/* We've parsed all headers and never found the one we're looking
* for. It's probably just not there, but let's check that we
* didn't make a mistake preventing us from seeing it. */
if (message->restrict_headers && header_desired &&
! g_hash_table_lookup_extended (message->headers,
header_desired, NULL, NULL))
{
INTERNAL_ERROR ("Attempt to get header \"%s\" which was not\n"
"included in call to notmuch_message_file_restrict_headers\n",
header_desired);
static notmuch_bool_t mbox_warning = FALSE;
if (! mbox_warning) {
mbox_warning = TRUE;
fprintf (stderr, "\
Warning: %s is an mbox containing a single message,\n\
likely caused by misconfigured mail delivery. Support for single-message\n\
mboxes is deprecated and may be removed in the future.\n", message->filename);
}
}
return "";
DONE:
g_object_unref (stream);
g_object_unref (parser);
if (status) {
g_hash_table_destroy (message->headers);
message->headers = NULL;
if (message->message) {
g_object_unref (message->message);
message->message = NULL;
}
rewind (message->file);
}
return status;
}
notmuch_status_t
_notmuch_message_file_get_mime_message (notmuch_message_file_t *message,
GMimeMessage **mime_message)
{
notmuch_status_t status;
status = _notmuch_message_file_parse (message);
if (status)
return status;
*mime_message = message->message;
return NOTMUCH_STATUS_SUCCESS;
}
/*
* Get all instances of a header decoded and concatenated.
*
* The result must be freed using g_free().
*
* Return NULL on errors, empty string for non-existing headers.
*/
static char *
_notmuch_message_file_get_combined_header (notmuch_message_file_t *message,
const char *header)
{
GMimeHeaderList *headers;
GMimeHeaderIter *iter;
char *combined = NULL;
headers = g_mime_object_get_header_list (GMIME_OBJECT (message->message));
if (! headers)
return NULL;
iter = g_mime_header_iter_new ();
if (! iter)
return NULL;
if (! g_mime_header_list_get_iter (headers, iter))
goto DONE;
do {
const char *value;
char *decoded;
if (strcasecmp (g_mime_header_iter_get_name (iter), header) != 0)
continue;
/* Note that GMime retains ownership of value... */
value = g_mime_header_iter_get_value (iter);
/* ... while decoded needs to be freed with g_free(). */
decoded = g_mime_utils_header_decode_text (value);
if (! decoded) {
if (combined) {
g_free (combined);
combined = NULL;
}
goto DONE;
}
if (combined) {
char *tmp = g_strdup_printf ("%s %s", combined, decoded);
g_free (decoded);
g_free (combined);
if (! tmp) {
combined = NULL;
goto DONE;
}
combined = tmp;
} else {
combined = decoded;
}
} while (g_mime_header_iter_next (iter));
/* Return empty string for non-existing headers. */
if (! combined)
combined = g_strdup ("");
DONE:
g_mime_header_iter_free (iter);
return combined;
}
const char *
notmuch_message_file_get_header (notmuch_message_file_t *message,
const char *header)
{
const char *value;
char *decoded;
if (_notmuch_message_file_parse (message))
return NULL;
/* If we have a cached decoded value, use it. */
value = g_hash_table_lookup (message->headers, header);
if (value)
return value;
if (strcasecmp (header, "received") == 0) {
/*
* The Received: header is special. We concatenate all
* instances of the header as we use this when analyzing the
* path the mail has taken from sender to recipient.
*/
decoded = _notmuch_message_file_get_combined_header (message, header);
} else {
value = g_mime_object_get_header (GMIME_OBJECT (message->message),
header);
if (value)
decoded = g_mime_utils_header_decode_text (value);
else
decoded = g_strdup ("");
}
if (! decoded)
return NULL;
/* Cache the decoded value. We also own the strings. */
g_hash_table_insert (message->headers, xstrdup (header), decoded);
return decoded;
}

View file

@ -412,6 +412,7 @@ _notmuch_message_ensure_message_file (notmuch_message_t *message)
const char *
notmuch_message_get_header (notmuch_message_t *message, const char *header)
{
try {
std::string value;
/* Fetch header from the appropriate xapian value field if
@ -426,6 +427,13 @@ notmuch_message_get_header (notmuch_message_t *message, const char *header)
if (!value.empty())
return talloc_strdup (message, value.c_str ());
} catch (Xapian::Error &error) {
fprintf (stderr, "A Xapian exception occurred when reading header: %s\n",
error.get_msg().c_str());
message->notmuch->exception_reported = TRUE;
return NULL;
}
/* Otherwise fall back to parsing the file */
_notmuch_message_ensure_message_file (message);
if (message->message_file == NULL)
@ -473,6 +481,153 @@ notmuch_message_get_replies (notmuch_message_t *message)
return _notmuch_messages_create (message->replies);
}
static void
_notmuch_message_remove_terms (notmuch_message_t *message, const char *prefix)
{
Xapian::TermIterator i;
size_t prefix_len = strlen (prefix);
while (1) {
i = message->doc.termlist_begin ();
i.skip_to (prefix);
/* Terminate loop when no terms remain with desired prefix. */
if (i == message->doc.termlist_end () ||
strncmp ((*i).c_str (), prefix, prefix_len))
break;
try {
message->doc.remove_term ((*i));
} catch (const Xapian::InvalidArgumentError) {
/* Ignore failure to remove non-existent term. */
}
}
}
/* Return true if p points at "new" or "cur". */
static bool is_maildir (const char *p)
{
return strcmp (p, "cur") == 0 || strcmp (p, "new") == 0;
}
/* Add "folder:" term for directory. */
static notmuch_status_t
_notmuch_message_add_folder_terms (notmuch_message_t *message,
const char *directory)
{
char *folder, *last;
folder = talloc_strdup (NULL, directory);
if (! folder)
return NOTMUCH_STATUS_OUT_OF_MEMORY;
/*
* If the message file is in a leaf directory named "new" or
* "cur", presume maildir and index the parent directory. Thus a
* "folder:" prefix search matches messages in the specified
* maildir folder, i.e. in the specified directory and its "new"
* and "cur" subdirectories.
*
* Note that this means the "folder:" prefix can't be used for
* distinguishing between message files in "new" or "cur". The
* "path:" prefix needs to be used for that.
*
* Note the deliberate difference to _filename_is_in_maildir(). We
* don't want to index different things depending on the existence
* or non-existence of all maildir sibling directories "new",
* "cur", and "tmp". Doing so would be surprising, and difficult
* for the user to fix in case all subdirectories were not in
* place during indexing.
*/
last = strrchr (folder, '/');
if (last) {
if (is_maildir (last + 1))
*last = '\0';
} else if (is_maildir (folder)) {
*folder = '\0';
}
_notmuch_message_add_term (message, "folder", folder);
talloc_free (folder);
return NOTMUCH_STATUS_SUCCESS;
}
#define RECURSIVE_SUFFIX "/**"
/* Add "path:" terms for directory. */
static notmuch_status_t
_notmuch_message_add_path_terms (notmuch_message_t *message,
const char *directory)
{
/* Add exact "path:" term. */
_notmuch_message_add_term (message, "path", directory);
if (strlen (directory)) {
char *path, *p;
path = talloc_asprintf (NULL, "%s%s", directory, RECURSIVE_SUFFIX);
if (! path)
return NOTMUCH_STATUS_OUT_OF_MEMORY;
/* Add recursive "path:" terms for directory and all parents. */
for (p = path + strlen (path) - 1; p > path; p--) {
if (*p == '/') {
strcpy (p, RECURSIVE_SUFFIX);
_notmuch_message_add_term (message, "path", path);
}
}
talloc_free (path);
}
/* Recursive all-matching path:** for consistency. */
_notmuch_message_add_term (message, "path", "**");
return NOTMUCH_STATUS_SUCCESS;
}
/* Add directory based terms for all filenames of the message. */
static notmuch_status_t
_notmuch_message_add_directory_terms (void *ctx, notmuch_message_t *message)
{
const char *direntry_prefix = _find_prefix ("file-direntry");
int direntry_prefix_len = strlen (direntry_prefix);
Xapian::TermIterator i = message->doc.termlist_begin ();
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
for (i.skip_to (direntry_prefix); i != message->doc.termlist_end (); i++) {
unsigned int directory_id;
const char *direntry, *directory;
char *colon;
/* Terminate loop at first term without desired prefix. */
if (strncmp ((*i).c_str (), direntry_prefix, direntry_prefix_len))
break;
/* Indicate that there are filenames remaining. */
status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
direntry = (*i).c_str ();
direntry += direntry_prefix_len;
directory_id = strtol (direntry, &colon, 10);
if (colon == NULL || *colon != ':')
INTERNAL_ERROR ("malformed direntry");
directory = _notmuch_database_get_directory_path (ctx,
message->notmuch,
directory_id);
_notmuch_message_add_folder_terms (message, directory);
_notmuch_message_add_path_terms (message, directory);
}
return status;
}
/* Add an additional 'filename' for 'message'.
*
* This change will not be reflected in the database until the next
@ -504,8 +659,8 @@ _notmuch_message_add_filename (notmuch_message_t *message,
* notmuch_directory_get_child_files() . */
_notmuch_message_add_term (message, "file-direntry", direntry);
/* New terms allow user to search with folder: specification. */
_notmuch_message_gen_terms (message, "folder", directory);
_notmuch_message_add_folder_terms (message, directory);
_notmuch_message_add_path_terms (message, directory);
talloc_free (local);
@ -528,17 +683,10 @@ notmuch_status_t
_notmuch_message_remove_filename (notmuch_message_t *message,
const char *filename)
{
const char *direntry_prefix = _find_prefix ("file-direntry");
int direntry_prefix_len = strlen (direntry_prefix);
const char *folder_prefix = _find_prefix ("folder");
int folder_prefix_len = strlen (folder_prefix);
void *local = talloc_new (message);
char *zfolder_prefix = talloc_asprintf(local, "Z%s", folder_prefix);
int zfolder_prefix_len = strlen (zfolder_prefix);
char *direntry;
notmuch_private_status_t private_status;
notmuch_status_t status;
Xapian::TermIterator i, last;
status = _notmuch_database_filename_to_direntry (
local, message->notmuch, filename, NOTMUCH_FIND_LOOKUP, &direntry);
@ -553,85 +701,38 @@ _notmuch_message_remove_filename (notmuch_message_t *message,
if (status)
return status;
/* Re-synchronize "folder:" terms for this message. This requires:
* 1. removing all "folder:" terms
* 2. removing all "folder:" stemmed terms
* 3. adding back terms for all remaining filenames of the message. */
/* Re-synchronize "folder:" and "path:" terms for this message. */
/* 1. removing all "folder:" terms */
while (1) {
i = message->doc.termlist_begin ();
i.skip_to (folder_prefix);
/* Remove all "folder:" terms. */
_notmuch_message_remove_terms (message, _find_prefix ("folder"));
/* Terminate loop when no terms remain with desired prefix. */
if (i == message->doc.termlist_end () ||
strncmp ((*i).c_str (), folder_prefix, folder_prefix_len))
{
break;
}
/* Remove all "path:" terms. */
_notmuch_message_remove_terms (message, _find_prefix ("path"));
try {
message->doc.remove_term ((*i));
} catch (const Xapian::InvalidArgumentError) {
/* Ignore failure to remove non-existent term. */
}
}
/* 2. removing all "folder:" stemmed terms */
while (1) {
i = message->doc.termlist_begin ();
i.skip_to (zfolder_prefix);
/* Terminate loop when no terms remain with desired prefix. */
if (i == message->doc.termlist_end () ||
strncmp ((*i).c_str (), zfolder_prefix, zfolder_prefix_len))
{
break;
}
try {
message->doc.remove_term ((*i));
} catch (const Xapian::InvalidArgumentError) {
/* Ignore failure to remove non-existent term. */
}
}
/* 3. adding back terms for all remaining filenames of the message. */
i = message->doc.termlist_begin ();
i.skip_to (direntry_prefix);
for (; i != message->doc.termlist_end (); i++) {
unsigned int directory_id;
const char *direntry, *directory;
char *colon;
/* Terminate loop at first term without desired prefix. */
if (strncmp ((*i).c_str (), direntry_prefix, direntry_prefix_len))
break;
/* Indicate that there are filenames remaining. */
status = NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID;
direntry = (*i).c_str ();
direntry += direntry_prefix_len;
directory_id = strtol (direntry, &colon, 10);
if (colon == NULL || *colon != ':')
INTERNAL_ERROR ("malformed direntry");
directory = _notmuch_database_get_directory_path (local,
message->notmuch,
directory_id);
if (strlen (directory))
_notmuch_message_gen_terms (message, "folder", directory);
}
/* Add back terms for all remaining filenames of the message. */
status = _notmuch_message_add_directory_terms (local, message);
talloc_free (local);
return status;
}
/* Upgrade the "folder:" prefix from V1 to V2. */
#define FOLDER_PREFIX_V1 "XFOLDER"
#define ZFOLDER_PREFIX_V1 "Z" FOLDER_PREFIX_V1
void
_notmuch_message_upgrade_folder (notmuch_message_t *message)
{
/* Remove all old "folder:" terms. */
_notmuch_message_remove_terms (message, FOLDER_PREFIX_V1);
/* Remove all old "folder:" stemmed terms. */
_notmuch_message_remove_terms (message, ZFOLDER_PREFIX_V1);
/* Add new boolean "folder:" and "path:" terms. */
_notmuch_message_add_directory_terms (message, message);
}
char *
_notmuch_message_talloc_copy_data (notmuch_message_t *message)
{
@ -766,7 +867,9 @@ notmuch_message_get_date (notmuch_message_t *message)
try {
value = message->doc.get_value (NOTMUCH_VALUE_TIMESTAMP);
} catch (Xapian::Error &error) {
INTERNAL_ERROR ("Failed to read timestamp value from document.");
fprintf (stderr, "A Xapian exception occurred when reading date: %s\n",
error.get_msg().c_str());
message->notmuch->exception_reported = TRUE;
return 0;
}
@ -920,16 +1023,21 @@ _notmuch_message_gen_terms (notmuch_message_t *message,
return NOTMUCH_PRIVATE_STATUS_NULL_POINTER;
term_gen->set_document (message->doc);
term_gen->set_termpos (message->termpos);
if (prefix_name) {
const char *prefix = _find_prefix (prefix_name);
term_gen->set_termpos (message->termpos);
term_gen->index_text (text, 1, prefix);
message->termpos = term_gen->get_termpos ();
/* Create a gap between this an the next terms so they don't
* appear to be a phrase. */
message->termpos = term_gen->get_termpos () + 100;
}
term_gen->set_termpos (message->termpos);
term_gen->index_text (text);
/* Create a term gap, as above. */
message->termpos = term_gen->get_termpos () + 100;
return NOTMUCH_PRIVATE_STATUS_SUCCESS;
}

View file

@ -46,6 +46,8 @@ NOTMUCH_BEGIN_DECLS
#include <talloc.h>
#include <gmime/gmime.h>
#include "xutil.h"
#include "error_util.h"
@ -263,6 +265,9 @@ _notmuch_message_gen_terms (notmuch_message_t *message,
void
_notmuch_message_upgrade_filename_storage (notmuch_message_t *message);
void
_notmuch_message_upgrade_folder (notmuch_message_t *message);
notmuch_status_t
_notmuch_message_add_filename (notmuch_message_t *message,
const char *filename);
@ -317,13 +322,6 @@ notmuch_message_set_author (notmuch_message_t *message, const char *author);
const char *
notmuch_message_get_author (notmuch_message_t *message);
/* index.cc */
notmuch_status_t
_notmuch_message_index_file (notmuch_message_t *message,
const char *filename);
/* message-file.c */
/* XXX: I haven't decided yet whether these will actually get exported
@ -349,31 +347,31 @@ _notmuch_message_file_open_ctx (void *ctx, const char *filename);
void
notmuch_message_file_close (notmuch_message_file_t *message);
/* Restrict 'message' to only save the named headers.
/* Parse the message.
*
* When the caller is only interested in a short list of headers,
* known in advance, calling this function can avoid wasted time and
* memory parsing/saving header values that will never be needed.
*
* The variable arguments should be a list of const char * with a
* final '(const char *) NULL' to terminate the list.
*
* If this function is called, it must be called before any calls to
* notmuch_message_get_header for this message.
*
* After calling this function, if notmuch_message_get_header is
* called with a header name not in this list, then NULL will be
* returned even if that header exists in the actual message.
* This will be done automatically as necessary on other calls
* depending on it, but an explicit call allows for better error
* status reporting.
*/
void
notmuch_message_file_restrict_headers (notmuch_message_file_t *message, ...);
notmuch_status_t
_notmuch_message_file_parse (notmuch_message_file_t *message);
/* Identical to notmuch_message_restrict_headers but accepting a va_list. */
void
notmuch_message_file_restrict_headersv (notmuch_message_file_t *message,
va_list va_headers);
/* Get the gmime message of a message file.
*
* The message file is parsed as necessary.
*
* The GMimeMessage* is set to *mime_message on success (which the
* caller must not unref).
*
* XXX: Would be nice to not have to expose GMimeMessage here.
*/
notmuch_status_t
_notmuch_message_file_get_mime_message (notmuch_message_file_t *message,
GMimeMessage **mime_message);
/* Get the value of the specified header from the message as a UTF-8 string.
*
* The message file is parsed as necessary.
*
* The header name is case insensitive.
*
@ -384,13 +382,19 @@ notmuch_message_file_restrict_headersv (notmuch_message_file_t *message,
* only until the message is closed. The caller should copy it if
* needing to modify the value or to hold onto it for longer.
*
* Returns NULL if the message does not contain a header line matching
* 'header'.
* Returns NULL on errors, empty string if the message does not
* contain a header line matching 'header'.
*/
const char *
notmuch_message_file_get_header (notmuch_message_file_t *message,
const char *header);
/* index.cc */
notmuch_status_t
_notmuch_message_index_file (notmuch_message_t *message,
notmuch_message_file_t *message_file);
/* messages.c */
typedef struct _notmuch_message_node {

File diff suppressed because it is too large Load diff

View file

@ -462,6 +462,9 @@ notmuch_threads_valid (notmuch_threads_t *threads)
{
unsigned int doc_id;
if (! threads)
return FALSE;
while (threads->doc_id_pos < threads->doc_ids->len) {
doc_id = g_array_index (threads->doc_ids, unsigned int,
threads->doc_id_pos);

View file

@ -524,7 +524,7 @@ _notmuch_thread_create (void *ctx,
_resolve_thread_relationships (thread);
/* Commit to returning thread. */
talloc_steal (ctx, thread);
(void) talloc_steal (ctx, thread);
DONE:
talloc_free (local);

2
man/.gitignore vendored
View file

@ -1,2 +0,0 @@
# ignore gzipped man pages
*.[0-9].gz

View file

@ -1,55 +0,0 @@
# -*- Makefile -*-
dir := man
# this variable seems to be needed to prevent lazy evaluation causing
# problems with $(dir) changing values.
MAIN_PAGE := $(dir)/man1/notmuch.1
MAN1 := \
$(MAIN_PAGE) \
$(dir)/man1/notmuch-compact.1 \
$(dir)/man1/notmuch-config.1 \
$(dir)/man1/notmuch-count.1 \
$(dir)/man1/notmuch-dump.1 \
$(dir)/man1/notmuch-restore.1 \
$(dir)/man1/notmuch-insert.1 \
$(dir)/man1/notmuch-new.1 \
$(dir)/man1/notmuch-reply.1 \
$(dir)/man1/notmuch-search.1 \
$(dir)/man1/notmuch-show.1 \
$(dir)/man1/notmuch-tag.1
MAN5 := $(dir)/man5/notmuch-hooks.5
MAN7 := $(dir)/man7/notmuch-search-terms.7
MAN1_GZ := $(addsuffix .gz,$(MAN1))
MAN5_GZ := $(addsuffix .gz,$(MAN5))
MAN7_GZ := $(addsuffix .gz,$(MAN7))
MAN_SOURCE := $(MAN1) $(MAN5) $(MAN7)
MAN_BACKUP := $(addsuffix .bak,$(MAN_SOURCE))
COMPRESSED_MAN := $(MAN1_GZ) $(MAN5_GZ) $(MAN7_GZ)
%.gz: %
gzip --stdout $^ > $@
.PHONY: install-man update-man-versions
install-man: $(COMPRESSED_MAN)
mkdir -p "$(DESTDIR)$(mandir)/man1"
mkdir -p "$(DESTDIR)$(mandir)/man5"
mkdir -p "$(DESTDIR)$(mandir)/man7"
install -m0644 $(MAN1_GZ) $(DESTDIR)/$(mandir)/man1
install -m0644 $(MAN5_GZ) $(DESTDIR)/$(mandir)/man5
install -m0644 $(MAN7_GZ) $(DESTDIR)/$(mandir)/man7
cd $(DESTDIR)/$(mandir)/man1 && ln -sf notmuch.1.gz notmuch-setup.1.gz
update-man-versions: $(MAN_SOURCE)
for file in $(MAN_SOURCE); do \
cp $$file $$file.bak ; \
sed "s/^.TH NOTMUCH\([^[:blank:]]*\) \([1-9]\) .*$$/.TH NOTMUCH\1 \2 ${DATE} \"Notmuch ${VERSION}\"/" \
< $$file.bak > $$file; \
done
CLEAN := $(CLEAN) $(COMPRESSED_MAN) $(MAN_BACKUP)

View file

@ -1,62 +0,0 @@
.TH NOTMUCH-COMPACT 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-compact \- compact the notmuch database
.SH SYNOPSIS
.B notmuch compact
.RI "[ --quiet ]"
.RI "[ --backup=<" directory "> ]"
.SH DESCRIPTION
The
.B compact
command can be used to compact the notmuch database. This can both reduce
the space required by the database and improve lookup performance.
The compacted database is built in a temporary directory and is later
moved into the place of the origin database. The original uncompacted
database is discarded, unless the
.BR "\-\-backup=" <directory>
option is used.
Note that the database write lock will be held during the compaction
process (which may be quite long) to protect data integrity.
Supported options for
.B compact
include
.RS 4
.TP 4
.BR "\-\-backup=" <directory>
Save the current database to the given directory before replacing it
with the compacted database. The backup directory must not exist and
it must reside on the same mounted filesystem as the current database.
.RE
.RS 4
.TP 4
.BR \-\-quiet
Do not report database compaction progress to stdout.
.RE
.RE
.SH ENVIRONMENT
The following environment variables can be used to control the
behavior of notmuch.
.TP
.B NOTMUCH_CONFIG
Specifies the location of the notmuch configuration file. Notmuch will
use ${HOME}/.notmuch\-config if this variable is not set.
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-count\fR(1), \fBnotmuch-dump\fR(1),
\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,158 +0,0 @@
.TH NOTMUCH-CONFIG 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-config \- access notmuch configuration file
.SH SYNOPSIS
.B notmuch config get
.RI "<" section ">.<" item ">"
.B notmuch config set
.RI "<" section ">.<" item "> [" value " ...]"
.B notmuch config list
.SH DESCRIPTION
The
.B config
command can be used to get or set settings in the notmuch
configuration file.
.RS 4
.TP 4
.B get
The value of the specified configuration item is printed to stdout. If
the item has multiple values (it is a list), each value is separated
by a newline character.
.RE
.RS 4
.TP 4
.B set
The specified configuration item is set to the given value. To specify
a multiple-value item (a list), 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
.RS 4
.TP 4
.B list
Every configuration item is printed to stdout, each on a separate line
of the form:
.RI "" section "." item "=" value
No additional whitespace surrounds the dot or equals sign characters. In a
multiple-value item (a list), the values are separated by semicolon characters.
.RE
The available configuration items are described below.
.RS 4
.TP 4
.B database.path
The top-level directory where your mail currently exists and to where
mail will be delivered in the future. Files should be individual email
messages. Notmuch will store its database within a sub-directory of
the path configured here named
.BR ".notmuch".
.RE
.RS 4
.TP 4
.B user.name
Your full name.
.RE
.RS 4
.TP 4
.B user.primary_email
Your primary email address.
.RE
.RS 4
.TP 4
.B user.other_email
A list of other email addresses at which you receive email.
.RE
.RS 4
.TP 4
.B new.tags
A list of tags that will be added to all messages incorporated by
.BR "notmuch new".
.RE
.RS 4
.TP 4
.B new.ignore
A list of file and directory names, without path, that will not be
searched for messages by
.BR "notmuch new".
All the files and directories matching any of the names specified here
will be ignored, regardless of the location in the mail store
directory hierarchy.
.RE
.RS 4
.TP 4
.B search.exclude_tags
A list of tags that will be excluded from search results by
default. Using an excluded tag in a query will override that
exclusion.
.RE
.RS 4
.TP 4
.B maildir.synchronize_flags
If true, then the following maildir flags (in message filenames) will
be synchronized with the corresponding notmuch tags:
Flag Tag
---- -------
D draft
F flagged
P passed
R replied
S unread (added when 'S' flag is not present)
The
.B notmuch new
command will notice flag changes in filenames and update tags, while
the
.B notmuch tag
and
.B notmuch restore
commands will notice tag changes and update flags in filenames.
If there have been any changes in the maildir (new messages added, old
ones removed or renamed, maildir flags changed, etc.), it is advisable
to run
.B notmuch new
before
.B notmuch tag
or
.B notmuch restore
commands to ensure the tag changes are properly synchronized to the
maildir flags, as the commands expect the database and maildir to be
in sync.
.RE
.RE
.SH ENVIRONMENT
The following environment variables can be used to control the
behavior of notmuch.
.TP
.B NOTMUCH_CONFIG
Specifies the location of the notmuch configuration file. Notmuch will
use ${HOME}/.notmuch\-config if this variable is not set.
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-count\fR(1), \fBnotmuch-dump\fR(1),
\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,86 +0,0 @@
.TH NOTMUCH-COUNT 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-count \- count messages matching the given search terms
.SH SYNOPSIS
.B notmuch count
.RI [ options "... ] <" search-term ">..."
.SH DESCRIPTION
Count messages matching the search terms.
The number of matching messages (or threads) is output to stdout.
With no search terms, a count of all messages (or threads) in the database will
be displayed.
See \fBnotmuch-search-terms\fR(7)
for details of the supported syntax for <search-terms>.
Supported options for
.B count
include
.RS 4
.TP 4
.B \-\-output=(messages|threads|files)
.RS 4
.TP 4
.B messages
Output the number of matching messages. This is the default.
.RE
.RS 4
.TP 4
.B threads
Output the number of matching threads.
.RE
.RS 4
.TP 4
.B files
Output the number of files associated with matching messages. This may
be bigger than the number of matching messages due to duplicates
(i.e. multiple files having the same message-id).
.RE
.RE
.RS 4
.TP 4
.BR \-\-exclude=(true|false)
Specify whether to omit messages matching search.tag_exclude from the
count (the default) or not.
.RE
.RS 4
.TP 4
.BR \-\-batch
Read queries from a file (stdin by default), one per line, and output
the number of matching messages (or threads) to stdout, one per
line. On an empty input line the count of all messages (or threads) in
the database will be output. This option is not compatible with
specifying search terms on the command line.
.RE
.RS 4
.TP 4
.BR "\-\-input=" <filename>
Read input from given file, instead of from stdin. Implies
.BR --batch .
.RE
.RE
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-dump\fR(1),
\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,98 +0,0 @@
.TH NOTMUCH-DUMP 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-dump \- creates a plain-text dump of the tags of each message
.SH SYNOPSIS
.B "notmuch dump"
.RB [ "\-\-format=(sup|batch-tag)" "] [--]"
.RI "[ --output=<" filename "> ] [--]"
.RI "[ <" search-term ">...]"
.SH DESCRIPTION
Dump tags for messages matching the given search terms.
Output is to the given filename, if any, or to stdout.
These tags are the only data in the notmuch database that can't be
recreated from the messages themselves. The output of notmuch dump is
therefore the only critical thing to backup (and much more friendly to
incremental backup than the native database files.)
.TP 4
.B \-\-format=(sup|batch-tag)
Notmuch restore supports two plain text dump formats, both with one message-id
per line, followed by a list of tags.
.RS 4
.TP 4
.B sup
The
.B sup
dump file format is specifically chosen to be
compatible with the format of files produced by sup-dump.
So if you've previously been using sup for mail, then the
.B "notmuch restore"
command provides you a way to import all of your tags (or labels as
sup calls them).
Each line has the following form
.RS 4
.RI < message-id >
.B (
.RI < tag "> ..."
.B )
with zero or more tags are separated by spaces. Note that (malformed)
message-ids may contain arbitrary non-null characters. Note also
that tags with spaces will not be correctly restored with this format.
.RE
.RE
.RS 4
.TP 4
.B batch-tag
The
.B batch-tag
dump format is intended to more robust against malformed message-ids
and tags containing whitespace or non-\fBascii\fR(7) characters.
Each line has the form
.RS 4
.RI "+<" "encoded-tag" "> " "" "+<" "encoded-tag" "> ... -- " "" " id:<" quoted-message-id >
Tags are hex-encoded by replacing every byte not matching the regex
.B [A-Za-z0-9@=.,_+-]
with
.B %nn
where nn is the two digit hex encoding. The message ID is a valid Xapian
query, quoted using Xapian boolean term quoting rules: if the ID contains
whitespace or a close paren or starts with a double quote, it must be
enclosed in double quotes and double quotes inside the ID must be doubled.
The astute reader will notice this is a special case of the batch input
format for \fBnotmuch-tag\fR(1); note that the single message-id query is
mandatory for \fBnotmuch-restore\fR(1).
.RE
With no search terms, a dump of all messages in the database will be
generated. A "--" argument instructs notmuch that the
remaining arguments are search terms.
See \fBnotmuch-search-terms\fR(7)
for details of the supported syntax for <search-terms>.
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,75 +0,0 @@
.TH NOTMUCH-INSERT 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-insert \- add a message to the maildir and notmuch database
.SH SYNOPSIS
.B notmuch insert
.RI "[" options "]"
.RI "[ +<" tag> "|\-<" tag "> ... ]"
.SH DESCRIPTION
.B notmuch insert
reads a message from standard input
and delivers it into the maildir directory given by configuration option
.BR database.path ,
then incorporates the message into the notmuch database.
It is an alternative to using a separate tool to deliver
the message then running
.B notmuch new
afterwards.
The new message will be tagged with the tags specified by the
.B new.tags
configuration option, then by operations specified on the command-line:
tags prefixed by '+' are added while
those prefixed by '\-' are removed.
If the new message is a duplicate of an existing message in the database
(it has same Message-ID), it will be added to the maildir folder and
notmuch database, but the tags will not be changed.
Option arguments must appear before any tag operation arguments.
Supported options for
.B insert
include
.RS 4
.TP 4
.BI "--folder=<" folder ">"
Deliver the message to the specified folder,
relative to the top-level directory given by the value of
\fBdatabase.path\fR.
The default is to deliver to the top-level directory.
.RE
.RS 4
.TP 4
.B "--create-folder"
Try to create the folder named by the
.B "--folder"
option, if it does not exist.
Otherwise the folder must already exist for mail
delivery to succeed.
.RE
.SH EXIT STATUS
This command returns exit status 0 if the message was successfully
added to the mail directory, even if the message could not be indexed
and added to the notmuch database. In the latter case, a warning will
be printed to standard error but the message file will be left on disk.
If the message could not be written to disk then a non-zero exit
status is returned.
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-reply\fR(1),
\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,70 +0,0 @@
.TH NOTMUCH-NEW 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-new \- incorporate new mail into the notmuch database
.SH SYNOPSIS
.B notmuch new
.RB "[" --no-hooks "]"
.SH DESCRIPTION
Find and import any new messages to the database.
The
.B new
command scans all sub-directories of the database, performing
full-text indexing on new messages that are found. Each new message
will automatically be tagged with both the
.BR inbox " and " unread
tags.
You should run
.B "notmuch new"
once after first running
.B "notmuch setup"
to create the initial database. The first run may take a long time if
you have a significant amount of mail (several hundred thousand
messages or more). Subsequently, you should run
.B "notmuch new"
whenever new mail is delivered and you wish to incorporate it into the
database. These subsequent runs will be much quicker than the initial
run.
Invoking
.B notmuch
with no command argument will run
.B new
if
.B "notmuch setup"
has previously been completed, but
.B "notmuch new"
has not previously been run.
.B "notmuch new"
updates tags according to maildir flag changes if the
.B "maildir.synchronize_flags"
configuration option is enabled. See \fBnotmuch-config\fR(1) for
details.
The
.B new
command supports hooks. See \fBnotmuch-hooks(5)\fR
for more details on hooks.
Supported options for
.B new
include
.RS 4
.TP 4
.BR \-\-no\-hooks
Prevents hooks from being run.
.RE
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5), \fBnotmuch-insert\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,133 +0,0 @@
.TH NOTMUCH-REPLY 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-reply \- constructs a reply template for a set of messages
.SH SYNOPSIS
.B notmuch reply
.RI "[" options "...] <" search-term ">..."
.SH DESCRIPTION
Constructs a reply template for a set of messages.
To make replying to email easier,
.B notmuch reply
takes an existing set of messages and constructs a suitable mail
template. The Reply-to: header (if any, otherwise From:) is used for
the To: address. Unless
.BR \-\-reply-to=sender
is specified, values from the To: and Cc: headers are copied, but not
including any of the current user's email addresses (as configured in
primary_mail or other_email in the .notmuch\-config file) in the
recipient list.
It also builds a suitable new subject, including Re: at the front (if
not already present), and adding the message IDs of the messages being
replied to to the References list and setting the In\-Reply\-To: field
correctly.
Finally, the original contents of the emails are quoted by prefixing
each line with '> ' and included in the body.
The resulting message template is output to stdout.
Supported options for
.B reply
include
.RS
.TP 4
.BR \-\-format= ( default | json | sexp | headers\-only )
.RS
.TP 4
.BR default
Includes subject and quoted message body as an RFC 2822 message.
.TP
.BR json
Produces JSON output containing headers for a reply message and the
contents of the original message. This output can be used by a client
to create a reply message intelligently.
.TP
.BR sexp
Produces S-Expression output containing headers for a reply message and
the contents of the original message. This output can be used by a client
to create a reply message intelligently.
.TP
.BR headers\-only
Only produces In\-Reply\-To, References, To, Cc, and Bcc headers.
.RE
.RE
.RS
.TP 4
.BR \-\-format-version=N
Use the specified structured output format version. This is intended
for programs that invoke \fBnotmuch\fR(1) internally. If omitted, the
latest supported version will be used.
.RE
.RS
.TP 4
.BR \-\-reply\-to= ( all | sender )
.RS
.TP 4
.BR all " (default)"
Replies to all addresses.
.TP 4
.BR sender
Replies only to the sender. If replying to user's own message
(Reply-to: or From: header is one of the user's configured email
addresses), try To:, Cc:, and Bcc: headers in this order, and copy
values from the first that contains something other than only the
user's addresses.
.RE
.RE
.RS
.TP 4
.B \-\-decrypt
Decrypt any MIME encrypted parts found in the selected content
(ie. "multipart/encrypted" parts). Status of the decryption will be
reported (currently only supported with --format=json and
--format=sexp) and on successful decryption the multipart/encrypted
part will be replaced by the decrypted content.
Decryption expects a functioning \fBgpg-agent\fR(1) to provide any
needed credentials. Without one, the decryption will fail.
.RE
See \fBnotmuch-search-terms\fR(7)
for details of the supported syntax for <search-terms>.
Note: It is most common to use
.B "notmuch reply"
with a search string matching a single message, (such as
id:<message-id>), but it can be useful to reply to several messages at
once. For example, when a series of patches are sent in a single
thread, replying to the entire thread allows for the reply to comment
on issues found in multiple patches. The default format supports
replying to multiple messages at once, but the JSON and S-Expression
formats do not.
.RE
.RE
.SH EXIT STATUS
This command supports the following special exit status codes
.TP
.B 20
The requested format version is too old.
.TP
.B 21
The requested format version is too new.
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,91 +0,0 @@
.TH NOTMUCH-RESTORE 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-restore \- restores the tags from the given file (see notmuch dump)
.SH SYNOPSIS
.B "notmuch restore"
.RB [ "--accumulate" ]
.RB [ "--format=(auto|batch-tag|sup)" ]
.RI "[ --input=<" filename "> ]"
.SH DESCRIPTION
Restores the tags from the given file (see
.BR "notmuch dump" ")."
The input is read from the given filename, if any, or from stdin.
Supported options for
.B restore
include
.RS 4
.TP 4
.B \-\-accumulate
The union of the existing and new tags is applied, instead of
replacing each message's tags as they are read in from the dump file.
.RE
.RS 4
.TP 4
.B \-\-format=(sup|batch-tag|auto)
Notmuch restore supports two plain text dump formats, with each line
specifying a message-id and a set of tags.
For details of the actual formats, see \fBnotmuch-dump\fR(1).
.RS 4
.TP 4
.B sup
The
.B sup
dump file format is specifically chosen to be
compatible with the format of files produced by sup-dump.
So if you've previously been using sup for mail, then the
.B "notmuch restore"
command provides you a way to import all of your tags (or labels as
sup calls them).
.RE
.RS 4
.TP 4
.B batch-tag
The
.B batch-tag
dump format is intended to more robust against malformed message-ids
and tags containing whitespace or non-\fBascii\fR(7) characters. See
\fBnotmuch-dump\fR(1) for details on this format.
.B "notmuch restore"
updates the maildir flags according to tag changes if the
.B "maildir.synchronize_flags"
configuration option is enabled. See \fBnotmuch-config\fR(1) for
details.
.RE
.RS 4
.TP 4
.B auto
This option (the default) tries to guess the format from the
input. For correctly formed input in either supported format, this
heuristic, based the fact that batch-tag format contains no parentheses,
should be accurate.
.RE
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,199 +0,0 @@
.TH NOTMUCH-SEARCH 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-search \- search for messages matching the given search terms
.SH SYNOPSIS
.B notmuch search
.RI [ options "...] <" search-term ">..."
.SH DESCRIPTION
Search for messages matching the given search terms, and display as
results the threads containing the matched messages.
The output consists of one line per thread, giving a thread ID, the
date of the newest (or oldest, depending on the sort option) matched
message in the thread, the number of matched messages and total
messages in the thread, the names of all participants in the thread,
and the subject of the newest (or oldest) message.
See \fBnotmuch-search-terms\fR(7)
for details of the supported syntax for <search-terms>.
Supported options for
.B search
include
.RS 4
.TP 4
.BR \-\-format= ( json | sexp | text | text0 )
Presents the results in either JSON, S-Expressions, newline character
separated plain-text (default), or null character separated plain-text
(compatible with \fBxargs\fR(1) -0 option where available).
.RE
.RS 4
.TP 4
.BR \-\-format-version=N
Use the specified structured output format version. This is intended
for programs that invoke \fBnotmuch\fR(1) internally. If omitted, the
latest supported version will be used.
.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), separated by null
characters (\-\-format=text0), as a JSON array (\-\-format=json), or
an S-Expression list (\-\-format=sexp).
.RE
.RS 4
.TP 4
.B messages
Output the message IDs of all messages matching the search terms,
either one per line (\-\-format=text), separated by null characters
(\-\-format=text0), as a JSON array (\-\-format=json), or as an
S-Expression list (\-\-format=sexp).
.RE
.RS 4
.TP 4
.B files
Output the filenames of all messages matching the search terms, either
one per line (\-\-format=text), separated by null characters
(\-\-format=text0), as a JSON array (\-\-format=json), or as an
S-Expression list (\-\-format=sexp).
Note that each message may have multiple filenames associated with it.
All of them are included in the output, unless limited with the
\-\-duplicate=N option.
.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), separated by null characters
(\-\-format=text0), as a JSON array (\-\-format=json), or as an
S-Expression list (\-\-format=sexp).
.RE
.RE
.RS 4
.TP 4
.BR \-\-sort= ( newest\-first | oldest\-first )
This option can be used to present results in either chronological order
.RB ( oldest\-first )
or reverse chronological order
.RB ( newest\-first ).
Note: The thread order will be distinct between these two options
(beyond being simply reversed). When sorting by
.B oldest\-first
the threads will be sorted by the oldest message in each thread, but
when sorting by
.B newest\-first
the threads will be sorted by the newest message in each thread.
By default, results will be displayed in reverse chronological order,
(that is, the newest results will be displayed first).
.RE
.RS 4
.TP 4
.BR \-\-offset=[\-]N
Skip displaying the first N results. With the leading '\-', start at the Nth
result from the end.
.RE
.RS 4
.TP 4
.BR \-\-limit=N
Limit the number of displayed results to N.
.RE
.RS 4
.TP 4
.BR \-\-exclude=(true|false|all|flag)
A message is called "excluded" if it matches at least one tag in
search.tag_exclude that does not appear explicitly in the search terms.
This option specifies whether to omit excluded messages in the search
process.
The default value,
.BR true ,
prevents excluded messages from matching the search terms.
.B all
additionally prevents excluded messages from appearing in displayed
results, in effect behaving as though the excluded messages do not exist.
.B false
allows excluded messages to match search terms and appear in displayed
results. Excluded messages are still marked in the relevant outputs.
.B flag
only has an effect when
.BR --output=summary .
The output is almost identical to
.BR false ,
but the "match count" is the number of matching non-excluded messages in the
thread, rather than the number of matching messages.
.RE
.RS 4
.TP 4
.BR \-\-duplicate=N
Effective with
.BR --output=files ,
output the Nth filename associated with each message matching the
query (N is 1-based). If N is greater than the number of files
associated with the message, don't print anything.
Note that this option is orthogonal with the
.BR folder:
search prefix. The prefix matches messages based on filenames. This
option filters filenames of the matching messages.
.RE
.SH EXIT STATUS
This command supports the following special exit status codes
.TP
.B 20
The requested format version is too old.
.TP
.B 21
The requested format version is too new.
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1 +0,0 @@
notmuch.1

View file

@ -1,250 +0,0 @@
.TH NOTMUCH-SHOW 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-show \- show messages matching the given search terms
.SH SYNOPSIS
.B notmuch show
.RI "[" options "...] <" search-term ">..."
.SH DESCRIPTION
Shows all messages matching the search terms.
See \fBnotmuch-search-terms\fR(7)
for details of the supported syntax for <search-terms>.
The messages will be grouped and sorted based on the threading (all
replies to a particular message will appear immediately after that
message in date order). The output is not indented by default, but
depth tags are printed so that proper indentation can be performed by
a post-processor (such as the emacs interface to notmuch).
Supported options for
.B show
include
.RS 4
.TP 4
.B \-\-entire\-thread=(true|false)
If true,
.B notmuch show
outputs all messages in the thread of any message matching the search
terms; if false, it outputs only the matching messages. For
.B --format=json
and
.B --format=sexp
this defaults to true. For other formats, this defaults to false.
.RE
.RS 4
.TP 4
.B \-\-format=(text|json|sexp|mbox|raw)
.RS 4
.TP 4
.BR text " (default for messages)"
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. For a multipart MIME message, these parts will
be nested.
.RE
.RS 4
.TP 4
.B json
The output is formatted with Javascript Object Notation (JSON). This
format is more robust than the text format for automated
processing. The nested structure of multipart MIME messages is
reflected in nested JSON output. By default JSON output includes all
messages in a matching thread; that is, by default,
.B \-\-format=json
sets
.B "\-\-entire\-thread"
The caller can disable this behaviour by setting
.B \-\-entire\-thread=false
.RE
.RS 4
.TP 4
.B sexp
The output is formatted as an S-Expression (sexp). This
format is more robust than the text format for automated
processing. The nested structure of multipart MIME messages is
reflected in nested S-Expression output. By default,
S-Expression output includes all messages in a matching thread;
that is, by default,
.B \-\-format=sexp
sets
.B "\-\-entire\-thread"
The caller can disable this behaviour by setting
.B \-\-entire\-thread=false
.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:
.nf
.nh
http://homepage.ntlworld.com/jonathan.deboynepollard/FGA/mail-mbox-formats.html
.hy
.fi
.
.RE
.RS 4
.TP 4
.BR raw " (default for a single part, see \-\-part)"
For a message or an attached message part, the original, raw content
of the email message is output. Consumers of this format should expect
to implement MIME decoding and similar functions.
For a single part (\-\-part) the raw part content is output after
performing any necessary MIME decoding. Note that messages with a
simple body still have two parts: part 0 is the whole message and part
1 is the body.
For a multipart part, the part headers and body (including all child
parts) is output.
The raw format must only be used with search terms matching single
message.
.RE
.RE
.RS 4
.TP 4
.BR \-\-format-version=N
Use the specified structured output format version. This is intended
for programs that invoke \fBnotmuch\fR(1) internally. If omitted, the
latest supported version will be used.
.RE
.RS 4
.TP 4
.B \-\-part=N
Output the single decoded MIME part N of a single message. The search
terms must match only a single message. Message parts are numbered in
a depth-first walk of the message MIME structure, and are identified
in the 'json', 'sexp' or 'text' output formats.
.RE
.RS 4
.TP 4
.B \-\-verify
Compute and report the validity of any MIME cryptographic signatures
found in the selected content (ie. "multipart/signed" parts). Status
of the signature will be reported (currently only supported with
--format=json and --format=sexp), and the multipart/signed part
will be replaced by the signed data.
.RE
.RS 4
.TP 4
.B \-\-decrypt
Decrypt any MIME encrypted parts found in the selected content
(ie. "multipart/encrypted" parts). Status of the decryption will be
reported (currently only supported with --format=json and
--format=sexp) and on successful decryption the multipart/encrypted
part will be replaced by the decrypted content.
Decryption expects a functioning \fBgpg-agent\fR(1) to provide any
needed credentials. Without one, the decryption will fail.
Implies --verify.
.RE
.RS 4
.TP 4
.BR \-\-exclude=(true|false)
Specify whether to omit threads only matching search.tag_exclude from
the search results (the default) or not. In either case the excluded
message will be marked with the exclude flag (except when output=mbox
when there is nowhere to put the flag).
If --entire-thread is specified then complete threads are returned
regardless (with the excluded flag being set when appropriate) but
threads that only match in an excluded message are not returned when
.B --exclude=true.
The default is
.B --exclude=true.
.RE
.RS 4
.TP 4
.B \-\-body=(true|false)
If true (the default)
.B notmuch show
includes the bodies of the messages in the output; if false,
bodies are omitted.
.B --body=false
is only implemented for the json and sexp formats and it is incompatible with
.B --part > 0.
This is useful if the caller only needs the headers as body-less
output is much faster and substantially smaller.
.RE
.RS 4
.TP 4
.B \-\-include-html
Include "text/html" parts as part of the output (currently only supported with
--format=json and --format=sexp).
By default, unless
.B --part=N
is used to select a specific part or
.B --include-html
is used to include all "text/html" parts, no part with content type "text/html"
is included in the output.
.RE
A common use of
.B notmuch show
is to display a single thread of email messages. For this, use a
search term of "thread:<thread-id>" as can be seen in the first
column of output from the
.B notmuch search
command.
.SH EXIT STATUS
This command supports the following special exit status codes
.TP
.B 20
The requested format version is too old.
.TP
.B 21
The requested format version is too new.
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1),
\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7),
\fBnotmuch-tag\fR(1)

View file

@ -1,142 +0,0 @@
.TH NOTMUCH-TAG 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-tag \- add/remove tags for all messages matching the search terms
.SH SYNOPSIS
.B notmuch tag
.RI [ options "...] +<" tag ">|\-<" tag "> [...] [\-\-] <" search-term "> [...]"
.B notmuch tag
.RI "--batch"
.RI "[ --input=<" filename "> ]"
.SH DESCRIPTION
Add/remove tags for all messages matching the search terms.
See \fBnotmuch-search-terms\fR(7)
for details of the supported syntax for
.RI < search-term >.
Tags prefixed by '+' are added while those prefixed by '\-' are
removed. For each message, tag changes are applied in the order they
appear on the command line.
The beginning of the search terms is recognized by the first
argument that begins with neither '+' nor '\-'. Support for
an initial search term beginning with '+' or '\-' is provided
by allowing the user to specify a "\-\-" argument to separate
the tags from the search terms.
.B "notmuch tag"
updates the maildir flags according to tag changes if the
.B "maildir.synchronize_flags"
configuration option is enabled. See \fBnotmuch-config\fR(1) for
details.
Supported options for
.B tag
include
.RS 4
.TP 4
.BR \-\-remove\-all
Remove all tags from each message matching the search terms before
applying the tag changes appearing on the command line. This means
setting the tags of each message to the tags to be added. If there are
no tags to be added, the messages will have no tags.
.RE
.RS 4
.TP 4
.BR \-\-batch
Read batch tagging operations from a file (stdin by default). This is more
efficient than repeated
.B notmuch tag
invocations. See
.B TAG FILE FORMAT
below for the input format. This option is not compatible with
specifying tagging on the command line.
.RE
.RS 4
.TP 4
.BR "\-\-input=" <filename>
Read input from given file, instead of from stdin. Implies
.BR --batch .
.SH TAG FILE FORMAT
The input must consist of lines of the format:
.RI "+<" tag ">|\-<" tag "> [...] [\-\-] <" query ">"
Each line is interpreted similarly to
.B notmuch tag
command line arguments. The delimiter is one or more spaces ' '. Any
characters in
.RI < tag >
.B may
be hex-encoded with %NN where NN is the hexadecimal value of the
character. To hex-encode a character with a multi-byte UTF-8 encoding,
hex-encode each byte.
Any spaces in <tag>
.B must
be hex-encoded as %20. Any characters that are not
part of
.RI < tag >
.B must not
be hex-encoded.
In the future tag:"tag with spaces" style quoting may be supported for
.RI < tag >
as well;
for this reason all double quote characters in
.RI < tag >
.B should
be hex-encoded.
The
.RI < query >
should be quoted using Xapian boolean term quoting rules: if a term
contains whitespace or a close paren or starts with a double quote, it
must be enclosed in double quotes (not including any prefix) and
double quotes inside the term must be doubled (see below for
examples).
Leading and trailing space ' ' is ignored. Empty lines and lines
beginning with '#' are ignored.
.SS EXAMPLE
The following shows a valid input to batch tagging. Note that only the
isolated '*' acts as a wildcard. Also note the two different quotings
of the tag
.B space in tags
.
.RS
.nf
+winner *
+foo::bar%25 -- (One and Two) or (One and tag:winner)
+found::it -- tag:foo::bar%
# ignore this line and the next
+space%20in%20tags -- Two
# add tag '(tags)', among other stunts.
+crazy{ +(tags) +&are +#possible\ -- tag:"space in tags"
+match*crazy -- tag:crazy{
+some_tag -- id:"this is ""nauty)"""
.fi
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1),
\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7),
\fBnotmuch-show\fR(1),

View file

@ -1,190 +0,0 @@
.\" notmuch - Not much of an email program, (just index, search and tagging)
.\"
.\" Copyright © 2009 Carl Worth
.\"
.\" Notmuch 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.
.\"
.\" Notmuch 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>
.TH NOTMUCH 1 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch \- thread-based email index, search, and tagging
.SH SYNOPSIS
.B notmuch
.RI "[" option " ...] " command " [" arg " ...]"
.SH DESCRIPTION
Notmuch is a command-line based program for indexing, searching,
reading, and tagging large collections of email messages.
This page describes how to get started using notmuch from the command
line, and gives a brief overview of the commands available. For more
information on e.g.
.B notmuch show
consult the \fBnotmuch-show\fR(1) man page, also accessible via
.B notmuch help show
The quickest way to get started with Notmuch is to simply invoke the
.B notmuch
command with no arguments, which will interactively guide you through
the process of indexing your mail.
.SH NOTE
While the command-line program
.B notmuch
provides powerful functionality, it does not provide the most
convenient interface for that functionality. More sophisticated
interfaces are expected to be built on top of either the command-line
interface, or more likely, on top of the notmuch library
interface. See http://notmuchmail.org for more about alternate
interfaces to notmuch. The emacs-based interface to notmuch (available under
.B emacs/
in the Notmuch source distribution) is probably the most widely used at
this time.
.SH OPTIONS
Supported global options for
.B notmuch
include
.RS 4
.TP 4
.B \-\-help
Print a synopsis of available commands and exit.
.RE
.RS 4
.TP 4
.B \-\-version
Print the installed version of notmuch, and exit.
.RE
.RS 4
.TP 4
.B \-\-config=FILE
Specify the configuration file to use. This overrides any
configuration file specified by ${NOTMUCH_CONFIG}.
.RE
.SH COMMANDS
.SS SETUP
The
.B notmuch setup
command is used to configure Notmuch for first use, (or to reconfigure
it later).
The setup command will prompt for your full name, your primary email
address, any alternate email addresses you use, and the directory
containing your email archives. Your answers will be written to a
configuration file in ${NOTMUCH_CONFIG} (if set) or
${HOME}/.notmuch-config . This configuration file will be created with
descriptive comments, making it easy to edit by hand later to change the
configuration. Or you can run
.B "notmuch setup"
again to change the configuration.
The mail directory you specify can contain any number of
sub-directories and should 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.
Mail storage that uses mbox format, (where one mbox file contains many
messages), will not work with notmuch. If that's how your mail is
currently stored, it is recommended you first convert it to maildir
format with a utility such as mb2md before running
.B "notmuch setup" .
Invoking
.B notmuch
with no command argument will run
.B setup
if the setup command has not previously been completed.
.RE
.SS OTHER COMMANDS
Several of the notmuch commands accept search terms with a common
syntax. See \fNnotmuch-search-terms\fR(7)
for more details on the supported syntax.
The
.BR search ", " show " and " count
commands are used to query the email database.
The
.B reply
command is useful for preparing a template for an email reply.
The
.B tag
command is the only command available for manipulating database
contents.
The
.BR dump " and " restore
commands can be used to create a textual dump of email tags for backup
purposes, and to restore from that dump.
The
.B config
command can be used to get or set settings int the notmuch
configuration file.
.SH ENVIRONMENT
The following environment variables can be used to control the
behavior of notmuch.
.TP
.B NOTMUCH_CONFIG
Specifies the location of the notmuch configuration file. Notmuch will
use ${HOME}/.notmuch\-config if this variable is not set.
.TP
.B NOTMUCH_TALLOC_REPORT
Location to write a talloc memory usage report. See
.B talloc_enable_leak_report_full
in \fBtalloc\fR(3)
for more information.
.TP
.B NOTMUCH_DEBUG_QUERY
If set to a non-empty value, the notmuch library will print (to stderr) Xapian
queries it constructs.
.SH SEE ALSO
\fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1),
\fBnotmuch-search\fR(1), \fBnotmuch-search-terms\fR(7),
\fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1)
The notmuch website:
.B http://notmuchmail.org
.SH CONTACT
Feel free to send questions, comments, or kudos to the notmuch mailing
list <notmuch@notmuchmail.org> . Subscription is not required before
posting, but is available from the notmuchmail.org website.
Real-time interaction with the Notmuch community is available via IRC
(server: irc.freenode.net, channel: #notmuch).

View file

@ -1,48 +0,0 @@
.TH NOTMUCH-HOOKS 5 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-hooks \- hooks for notmuch
.SH SYNOPSIS
$DATABASEDIR/.notmuch/hooks/*
.SH DESCRIPTION
Hooks are scripts (or arbitrary executables or symlinks to such) that notmuch
invokes before and after certain actions. These scripts reside in
the .notmuch/hooks directory within the database directory and must have
executable permissions.
The currently available hooks are described below.
.RS 4
.TP 4
.B pre\-new
This hook is invoked by the
.B new
command before scanning or importing new messages into the database. If this
hook exits with a non-zero status, notmuch will abort further processing of the
.B new
command.
Typically this hook is used for fetching or delivering new mail to be imported
into the database.
.RE
.RS 4
.TP 4
.B post\-new
This hook is invoked by the
.B new
command after new messages have been imported into the database and initial tags
have been applied. The hook will not be run if there have been any errors during
the scan or import.
Typically this hook is used to perform additional query\-based tagging on the
imported messages.
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1), \fBnotmuch-search\fR(1),
\fBnotmuch-search-terms\fR(7), \fBnotmuch-show\fR(1),
\fBnotmuch-tag\fR(1)

View file

@ -1,269 +0,0 @@
.TH NOTMUCH-SEARCH-TERMS 7 2013-12-30 "Notmuch 0.17"
.SH NAME
notmuch-search-terms \- syntax for notmuch queries
.SH SYNOPSIS
.B notmuch count
.RI [ options... ]
.RI < search-term ">..."
.B "notmuch dump"
.RI "[ <" filename "> ] [--]"
.RI "[ <" search-term ">...]"
.B notmuch search
.RI [ options "...] <" search-term ">..."
.B notmuch show
.RI "[" options "...] <" search-term ">..."
.B notmuch tag
.RI "+<" tag> "|\-<" tag "> [...] [\-\-] <" search-term ">..."
.SH DESCRIPTION
Several notmuch commands accept a common syntax for search terms.
The search terms can consist of free-form text (and quoted phrases)
which will match all messages that contain all of the given
terms/phrases in the body, the subject, or any of the sender or
recipient headers.
As a special case, a search string consisting of exactly a single
asterisk ("*") will match all messages.
In addition to free text, the following prefixes can be used to force
terms to match against specific portions of an email, (where
<brackets> indicate user-supplied values):
from:<name-or-address>
to:<name-or-address>
subject:<word-or-quoted-phrase>
attachment:<word>
tag:<tag> (or is:<tag>)
id:<message-id>
thread:<thread-id>
folder:<directory-path>
date:<since>..<until>
The
.B from:
prefix is used to match the name or address of the sender of an email
message.
The
.B to:
prefix is used to match the names or addresses of any recipient of an
email message, (whether To, Cc, or Bcc).
Any term prefixed with
.B subject:
will match only text from the subject of an email. Searching for a
phrase in the subject is supported by including quotation marks around
the phrase, immediately following
.BR subject: .
The
.B attachment:
prefix can be used to search for specific filenames (or extensions) of
attachments to email messages.
For
.BR tag: " and " is:
valid tag values include
.BR inbox " and " unread
by default for new messages added by
.B notmuch new
as well as any other tag values added manually with
.BR "notmuch tag" .
For
.BR id: ,
message ID values are the literal contents of the Message\-ID: header
of email messages, but without the '<', '>' delimiters.
The
.B thread:
prefix can be used with the thread ID values that are generated
internally by notmuch (and do not appear in email messages). These
thread ID values can be seen in the first column of output from
.B "notmuch search"
The
.B folder:
prefix can be used to search for email message files that are
contained within particular directories within the mail store. If the
same email message has multiple message files associated with it, it's
sufficient for a match that at least one of the files is contained
within a matching directory. Only the directory components below the
top-level mail database path are available to be searched.
The
.B date:
prefix can be used to restrict the results to only messages within a
particular time range (based on the Date: header) with a range syntax
of:
date:<since>..<until>
See \fBDATE AND TIME SEARCH\fR below for details on the range
expression, and supported syntax for <since> and <until> date and time
expressions.
The time range can also be specified using timestamps with a syntax
of:
<initial-timestamp>..<final-timestamp>
Each timestamp is a number representing the number of seconds since
1970\-01\-01 00:00:00 UTC.
In addition to individual terms, multiple terms can be
combined with Boolean operators (
.BR and ", " or ", " not
, etc.). Each term in the query will be implicitly connected by a
logical AND if no explicit operator is provided, (except that terms
with a common prefix will be implicitly combined with OR until we get
Xapian defect #402 fixed).
Parentheses can also be used to control the combination of the Boolean
operators, but will have to be protected from interpretation by the
shell, (such as by putting quotation marks around any parenthesized
expression).
.SH DATE AND TIME SEARCH
notmuch understands a variety of standard and natural ways of
expressing dates and times, both in absolute terms ("2012-10-24") and
in relative terms ("yesterday"). Any number of relative terms can be
combined ("1 hour 25 minutes") and an absolute date/time can be
combined with relative terms to further adjust it. A non-exhaustive
description of the syntax supported for absolute and relative terms is
given below.
.RS 4
.TP 4
.B The range expression
date:<since>..<until>
The above expression restricts the results to only messages from
<since> to <until>, based on the Date: header.
<since> and <until> can describe imprecise times, such as "yesterday".
In this case, <since> is taken as the earliest time it could describe
(the beginning of yesterday) and <until> is taken as the latest time
it could describe (the end of yesterday). Similarly,
date:january..february matches from the beginning of January to the
end of February.
Currently, we do not support spaces in range expressions. You can
replace the spaces with '_', or (in most cases) '-', or (in some
cases) leave the spaces out altogether. Examples in this man page use
spaces for clarity.
Open-ended ranges are supported (since Xapian 1.2.1), i.e. it's
possible to specify date:..<until> or date:<since>.. to not limit the
start or end time, respectively. Pre-1.2.1 Xapian does not report an
error on open ended ranges, but it does not work as expected either.
Entering date:expr without ".." (for example date:yesterday) won't
work, as it's not interpreted as a range expression at all. You can
achieve the expected result by duplicating the expr both sides of ".."
(for example date:yesterday..yesterday).
.RE
.RS 4
.TP 4
.B Relative date and time
[N|number] (years|months|weeks|days|hours|hrs|minutes|mins|seconds|secs) [...]
All refer to past, can be repeated and will be accumulated.
Units can be abbreviated to any length, with the otherwise ambiguous
single m being m for minutes and M for months.
Number can also be written out one, two, ..., ten, dozen,
hundred. Additionally, the unit may be preceded by "last" or "this"
(e.g., "last week" or "this month").
When combined with absolute date and time, the relative date and time
specification will be relative from the specified absolute date and
time.
Examples: 5M2d, two weeks
.RE
.RS 4
.TP 4
.B Supported absolute time formats
H[H]:MM[:SS] [(am|a.m.|pm|p.m.)]
H[H] (am|a.m.|pm|p.m.)
HHMMSS
now
noon
midnight
Examples: 17:05, 5pm
.RE
.RS 4
.TP 4
.B Supported absolute date formats
YYYY-MM[-DD]
DD-MM[-[YY]YY]
MM-YYYY
M[M]/D[D][/[YY]YY]
M[M]/YYYY
D[D].M[M][.[YY]YY]
D[D][(st|nd|rd|th)] Mon[thname] [YYYY]
Mon[thname] D[D][(st|nd|rd|th)] [YYYY]
Wee[kday]
Month names can be abbreviated at three or more characters.
Weekday names can be abbreviated at three or more characters.
Examples: 2012-07-31, 31-07-2012, 7/31/2012, August 3
.RE
.RS 4
.TP 4
.B Time zones
(+|-)HH:MM
(+|-)HH[MM]
Some time zone codes, e.g. UTC, EET.
.RE
.SH SEE ALSO
\fBnotmuch\fR(1), \fBnotmuch-config\fR(1), \fBnotmuch-count\fR(1),
\fBnotmuch-dump\fR(1), \fBnotmuch-hooks\fR(5),
\fBnotmuch-insert\fR(1), \fBnotmuch-new\fR(1),
\fBnotmuch-reply\fR(1), \fBnotmuch-restore\fR(1),
\fBnotmuch-search\fR(1), \fBnotmuch-show\fR(1), \fBnotmuch-tag\fR(1)

View file

@ -441,5 +441,18 @@ mime_node_child (mime_node_t *parent, int child);
mime_node_t *
mime_node_seek_dfs (mime_node_t *node, int n);
typedef enum dump_formats {
DUMP_FORMAT_AUTO,
DUMP_FORMAT_BATCH_TAG,
DUMP_FORMAT_SUP
} dump_format_t;
int
notmuch_database_dump (notmuch_database_t *notmuch,
const char *output_file_name,
const char *query_str,
dump_format_t output_format,
notmuch_bool_t gzip_output);
#include "command-line-arguments.h"
#endif

View file

@ -32,7 +32,7 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[])
const char *path = notmuch_config_get_database_path (config);
const char *backup_path = NULL;
notmuch_status_t ret;
notmuch_bool_t quiet;
notmuch_bool_t quiet = FALSE;
int opt_index;
notmuch_opt_desc_t options[] = {
@ -42,7 +42,7 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[])
opt_index = parse_arguments (argc, argv, options, 1);
if (opt_index < 0)
return 1;
return EXIT_FAILURE;
if (! quiet)
printf ("Compacting database...\n");
@ -50,7 +50,7 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[])
quiet ? NULL : status_update_cb, NULL);
if (ret) {
fprintf (stderr, "Compaction failed: %s\n", notmuch_status_to_string (ret));
return 1;
return EXIT_FAILURE;
}
if (! quiet) {
@ -60,5 +60,5 @@ notmuch_compact_command (notmuch_config_t *config, int argc, char *argv[])
printf ("Done.\n");
}
return 0;
return EXIT_SUCCESS;
}

View file

@ -454,7 +454,7 @@ notmuch_config_save (notmuch_config_t *config)
}
/* Try not to overwrite symlinks. */
filename = realpath (config->filename, NULL);
filename = canonicalize_file_name (config->filename);
if (! filename) {
if (errno == ENOENT) {
filename = strdup (config->filename);
@ -496,6 +496,32 @@ notmuch_config_is_new (notmuch_config_t *config)
return config->is_new;
}
static const char *
_config_get (notmuch_config_t *config, char **field,
const char *group, const char *key)
{
/* read from config file and cache value, if not cached already */
if (*field == NULL) {
char *value;
value = g_key_file_get_string (config->key_file, group, key, NULL);
if (value) {
*field = talloc_strdup (config, value);
free (value);
}
}
return *field;
}
static void
_config_set (notmuch_config_t *config, char **field,
const char *group, const char *key, const char *value)
{
g_key_file_set_string (config->key_file, group, key, value);
/* drop the cached value */
talloc_free (*field);
*field = NULL;
}
static const char **
_config_get_list (notmuch_config_t *config,
@ -504,6 +530,7 @@ _config_get_list (notmuch_config_t *config,
{
assert(outlist);
/* read from config file and cache value, if not cached already */
if (*outlist == NULL) {
char **inlist = g_key_file_get_string_list (config->key_file,
@ -535,6 +562,8 @@ _config_set_list (notmuch_config_t *config,
size_t length, const char ***config_var )
{
g_key_file_set_string_list (config->key_file, group, name, list, length);
/* drop the cached value */
talloc_free (*config_var);
*config_var = NULL;
}
@ -542,85 +571,40 @@ _config_set_list (notmuch_config_t *config,
const char *
notmuch_config_get_database_path (notmuch_config_t *config)
{
char *path;
if (config->database_path == NULL) {
path = g_key_file_get_string (config->key_file,
"database", "path", NULL);
if (path) {
config->database_path = talloc_strdup (config, path);
free (path);
}
}
return config->database_path;
return _config_get (config, &config->database_path, "database", "path");
}
void
notmuch_config_set_database_path (notmuch_config_t *config,
const char *database_path)
{
g_key_file_set_string (config->key_file,
"database", "path", database_path);
talloc_free (config->database_path);
config->database_path = NULL;
_config_set (config, &config->database_path, "database", "path", database_path);
}
const char *
notmuch_config_get_user_name (notmuch_config_t *config)
{
char *name;
if (config->user_name == NULL) {
name = g_key_file_get_string (config->key_file,
"user", "name", NULL);
if (name) {
config->user_name = talloc_strdup (config, name);
free (name);
}
}
return config->user_name;
return _config_get (config, &config->user_name, "user", "name");
}
void
notmuch_config_set_user_name (notmuch_config_t *config,
const char *user_name)
{
g_key_file_set_string (config->key_file,
"user", "name", user_name);
talloc_free (config->user_name);
config->user_name = NULL;
_config_set (config, &config->user_name, "user", "name", user_name);
}
const char *
notmuch_config_get_user_primary_email (notmuch_config_t *config)
{
char *email;
if (config->user_primary_email == NULL) {
email = g_key_file_get_string (config->key_file,
"user", "primary_email", NULL);
if (email) {
config->user_primary_email = talloc_strdup (config, email);
free (email);
}
}
return config->user_primary_email;
return _config_get (config, &config->user_primary_email, "user", "primary_email");
}
void
notmuch_config_set_user_primary_email (notmuch_config_t *config,
const char *primary_email)
{
g_key_file_set_string (config->key_file,
"user", "primary_email", primary_email);
talloc_free (config->user_primary_email);
config->user_primary_email = NULL;
_config_set (config, &config->user_primary_email, "user", "primary_email", primary_email);
}
const char **
@ -839,34 +823,39 @@ notmuch_config_command_list (notmuch_config_t *config)
int
notmuch_config_command (notmuch_config_t *config, int argc, char *argv[])
{
int ret;
argc--; argv++; /* skip subcommand argument */
if (argc < 1) {
fprintf (stderr, "Error: notmuch config requires at least one argument.\n");
return 1;
return EXIT_FAILURE;
}
if (strcmp (argv[0], "get") == 0) {
if (argc != 2) {
fprintf (stderr, "Error: notmuch config get requires exactly "
"one argument.\n");
return 1;
return EXIT_FAILURE;
}
return notmuch_config_command_get (config, argv[1]);
ret = notmuch_config_command_get (config, argv[1]);
} else if (strcmp (argv[0], "set") == 0) {
if (argc < 2) {
fprintf (stderr, "Error: notmuch config set requires at least "
"one argument.\n");
return 1;
return EXIT_FAILURE;
}
return notmuch_config_command_set (config, argv[1], argc - 2, argv + 2);
ret = notmuch_config_command_set (config, argv[1], argc - 2, argv + 2);
} else if (strcmp (argv[0], "list") == 0) {
return notmuch_config_command_list (config);
}
ret = notmuch_config_command_list (config);
} else {
fprintf (stderr, "Unrecognized argument for notmuch config: %s\n",
argv[0]);
return 1;
return EXIT_FAILURE;
}
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
}
notmuch_bool_t

View file

@ -150,10 +150,8 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[])
};
opt_index = parse_arguments (argc, argv, options, 1);
if (opt_index < 0) {
return 1;
}
if (opt_index < 0)
return EXIT_FAILURE;
if (input_file_name) {
batch = TRUE;
@ -161,23 +159,23 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[])
if (input == NULL) {
fprintf (stderr, "Error opening %s for reading: %s\n",
input_file_name, strerror (errno));
return 1;
return EXIT_FAILURE;
}
}
if (batch && opt_index != argc) {
fprintf (stderr, "--batch and query string are not compatible\n");
return 1;
return EXIT_FAILURE;
}
if (notmuch_database_open (notmuch_config_get_database_path (config),
NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
return 1;
return EXIT_FAILURE;
query_str = query_string_from_args (config, argc-opt_index, argv+opt_index);
if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n");
return 1;
return EXIT_FAILURE;
}
if (exclude == EXCLUDE_TRUE) {
@ -197,5 +195,5 @@ notmuch_count_command (notmuch_config_t *config, int argc, char *argv[])
if (input != stdin)
fclose (input);
return ret;
return ret ? EXIT_FAILURE : EXIT_SUCCESS;
}

View file

@ -19,67 +19,27 @@
*/
#include "notmuch-client.h"
#include "dump-restore-private.h"
#include "hex-escape.h"
#include "string-util.h"
#include <zlib.h>
int
notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
static int
database_dump_file (notmuch_database_t *notmuch, gzFile output,
const char *query_str, int output_format)
{
notmuch_database_t *notmuch;
notmuch_query_t *query;
FILE *output = stdout;
notmuch_messages_t *messages;
notmuch_message_t *message;
notmuch_tags_t *tags;
const char *query_str = "";
if (notmuch_database_open (notmuch_config_get_database_path (config),
NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
return 1;
char *output_file_name = NULL;
int opt_index;
int output_format = DUMP_FORMAT_SUP;
notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, &output_format, "format", 'f',
(notmuch_keyword_t []){ { "sup", DUMP_FORMAT_SUP },
{ "batch-tag", DUMP_FORMAT_BATCH_TAG },
{ 0, 0 } } },
{ NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 },
{ 0, 0, 0, 0, 0 }
};
opt_index = parse_arguments (argc, argv, options, 1);
if (opt_index < 0) {
/* diagnostics already printed */
return 1;
}
if (output_file_name) {
output = fopen (output_file_name, "w");
if (output == NULL) {
fprintf (stderr, "Error opening %s for writing: %s\n",
output_file_name, strerror (errno));
return 1;
}
}
if (opt_index < argc) {
query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n");
return 1;
}
}
if (! query_str)
query_str = "";
query = notmuch_query_create (notmuch, query_str);
if (query == NULL) {
fprintf (stderr, "Out of memory\n");
return 1;
return EXIT_FAILURE;
}
/* Don't ask xapian to sort by Message-ID. Xapian optimizes returning the
* first results quickly at the expense of total time.
@ -111,7 +71,7 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
}
if (output_format == DUMP_FORMAT_SUP) {
fprintf (output, "%s (", message_id);
gzprintf (output, "%s (", message_id);
}
for (tags = notmuch_message_get_tags (message);
@ -120,43 +80,173 @@ notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
const char *tag_str = notmuch_tags_get (tags);
if (! first)
fputs (" ", output);
gzputs (output, " ");
first = 0;
if (output_format == DUMP_FORMAT_SUP) {
fputs (tag_str, output);
gzputs (output, tag_str);
} else {
if (hex_encode (notmuch, tag_str,
&buffer, &buffer_size) != HEX_SUCCESS) {
fprintf (stderr, "Error: failed to hex-encode tag %s\n",
tag_str);
return 1;
return EXIT_FAILURE;
}
fprintf (output, "+%s", buffer);
gzprintf (output, "+%s", buffer);
}
}
if (output_format == DUMP_FORMAT_SUP) {
fputs (")\n", output);
gzputs (output, ")\n");
} else {
if (make_boolean_term (notmuch, "id", message_id,
&buffer, &buffer_size)) {
fprintf (stderr, "Error quoting message id %s: %s\n",
message_id, strerror (errno));
return 1;
return EXIT_FAILURE;
}
fprintf (output, " -- %s\n", buffer);
gzprintf (output, " -- %s\n", buffer);
}
notmuch_message_destroy (message);
}
if (output != stdout)
fclose (output);
notmuch_query_destroy (query);
return EXIT_SUCCESS;
}
/* Dump database into output_file_name if it's non-NULL, stdout
* otherwise.
*/
int
notmuch_database_dump (notmuch_database_t *notmuch,
const char *output_file_name,
const char *query_str,
dump_format_t output_format,
notmuch_bool_t gzip_output)
{
gzFile output = NULL;
const char *mode = gzip_output ? "w9" : "wT";
const char *name_for_error = output_file_name ? output_file_name : "stdout";
char *tempname = NULL;
int outfd = -1;
int ret = -1;
if (output_file_name) {
tempname = talloc_asprintf (notmuch, "%s.XXXXXX", output_file_name);
outfd = mkstemp (tempname);
} else {
outfd = dup (STDOUT_FILENO);
}
if (outfd < 0) {
fprintf (stderr, "Bad output file %s\n", name_for_error);
goto DONE;
}
output = gzdopen (outfd, mode);
if (output == NULL) {
fprintf (stderr, "Error opening %s for (gzip) writing: %s\n",
name_for_error, strerror (errno));
if (close (outfd))
fprintf (stderr, "Error closing %s during shutdown: %s\n",
name_for_error, strerror (errno));
goto DONE;
}
ret = database_dump_file (notmuch, output, query_str, output_format);
if (ret) goto DONE;
ret = gzflush (output, Z_FINISH);
if (ret) {
fprintf (stderr, "Error flushing output: %s\n", gzerror (output, NULL));
goto DONE;
}
if (output_file_name) {
ret = fsync (outfd);
if (ret) {
fprintf (stderr, "Error syncing %s to disk: %s\n",
name_for_error, strerror (errno));
goto DONE;
}
}
if (gzclose_w (output) != Z_OK) {
fprintf (stderr, "Error closing %s: %s\n", name_for_error,
gzerror (output, NULL));
ret = EXIT_FAILURE;
output = NULL;
goto DONE;
}
if (output_file_name) {
ret = rename (tempname, output_file_name);
if (ret) {
fprintf (stderr, "Error renaming %s to %s: %s\n",
tempname, output_file_name, strerror (errno));
goto DONE;
}
}
DONE:
if (ret != EXIT_SUCCESS && output)
(void) gzclose_w (output);
if (ret != EXIT_SUCCESS && output_file_name)
(void) unlink (tempname);
return ret;
}
int
notmuch_dump_command (notmuch_config_t *config, int argc, char *argv[])
{
notmuch_database_t *notmuch;
const char *query_str = NULL;
int ret;
if (notmuch_database_open (notmuch_config_get_database_path (config),
NOTMUCH_DATABASE_MODE_READ_ONLY, &notmuch))
return EXIT_FAILURE;
char *output_file_name = NULL;
int opt_index;
int output_format = DUMP_FORMAT_BATCH_TAG;
notmuch_bool_t gzip_output = 0;
notmuch_opt_desc_t options[] = {
{ NOTMUCH_OPT_KEYWORD, &output_format, "format", 'f',
(notmuch_keyword_t []){ { "sup", DUMP_FORMAT_SUP },
{ "batch-tag", DUMP_FORMAT_BATCH_TAG },
{ 0, 0 } } },
{ NOTMUCH_OPT_STRING, &output_file_name, "output", 'o', 0 },
{ NOTMUCH_OPT_BOOLEAN, &gzip_output, "gzip", 'z', 0 },
{ 0, 0, 0, 0, 0 }
};
opt_index = parse_arguments (argc, argv, options, 1);
if (opt_index < 0)
return EXIT_FAILURE;
if (opt_index < argc) {
query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
if (query_str == NULL) {
fprintf (stderr, "Out of memory.\n");
return EXIT_FAILURE;
}
}
ret = notmuch_database_dump (notmuch, output_file_name, query_str,
output_format, gzip_output);
notmuch_database_destroy (notmuch);
return 0;
return ret;
}

View file

@ -295,7 +295,7 @@ copy_stdin (int fdin, int fdout)
* The file is renamed to encode notmuch tags as maildir flags. */
static void
add_file_to_database (notmuch_database_t *notmuch, const char *path,
tag_op_list_t *tag_ops)
tag_op_list_t *tag_ops, notmuch_bool_t synchronize_flags)
{
notmuch_message_t *message;
notmuch_status_t status;
@ -323,11 +323,15 @@ add_file_to_database (notmuch_database_t *notmuch, const char *path,
if (status == NOTMUCH_STATUS_DUPLICATE_MESSAGE_ID) {
/* Don't change tags of an existing message. */
if (synchronize_flags) {
status = notmuch_message_tags_to_maildir_flags (message);
if (status != NOTMUCH_STATUS_SUCCESS)
fprintf (stderr, "Error: failed to sync tags to maildir flags\n");
}
} else {
tag_op_list_apply (message, tag_ops, TAG_FLAG_MAILDIR_SYNC);
tag_op_flag_t flags = synchronize_flags ? TAG_FLAG_MAILDIR_SYNC : 0;
tag_op_list_apply (message, tag_ops, flags);
}
notmuch_message_destroy (message);
@ -335,7 +339,8 @@ add_file_to_database (notmuch_database_t *notmuch, const char *path,
static notmuch_bool_t
insert_message (void *ctx, notmuch_database_t *notmuch, int fdin,
const char *dir, tag_op_list_t *tag_ops)
const char *dir, tag_op_list_t *tag_ops,
notmuch_bool_t synchronize_flags)
{
char *tmppath;
char *newpath;
@ -377,7 +382,7 @@ insert_message (void *ctx, notmuch_database_t *notmuch, int fdin,
/* Even if adding the message to the notmuch database fails,
* the message is on disk and we consider the delivery completed. */
add_file_to_database (notmuch, newpath, tag_ops);
add_file_to_database (notmuch, newpath, tag_ops, synchronize_flags);
return TRUE;
@ -400,6 +405,7 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
char *query_string = NULL;
const char *folder = NULL;
notmuch_bool_t create_folder = FALSE;
notmuch_bool_t synchronize_flags;
const char *maildir;
int opt_index;
unsigned int i;
@ -412,32 +418,39 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
};
opt_index = parse_arguments (argc, argv, options, 1);
if (opt_index < 0) {
/* diagnostics already printed */
return 1;
}
if (opt_index < 0)
return EXIT_FAILURE;
db_path = notmuch_config_get_database_path (config);
new_tags = notmuch_config_get_new_tags (config, &new_tags_length);
synchronize_flags = notmuch_config_get_maildir_synchronize_flags (config);
tag_ops = tag_op_list_create (config);
if (tag_ops == NULL) {
fprintf (stderr, "Out of memory.\n");
return 1;
return EXIT_FAILURE;
}
for (i = 0; i < new_tags_length; i++) {
const char *error_msg;
error_msg = illegal_tag (new_tags[i], FALSE);
if (error_msg) {
fprintf (stderr, "Error: tag '%s' in new.tags: %s\n",
new_tags[i], error_msg);
return EXIT_FAILURE;
}
if (tag_op_list_append (tag_ops, new_tags[i], FALSE))
return 1;
return EXIT_FAILURE;
}
if (parse_tag_command_line (config, argc - opt_index, argv + opt_index,
&query_string, tag_ops))
return 1;
return EXIT_FAILURE;
if (*query_string != '\0') {
fprintf (stderr, "Error: unexpected query string: %s\n", query_string);
return 1;
return EXIT_FAILURE;
}
if (folder == NULL) {
@ -445,17 +458,17 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
} else {
if (! check_folder_name (folder)) {
fprintf (stderr, "Error: bad folder name: %s\n", folder);
return 1;
return EXIT_FAILURE;
}
maildir = talloc_asprintf (config, "%s/%s", db_path, folder);
if (! maildir) {
fprintf (stderr, "Out of memory\n");
return 1;
return EXIT_FAILURE;
}
if (create_folder && ! maildir_create_folder (config, maildir)) {
fprintf (stderr, "Error: creating maildir %s: %s\n",
maildir, strerror (errno));
return 1;
return EXIT_FAILURE;
}
}
@ -469,11 +482,12 @@ notmuch_insert_command (notmuch_config_t *config, int argc, char *argv[])
if (notmuch_database_open (notmuch_config_get_database_path (config),
NOTMUCH_DATABASE_MODE_READ_WRITE, &notmuch))
return 1;
return EXIT_FAILURE;
ret = insert_message (config, notmuch, STDIN_FILENO, maildir, tag_ops);
ret = insert_message (config, notmuch, STDIN_FILENO, maildir, tag_ops,
synchronize_flags);
notmuch_database_destroy (notmuch);
return (ret) ? 0 : 1;
return ret ? EXIT_SUCCESS : EXIT_FAILURE;
}

Some files were not shown because too many files have changed in this diff Show more