mirror of
https://git.notmuchmail.org/git/notmuch
synced 2025-03-13 19:15:15 +01:00
notmuch release 0.34.2-1 for unstable (sid) [dgit]
[dgit distro=debian no-split --quilt=linear] -----BEGIN PGP SIGNATURE----- iQIzBAABCAAdFiEEkiyHYXwaY0SiY6fqA0U5G1WqFSEFAmGzWWMACgkQA0U5G1Wq FSG8xg/+Jx1z1pqC9CAQpw1Ccb9OuYsniwj7rvhfutbS82zkuFFq2UdjHa3rP31a zABqN9b9VDUtsEE5Wyf+Md7SZ202NQK3sZ/vh///CHis12CLaMZkvpi+twi9f5pj pMzcwXJ5voaHxErDvvjIp3FV9+RsfpZl5owXpav8j6Hl05TZ4xHfopF0O9rSJQhK +KuUjb2zGO7clP78cEZhEn+hk5DmYf0obRDK7hHgL/n8FVB+pmUsa5riauW2u/Jm ozhL+sC3DjVspQLZ1SgHHBocfuLCOlMcp2sqLHEN9pqF7XcW3m0aq9GFfYlUNh1m kc8HpTxHI7hcQmS0zsURgqV2fNR9okS8ye0zaCMw5oJ7h4J36i5kbRj0TPfRW7h3 SKPuFGELPVqGj3NjUEhS34vgCcocmYmYqurAGKl1OnDouDT+ZpNMeg0EFrfhU/N3 jcRTYcw6IarFJ85h8uFPBqIXd0H8KoPvUTa85eS4a/wqcrxPZTgtuqu35mlpe+1U 77TyoQIT5AYa8c0N51a60jGK1ijY0/SBtMtb+hRxYuWLCU9wnWZEOBPakbqTVIdn +iBR4kYwQfhewqv2yLdCp/GsLqhQs2X79yBChWtcUc4WtM5B5QVlgU9TloyJJVHA 2HB6ZkJW3MlzIhdJ/naemCuoW3D5ApnXJzGIWJk1nsSyR/Gq66k= =lO2D -----END PGP SIGNATURE----- Merge tag 'debian/0.34.2-1' into debian/bullseye-backports notmuch release 0.34.2-1 for unstable (sid) [dgit] [dgit distro=debian no-split --quilt=linear]
This commit is contained in:
commit
1b58ea1e66
89 changed files with 3763 additions and 496 deletions
9
INSTALL
9
INSTALL
|
@ -41,6 +41,15 @@ Talloc, and zlib which are each described below:
|
|||
|
||||
GMime is available from https://github.com/jstedfast/gmime
|
||||
|
||||
Sfsexp
|
||||
------
|
||||
|
||||
sfsexp is the "small fast s-expression" library. Notmuch
|
||||
optionally use it to provide a second query parser.
|
||||
|
||||
sfsexp is available from https://github.com/mjsottile/sfsexp.
|
||||
In Debian Bookworm and later, install libsexp-dev.
|
||||
|
||||
Talloc
|
||||
------
|
||||
Talloc is a memory-pool allocator used by Notmuch.
|
||||
|
|
80
NEWS
80
NEWS
|
@ -1,3 +1,73 @@
|
|||
Notmuch 0.34.2 (2021-12-09)
|
||||
===========================
|
||||
|
||||
Library
|
||||
-------
|
||||
|
||||
Fix a bug that wrongly resolved conflict between the `database_path`
|
||||
parameter to `notmuch_database_open_with_config` and configuration
|
||||
item `database.path` in favour of the latter.
|
||||
|
||||
Python Bindings (notmuch2)
|
||||
--------------------------
|
||||
|
||||
When building the documentation for the `notmuch2` python module,
|
||||
import from the built module, not a system wide installed one.
|
||||
|
||||
The notmuch2.Database constructor now uses the library function
|
||||
`notmuch_database_open_with_config` to support the same configuration
|
||||
and database location options as the library does.
|
||||
|
||||
Fix some unprintable exception objects.
|
||||
|
||||
Notmuch 0.34.1 (2021-11-03)
|
||||
===========================
|
||||
|
||||
Library
|
||||
-------
|
||||
|
||||
Fix for deallocation and nulling of output parameter for
|
||||
notmuch_database_{open_with,create_with,load}_config when errors
|
||||
occur. This change fixes a potential use-after-free bug that has been
|
||||
present since 0.32. This release also improves the documentation of
|
||||
status returns for the same 3 functions.
|
||||
|
||||
Notmuch 0.34 (2021-10-20)
|
||||
=========================
|
||||
|
||||
General
|
||||
-------
|
||||
|
||||
An optional new s-expression based query parser is available if
|
||||
notmuch is built with the `sfsexp` library. See
|
||||
notmuch-sexp-queries(7) for syntax, and use `notmuch config get
|
||||
built_with.sexpr_query` to check if notmuch is compiled with
|
||||
s-expression query support.
|
||||
|
||||
CLI
|
||||
---
|
||||
|
||||
Support multiple `Delivered-To` headers in notmuch-reply(1).
|
||||
|
||||
Emacs
|
||||
-----
|
||||
|
||||
Functions are now allowed in `notmuch-search-result-format`.
|
||||
|
||||
Improvements to unthreaded view on large threads.
|
||||
|
||||
Tolerate bad/missing working directory for most commands.
|
||||
|
||||
Allow customization of tree drawing symbols in notmuch-tree mode.
|
||||
|
||||
Notmuch 0.33.2 (2021-09-30)
|
||||
===========================
|
||||
|
||||
Tests
|
||||
-----
|
||||
|
||||
Improve reliability of T355-smime by changing gpgsm initialization.
|
||||
|
||||
Notmuch 0.33.1 (2021-09-10)
|
||||
===========================
|
||||
|
||||
|
@ -14,7 +84,7 @@ Notmuch 0.33 (2021-09-03)
|
|||
Library
|
||||
-------
|
||||
|
||||
Correct documentatation about transactions.
|
||||
Correct documentation about transactions.
|
||||
|
||||
Add a configurable automatic commit of transactions. See
|
||||
`database.autocommit` in notmuch-config(1).
|
||||
|
@ -183,7 +253,7 @@ notmuch_database_remove_message or notmuch_message_delete in one
|
|||
session has been fixed.
|
||||
|
||||
As always, the canonical source of API documentation is
|
||||
`lib/notmuch.h`, or the doxygen formatted documentation in `notmuch(3)`
|
||||
`lib/notmuch.h`, or the doxygen formatted documentation in `notmuch(3)`.
|
||||
|
||||
CLI
|
||||
---
|
||||
|
@ -249,12 +319,12 @@ Fix for exclude tags in notmuch2 bindings.
|
|||
Build
|
||||
-----
|
||||
|
||||
Portability update for T360-symbol-hiding
|
||||
Portability update for T360-symbol-hiding.
|
||||
|
||||
Library
|
||||
-------
|
||||
|
||||
Fix for memory error in notmuch_database_get_config_list
|
||||
Fix for memory error in notmuch_database_get_config_list.
|
||||
|
||||
Notmuch 0.31.2 (2020-11-08)
|
||||
===========================
|
||||
|
@ -469,7 +539,7 @@ Command Line Interface
|
|||
----------------------
|
||||
|
||||
`notmuch show` now supports --body=false and --include-html with
|
||||
--format=text
|
||||
--format=text.
|
||||
|
||||
Fix several performance problems with `notmuch reindex`.
|
||||
|
||||
|
|
|
@ -53,6 +53,7 @@ ffibuilder.cdef(
|
|||
NOTMUCH_STATUS_NO_CONFIG,
|
||||
NOTMUCH_STATUS_NO_DATABASE,
|
||||
NOTMUCH_STATUS_DATABASE_EXISTS,
|
||||
NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
|
||||
NOTMUCH_STATUS_LAST_STATUS
|
||||
} notmuch_status_t;
|
||||
typedef enum {
|
||||
|
@ -102,20 +103,18 @@ ffibuilder.cdef(
|
|||
notmuch_status_to_string (notmuch_status_t status);
|
||||
|
||||
notmuch_status_t
|
||||
notmuch_database_create_verbose (const char *path,
|
||||
notmuch_database_t **database,
|
||||
char **error_message);
|
||||
notmuch_database_create_with_config (const char *database_path,
|
||||
const char *config_path,
|
||||
const char *profile,
|
||||
notmuch_database_t **database,
|
||||
char **error_message);
|
||||
notmuch_status_t
|
||||
notmuch_database_create (const char *path, notmuch_database_t **database);
|
||||
notmuch_status_t
|
||||
notmuch_database_open_verbose (const char *path,
|
||||
notmuch_database_mode_t mode,
|
||||
notmuch_database_t **database,
|
||||
char **error_message);
|
||||
notmuch_status_t
|
||||
notmuch_database_open (const char *path,
|
||||
notmuch_database_mode_t mode,
|
||||
notmuch_database_t **database);
|
||||
notmuch_database_open_with_config (const char *database_path,
|
||||
notmuch_database_mode_t mode,
|
||||
const char *config_path,
|
||||
const char *profile,
|
||||
notmuch_database_t **database,
|
||||
char **error_message);
|
||||
notmuch_status_t
|
||||
notmuch_database_close (notmuch_database_t *database);
|
||||
notmuch_status_t
|
||||
|
|
|
@ -31,6 +31,9 @@ class Mode(enum.Enum):
|
|||
READ_ONLY = capi.lib.NOTMUCH_DATABASE_MODE_READ_ONLY
|
||||
READ_WRITE = capi.lib.NOTMUCH_DATABASE_MODE_READ_WRITE
|
||||
|
||||
class ConfigFile(enum.Enum):
|
||||
EMPTY = b''
|
||||
SEARCH = capi.ffi.NULL
|
||||
|
||||
class QuerySortOrder(enum.Enum):
|
||||
OLDEST_FIRST = capi.lib.NOTMUCH_SORT_OLDEST_FIRST
|
||||
|
@ -71,6 +74,9 @@ class Database(base.NotmuchObject):
|
|||
:cvar EXCLUDE: Which messages to exclude from queries, ``TRUE``,
|
||||
``FLAG``, ``FALSE`` or ``ALL``. See the query documentation
|
||||
for details.
|
||||
:cvar CONFIG: Control loading of config file. Enumeration of
|
||||
``EMPTY`` (don't load a config file), and ``SEARCH`` (search as
|
||||
in :ref:`config_search`)
|
||||
:cvar AddedMessage: A namedtuple ``(msg, dup)`` used by
|
||||
:meth:`add` as return value.
|
||||
:cvar STR_MODE_MAP: A map mapping strings to :attr:`MODE` items.
|
||||
|
@ -81,9 +87,8 @@ class Database(base.NotmuchObject):
|
|||
still open.
|
||||
|
||||
:param path: The directory of where the database is stored. If
|
||||
``None`` the location will be read from the user's
|
||||
configuration file, respecting the ``NOTMUCH_CONFIG``
|
||||
environment variable if set.
|
||||
``None`` the location will be searched according to
|
||||
:ref:`database`
|
||||
:type path: str, bytes, os.PathLike or pathlib.Path
|
||||
:param mode: The mode to open the database in. One of
|
||||
:attr:`MODE.READ_ONLY` OR :attr:`MODE.READ_WRITE`. For
|
||||
|
@ -91,6 +96,8 @@ class Database(base.NotmuchObject):
|
|||
:attr:`MODE.READ_ONLY` and ``rw`` for :attr:`MODE.READ_WRITE`.
|
||||
:type mode: :attr:`MODE` or str.
|
||||
|
||||
:param config: Where to load the configuration from, if any.
|
||||
:type config: :attr:`CONFIG.EMPTY`, :attr:`CONFIG.SEARCH`, str, bytes, os.PathLike, pathlib.Path
|
||||
:raises KeyError: if an unknown mode string is used.
|
||||
:raises OSError: or subclasses if the configuration file can not
|
||||
be opened.
|
||||
|
@ -102,6 +109,7 @@ class Database(base.NotmuchObject):
|
|||
MODE = Mode
|
||||
SORT = QuerySortOrder
|
||||
EXCLUDE = QueryExclude
|
||||
CONFIG = ConfigFile
|
||||
AddedMessage = collections.namedtuple('AddedMessage', ['msg', 'dup'])
|
||||
_db_p = base.MemoryPointer()
|
||||
STR_MODE_MAP = {
|
||||
|
@ -109,18 +117,40 @@ class Database(base.NotmuchObject):
|
|||
'rw': MODE.READ_WRITE,
|
||||
}
|
||||
|
||||
def __init__(self, path=None, mode=MODE.READ_ONLY):
|
||||
@staticmethod
|
||||
def _cfg_path_encode(path):
|
||||
if isinstance(path,ConfigFile):
|
||||
path = path.value
|
||||
elif path is None:
|
||||
path = capi.ffi.NULL
|
||||
elif not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path):
|
||||
path = bytes(path)
|
||||
else:
|
||||
path = os.fsencode(path)
|
||||
return path
|
||||
|
||||
@staticmethod
|
||||
def _db_path_encode(path):
|
||||
if path is None:
|
||||
path = capi.ffi.NULL
|
||||
elif not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path):
|
||||
path = bytes(path)
|
||||
else:
|
||||
path = os.fsencode(path)
|
||||
return path
|
||||
|
||||
def __init__(self, path=None, mode=MODE.READ_ONLY, config=CONFIG.EMPTY):
|
||||
if isinstance(mode, str):
|
||||
mode = self.STR_MODE_MAP[mode]
|
||||
self.mode = mode
|
||||
if path is None:
|
||||
path = self.default_path()
|
||||
if not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path):
|
||||
path = bytes(path)
|
||||
|
||||
db_pp = capi.ffi.new('notmuch_database_t **')
|
||||
cmsg = capi.ffi.new('char**')
|
||||
ret = capi.lib.notmuch_database_open_verbose(os.fsencode(path),
|
||||
mode.value, db_pp, cmsg)
|
||||
ret = capi.lib.notmuch_database_open_with_config(self._db_path_encode(path),
|
||||
mode.value,
|
||||
self._cfg_path_encode(config),
|
||||
capi.ffi.NULL,
|
||||
db_pp, cmsg)
|
||||
if cmsg[0]:
|
||||
msg = capi.ffi.string(cmsg[0]).decode(errors='replace')
|
||||
capi.lib.free(cmsg[0])
|
||||
|
@ -132,18 +162,20 @@ class Database(base.NotmuchObject):
|
|||
self.closed = False
|
||||
|
||||
@classmethod
|
||||
def create(cls, path=None):
|
||||
def create(cls, path=None, config=ConfigFile.EMPTY):
|
||||
"""Create and open database in READ_WRITE mode.
|
||||
|
||||
This is creates a new notmuch database and returns an opened
|
||||
instance in :attr:`MODE.READ_WRITE` mode.
|
||||
|
||||
:param path: The directory of where the database is stored. If
|
||||
``None`` the location will be read from the user's
|
||||
configuration file, respecting the ``NOTMUCH_CONFIG``
|
||||
environment variable if set.
|
||||
:param path: The directory of where the database is stored.
|
||||
If ``None`` the location will be read searched by the
|
||||
notmuch library (see notmuch(3)::notmuch_open_with_config).
|
||||
:type path: str, bytes or os.PathLike
|
||||
|
||||
:param config: The pathname of the notmuch configuration file.
|
||||
:type config: :attr:`CONFIG.EMPTY`, :attr:`CONFIG.SEARCH`, str, bytes, os.PathLike, pathlib.Path
|
||||
|
||||
:raises OSError: or subclasses if the configuration file can not
|
||||
be opened.
|
||||
:raises configparser.Error: or subclasses if the configuration
|
||||
|
@ -154,14 +186,13 @@ class Database(base.NotmuchObject):
|
|||
|
||||
:returns: The newly created instance.
|
||||
"""
|
||||
if path is None:
|
||||
path = cls.default_path()
|
||||
if not hasattr(os, 'PathLike') and isinstance(path, pathlib.Path):
|
||||
path = bytes(path)
|
||||
|
||||
db_pp = capi.ffi.new('notmuch_database_t **')
|
||||
cmsg = capi.ffi.new('char**')
|
||||
ret = capi.lib.notmuch_database_create_verbose(os.fsencode(path),
|
||||
db_pp, cmsg)
|
||||
ret = capi.lib.notmuch_database_create_with_config(cls._db_path_encode(path),
|
||||
cls._cfg_path_encode(config),
|
||||
capi.ffi.NULL,
|
||||
db_pp, cmsg)
|
||||
if cmsg[0]:
|
||||
msg = capi.ffi.string(cmsg[0]).decode(errors='replace')
|
||||
capi.lib.free(cmsg[0])
|
||||
|
@ -176,7 +207,7 @@ class Database(base.NotmuchObject):
|
|||
ret = capi.lib.notmuch_database_destroy(db_pp[0])
|
||||
if ret != capi.lib.NOTMUCH_STATUS_SUCCESS:
|
||||
raise errors.NotmuchError(ret)
|
||||
return cls(path, cls.MODE.READ_WRITE)
|
||||
return cls(path, cls.MODE.READ_WRITE, config=config)
|
||||
|
||||
@staticmethod
|
||||
def default_path(cfg_path=None):
|
||||
|
@ -187,8 +218,8 @@ class Database(base.NotmuchObject):
|
|||
|
||||
:param cfg_path: The pathname of the notmuch configuration file.
|
||||
If not specified tries to use the pathname provided in the
|
||||
:env:`NOTMUCH_CONFIG` environment variable and falls back
|
||||
to :file:`~/.notmuch-config.
|
||||
:envvar:`NOTMUCH_CONFIG` environment variable and falls back
|
||||
to :file:`~/.notmuch-config`.
|
||||
:type cfg_path: str, bytes, os.PathLike or pathlib.Path.
|
||||
|
||||
:returns: The path of the database, which does not necessarily
|
||||
|
@ -198,8 +229,11 @@ class Database(base.NotmuchObject):
|
|||
be opened.
|
||||
:raises configparser.Error: or subclasses if the configuration
|
||||
file can not be parsed.
|
||||
:raises NotmuchError if the config file does not have the
|
||||
:raises NotmuchError: if the config file does not have the
|
||||
database.path setting.
|
||||
|
||||
.. deprecated:: 0.35
|
||||
Use the ``config`` parameter to :meth:`__init__` or :meth:`__create__` instead.
|
||||
"""
|
||||
if not cfg_path:
|
||||
cfg_path = _config_pathname()
|
||||
|
|
|
@ -56,6 +56,8 @@ class NotmuchError(Exception):
|
|||
NoDatabaseError,
|
||||
capi.lib.NOTMUCH_STATUS_DATABASE_EXISTS:
|
||||
DatabaseExistsError,
|
||||
capi.lib.NOTMUCH_STATUS_BAD_QUERY_SYNTAX:
|
||||
QuerySyntaxError,
|
||||
}
|
||||
return types[status]
|
||||
|
||||
|
@ -81,7 +83,8 @@ class NotmuchError(Exception):
|
|||
if self.message:
|
||||
return self.message
|
||||
elif self.status:
|
||||
return capi.lib.notmuch_status_to_string(self.status)
|
||||
char_str = capi.lib.notmuch_status_to_string(self.status)
|
||||
return capi.ffi.string(char_str).decode(errors='replace')
|
||||
else:
|
||||
return 'Unknown error'
|
||||
|
||||
|
@ -103,6 +106,7 @@ class IllegalArgumentError(NotmuchError): pass
|
|||
class NoConfigError(NotmuchError): pass
|
||||
class NoDatabaseError(NotmuchError): pass
|
||||
class DatabaseExistsError(NotmuchError): pass
|
||||
class QuerySyntaxError(NotmuchError): pass
|
||||
|
||||
class ObjectDestroyedError(NotmuchError):
|
||||
"""The object has already been destroyed and it's memory freed.
|
||||
|
|
8
bindings/python-cffi/tests/test_errors.py
Normal file
8
bindings/python-cffi/tests/test_errors.py
Normal file
|
@ -0,0 +1,8 @@
|
|||
from notmuch2 import _capi as capi
|
||||
from notmuch2 import _errors as errors
|
||||
|
||||
def test_status_no_message():
|
||||
exc = errors.NotmuchError(capi.lib.NOTMUCH_STATUS_PATH_ERROR)
|
||||
assert exc.status == capi.lib.NOTMUCH_STATUS_PATH_ERROR
|
||||
assert exc.message is None
|
||||
assert str(exc) == 'Path supplied is illegal for this function'
|
|
@ -1 +1 @@
|
|||
0.33.1
|
||||
0.34.2
|
||||
|
|
|
@ -1,3 +1,3 @@
|
|||
# this file should be kept in sync with ../../../version
|
||||
__VERSION__ = '0.33.1'
|
||||
__VERSION__ = '0.34.2'
|
||||
SOVERSION = '5'
|
||||
|
|
172
configure
vendored
172
configure
vendored
|
@ -496,16 +496,16 @@ int main () {
|
|||
}
|
||||
EOF
|
||||
if ! TEMP_GPG=$(mktemp -d "${TMPDIR:-/tmp}/notmuch.XXXXXX"); then
|
||||
printf 'No.\nCould not make tempdir for testing session-key support.\n'
|
||||
errors=$((errors + 1))
|
||||
printf 'No.\nCould not make tempdir for testing session-key support.\n'
|
||||
errors=$((errors + 1))
|
||||
elif ${CC} ${CFLAGS} ${gmime_cflags} _check_session_keys.c ${gmime_ldflags} -o _check_session_keys \
|
||||
&& GNUPGHOME=${TEMP_GPG} gpg --batch --quiet --import < "$srcdir"/test/gnupg-secret-key.asc \
|
||||
&& SESSION_KEY=$(GNUPGHOME=${TEMP_GPG} ./_check_session_keys) \
|
||||
&& [ $SESSION_KEY = 9:0BACD64099D1468AB07C796F0C0AC4851948A658A15B34E803865E9FC635F2F5 ]
|
||||
&& GNUPGHOME=${TEMP_GPG} gpg --batch --quiet --import < "$srcdir"/test/gnupg-secret-key.asc \
|
||||
&& SESSION_KEY=$(GNUPGHOME=${TEMP_GPG} ./_check_session_keys) \
|
||||
&& [ $SESSION_KEY = 9:0BACD64099D1468AB07C796F0C0AC4851948A658A15B34E803865E9FC635F2F5 ]
|
||||
then
|
||||
printf "OK.\n"
|
||||
printf "OK.\n"
|
||||
else
|
||||
cat <<EOF
|
||||
cat <<EOF
|
||||
No.
|
||||
*** Error: Could not extract session keys from encrypted message.
|
||||
|
||||
|
@ -515,15 +515,15 @@ version of GPGME.
|
|||
Please try to rebuild your version of GMime against a more recent
|
||||
version of GPGME (at least GPGME 1.8.0).
|
||||
EOF
|
||||
if command -v gpgme-config >/dev/null; then
|
||||
printf 'Your current GPGME development version is: %s\n' "$(gpgme-config --version)"
|
||||
else
|
||||
printf 'You do not have the GPGME development libraries installed.\n'
|
||||
fi
|
||||
errors=$((errors + 1))
|
||||
if command -v gpgme-config >/dev/null; then
|
||||
printf 'Your current GPGME development version is: %s\n' "$(gpgme-config --version)"
|
||||
else
|
||||
printf 'You do not have the GPGME development libraries installed.\n'
|
||||
fi
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
if [ -n "$TEMP_GPG" -a -d "$TEMP_GPG" ]; then
|
||||
rm -rf "$TEMP_GPG"
|
||||
rm -rf "$TEMP_GPG"
|
||||
fi
|
||||
|
||||
# see https://github.com/jstedfast/gmime/pull/90
|
||||
|
@ -570,36 +570,36 @@ int main () {
|
|||
}
|
||||
EOF
|
||||
if ! TEMP_GPG=$(mktemp -d "${TMPDIR:-/tmp}/notmuch.XXXXXX"); then
|
||||
printf 'No.\nCould not make tempdir for testing X.509 certificate validity support.\n'
|
||||
errors=$((errors + 1))
|
||||
printf 'No.\nCould not make tempdir for testing X.509 certificate validity support.\n'
|
||||
errors=$((errors + 1))
|
||||
elif ${CC} ${CFLAGS} ${gmime_cflags} _check_x509_validity.c ${gmime_ldflags} -o _check_x509_validity \
|
||||
&& echo disable-crl-checks > "$TEMP_GPG/gpgsm.conf" \
|
||||
&& echo "4D:E0:FF:63:C0:E9:EC:01:29:11:C8:7A:EE:DA:3A:9A:7F:6E:C1:0D S" >> "$TEMP_GPG/trustlist.txt" \
|
||||
&& GNUPGHOME=${TEMP_GPG} gpgsm --batch --quiet --import < "$srcdir"/test/smime/ca.crt
|
||||
&& echo disable-crl-checks > "$TEMP_GPG/gpgsm.conf" \
|
||||
&& echo "4D:E0:FF:63:C0:E9:EC:01:29:11:C8:7A:EE:DA:3A:9A:7F:6E:C1:0D S" >> "$TEMP_GPG/trustlist.txt" \
|
||||
&& GNUPGHOME=${TEMP_GPG} gpgsm --batch --quiet --import < "$srcdir"/test/smime/ca.crt
|
||||
then
|
||||
if GNUPGHOME=${TEMP_GPG} ./_check_x509_validity; then
|
||||
gmime_x509_cert_validity=1
|
||||
printf "Yes.\n"
|
||||
else
|
||||
gmime_x509_cert_validity=0
|
||||
printf "No.\n"
|
||||
if pkg-config --exists "gmime-3.0 >= 3.2.7"; then
|
||||
cat <<EOF
|
||||
if GNUPGHOME=${TEMP_GPG} ./_check_x509_validity; then
|
||||
gmime_x509_cert_validity=1
|
||||
printf "Yes.\n"
|
||||
else
|
||||
gmime_x509_cert_validity=0
|
||||
printf "No.\n"
|
||||
if pkg-config --exists "gmime-3.0 >= 3.2.7"; then
|
||||
cat <<EOF
|
||||
*** Error: GMime fails to calculate X.509 certificate validity, and
|
||||
is later than 3.2.7, which should have fixed this issue.
|
||||
|
||||
Please follow up on https://github.com/jstedfast/gmime/pull/90 with
|
||||
more details.
|
||||
EOF
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
fi
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
fi
|
||||
else
|
||||
printf 'No.\nFailed to set up gpgsm for testing X.509 certificate validity support.\n'
|
||||
errors=$((errors + 1))
|
||||
printf 'No.\nFailed to set up gpgsm for testing X.509 certificate validity support.\n'
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
if [ -n "$TEMP_GPG" -a -d "$TEMP_GPG" ]; then
|
||||
rm -rf "$TEMP_GPG"
|
||||
rm -rf "$TEMP_GPG"
|
||||
fi
|
||||
|
||||
# see https://dev.gnupg.org/T3464
|
||||
|
@ -647,31 +647,31 @@ int main () {
|
|||
}
|
||||
EOF
|
||||
if ! TEMP_GPG=$(mktemp -d "${TMPDIR:-/tmp}/notmuch.XXXXXX"); then
|
||||
printf 'No.\nCould not make tempdir for testing signature verification when decrypting with session keys.\n'
|
||||
errors=$((errors + 1))
|
||||
printf 'No.\nCould not make tempdir for testing signature verification when decrypting with session keys.\n'
|
||||
errors=$((errors + 1))
|
||||
elif ${CC} ${CFLAGS} ${gmime_cflags} _verify_sig_with_session_key.c ${gmime_ldflags} -o _verify_sig_with_session_key \
|
||||
&& GNUPGHOME=${TEMP_GPG} gpg --batch --quiet --import < "$srcdir"/test/gnupg-secret-key.asc \
|
||||
&& rm -f ${TEMP_GPG}/private-keys-v1.d/*.key
|
||||
&& GNUPGHOME=${TEMP_GPG} gpg --batch --quiet --import < "$srcdir"/test/gnupg-secret-key.asc \
|
||||
&& rm -f ${TEMP_GPG}/private-keys-v1.d/*.key
|
||||
then
|
||||
if GNUPGHOME=${TEMP_GPG} ./_verify_sig_with_session_key; then
|
||||
gmime_verify_with_session_key=1
|
||||
printf "Yes.\n"
|
||||
else
|
||||
gmime_verify_with_session_key=0
|
||||
printf "No.\n"
|
||||
cat <<EOF
|
||||
if GNUPGHOME=${TEMP_GPG} ./_verify_sig_with_session_key; then
|
||||
gmime_verify_with_session_key=1
|
||||
printf "Yes.\n"
|
||||
else
|
||||
gmime_verify_with_session_key=0
|
||||
printf "No.\n"
|
||||
cat <<EOF
|
||||
*** Error: GMime fails to verify signatures when decrypting with a session key.
|
||||
|
||||
This is most likely due to a buggy version of GPGME, which should be fixed in 1.13.2 or later.
|
||||
See https://dev.gnupg.org/T3464 for more details.
|
||||
EOF
|
||||
fi
|
||||
fi
|
||||
else
|
||||
printf 'No.\nFailed to set up gpg for testing signature verification while decrypting with a session key.\n'
|
||||
errors=$((errors + 1))
|
||||
printf 'No.\nFailed to set up gpg for testing signature verification while decrypting with a session key.\n'
|
||||
errors=$((errors + 1))
|
||||
fi
|
||||
if [ -n "$TEMP_GPG" -a -d "$TEMP_GPG" ]; then
|
||||
rm -rf "$TEMP_GPG"
|
||||
rm -rf "$TEMP_GPG"
|
||||
fi
|
||||
else
|
||||
have_gmime=0
|
||||
|
@ -768,24 +768,40 @@ have_python3=0
|
|||
if [ $have_python -eq 1 ]; then
|
||||
printf "Checking for python3 (>= 3.5)..."
|
||||
if "$python" -c 'import sys, sysconfig; assert sys.version_info >= (3,5)'; >/dev/null 2>&1; then
|
||||
printf "Yes.\n"
|
||||
have_python3=1
|
||||
printf "Yes.\n"
|
||||
have_python3=1
|
||||
else
|
||||
printf "No (will not install CFFI-based python bindings).\n"
|
||||
printf "No (will not install CFFI-based python bindings).\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
have_python3_dev=0
|
||||
if [ $have_python3 -eq 1 ]; then
|
||||
printf "Checking for python3 version ..."
|
||||
python3_version=$("$python" -c 'import sysconfig; print(sysconfig.get_python_version());')
|
||||
printf "(%s)\n" $python3_version
|
||||
|
||||
printf "Checking for python $python3_version development files..."
|
||||
if pkg-config --exists "python-$python3_version"; then
|
||||
have_python3_dev=1
|
||||
printf "Yes.\n"
|
||||
else
|
||||
have_python3_dev=0
|
||||
printf "No (will not install CFFI-based python bindings).\n"
|
||||
fi
|
||||
fi
|
||||
|
||||
have_python3_cffi=0
|
||||
have_python3_pytest=0
|
||||
if [ $have_python3 -eq 1 ]; then
|
||||
if [ $have_python3_dev -eq 1 ]; then
|
||||
printf "Checking for python3 cffi and setuptools... "
|
||||
if "$python" -c 'import cffi,setuptools; cffi.FFI().verify()' >/dev/null 2>&1; then
|
||||
printf "Yes.\n"
|
||||
have_python3_cffi=1
|
||||
WITH_PYTHON_DOCS=1
|
||||
printf "Yes.\n"
|
||||
have_python3_cffi=1
|
||||
WITH_PYTHON_DOCS=1
|
||||
else
|
||||
WITH_PYTHON_DOCS=0
|
||||
printf "No (will not install CFFI-based python bindings).\n"
|
||||
WITH_PYTHON_DOCS=0
|
||||
printf "No (will not install CFFI-based python bindings).\n"
|
||||
fi
|
||||
rm -rf __pycache__ # cffi.FFI().verify() uses this space
|
||||
|
||||
|
@ -793,10 +809,10 @@ if [ $have_python3 -eq 1 ]; then
|
|||
conf=$(mktemp)
|
||||
printf "[pytest]\nminversion=3.0\n" > $conf
|
||||
if "$python" -m pytest -c $conf --version >/dev/null 2>&1; then
|
||||
printf "Yes.\n"
|
||||
have_python3_pytest=1
|
||||
printf "Yes.\n"
|
||||
have_python3_pytest=1
|
||||
else
|
||||
printf "No (will not test CFFI-based python bindings).\n"
|
||||
printf "No (will not test CFFI-based python bindings).\n"
|
||||
fi
|
||||
rm -f $conf
|
||||
fi
|
||||
|
@ -820,6 +836,19 @@ else
|
|||
WITH_BASH=0
|
||||
fi
|
||||
|
||||
printf "Checking for sfsexp... "
|
||||
if pkg-config --exists sfsexp; then
|
||||
printf "Yes.\n"
|
||||
have_sfsexp=1
|
||||
sfsexp_cflags=$(pkg-config --cflags sfsexp)
|
||||
sfsexp_ldflags=$(pkg-config --libs sfsexp)
|
||||
else
|
||||
printf "No (will not enable s-expression queries).\n"
|
||||
have_sfsexp=0
|
||||
sfsexp_cflags=
|
||||
sfsexp_ldflags=
|
||||
fi
|
||||
|
||||
if [ -z "${EMACSLISPDIR-}" ]; then
|
||||
EMACSLISPDIR="\$(prefix)/share/emacs/site-lisp"
|
||||
fi
|
||||
|
@ -831,10 +860,10 @@ fi
|
|||
if [ $WITH_EMACS = "1" ]; then
|
||||
printf "Checking if emacs (>= 25) is available... "
|
||||
if emacs --quick --batch --eval '(if (< emacs-major-version 25) (kill-emacs 1))' > /dev/null 2>&1; then
|
||||
printf "Yes.\n"
|
||||
printf "Yes.\n"
|
||||
else
|
||||
printf "No (disabling emacs related parts of build)\n"
|
||||
WITH_EMACS=0
|
||||
printf "No (disabling emacs related parts of build)\n"
|
||||
WITH_EMACS=0
|
||||
fi
|
||||
fi
|
||||
|
||||
|
@ -1443,6 +1472,13 @@ HAVE_VALGRIND = ${have_valgrind}
|
|||
# And if so, flags needed at compile time for valgrind macros
|
||||
VALGRIND_CFLAGS = ${valgrind_cflags}
|
||||
|
||||
# Whether the sfsexp library is available
|
||||
HAVE_SFSEXP = ${have_sfsexp}
|
||||
|
||||
# And if so, flags needed at compile/link time for sfsexp
|
||||
SFSEXP_CFLAGS = ${sfsexp_cflags}
|
||||
SFSEXP_LDFLAGS = ${sfsexp_ldflags}
|
||||
|
||||
# Support for emacs
|
||||
WITH_EMACS = ${WITH_EMACS}
|
||||
|
||||
|
@ -1459,6 +1495,7 @@ WITH_ZSH = ${WITH_ZSH}
|
|||
COMMON_CONFIGURE_CFLAGS = \\
|
||||
\$(GMIME_CFLAGS) \$(TALLOC_CFLAGS) \$(ZLIB_CFLAGS) \\
|
||||
-DHAVE_VALGRIND=\$(HAVE_VALGRIND) \$(VALGRIND_CFLAGS) \\
|
||||
-DHAVE_SFSEXP=\$(HAVE_SFSEXP) \$(SFSEXP_CFLAGS) \\
|
||||
-DHAVE_GETLINE=\$(HAVE_GETLINE) \\
|
||||
-DWITH_EMACS=\$(WITH_EMACS) \\
|
||||
-DHAVE_CANONICALIZE_FILE_NAME=\$(HAVE_CANONICALIZE_FILE_NAME) \\
|
||||
|
@ -1475,7 +1512,7 @@ CONFIGURE_CFLAGS = \$(COMMON_CONFIGURE_CFLAGS)
|
|||
|
||||
CONFIGURE_CXXFLAGS = \$(COMMON_CONFIGURE_CFLAGS) \$(XAPIAN_CXXFLAGS)
|
||||
|
||||
CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS)
|
||||
CONFIGURE_LDFLAGS = \$(GMIME_LDFLAGS) \$(TALLOC_LDFLAGS) \$(ZLIB_LDFLAGS) \$(XAPIAN_LDFLAGS) \$(SFSEXP_LDFLAGS)
|
||||
EOF
|
||||
|
||||
# construct the sh.config
|
||||
|
@ -1524,6 +1561,9 @@ NOTMUCH_HAVE_PYTHON3_CFFI=${have_python3_cffi}
|
|||
# Is the python pytest package available?
|
||||
NOTMUCH_HAVE_PYTHON3_PYTEST=${have_python3_pytest}
|
||||
|
||||
# Is the sfsexp library available?
|
||||
NOTMUCH_HAVE_SFSEXP=${have_sfsexp}
|
||||
|
||||
# Platform we are run on
|
||||
PLATFORM=${platform}
|
||||
EOF
|
||||
|
@ -1531,10 +1571,10 @@ EOF
|
|||
{
|
||||
echo "# Generated by configure, run from doc/conf.py"
|
||||
if [ $WITH_EMACS = "1" ]; then
|
||||
echo "tags.add('WITH_EMACS')"
|
||||
echo "tags.add('WITH_EMACS')"
|
||||
fi
|
||||
if [ $WITH_PYTHON_DOCS = "1" ]; then
|
||||
echo "tags.add('WITH_PYTHON')"
|
||||
echo "tags.add('WITH_PYTHON')"
|
||||
fi
|
||||
printf "rsti_dir = '%s'\n" "$(cd emacs && pwd -P)"
|
||||
} > sphinx.config
|
||||
|
|
41
debian/changelog
vendored
41
debian/changelog
vendored
|
@ -1,3 +1,44 @@
|
|||
notmuch (0.34.2-1) unstable; urgency=medium
|
||||
|
||||
* New upstream bugfix with release, with fixes database location in
|
||||
library and notmuch2 python module.
|
||||
* Build only against the default version of python, to avoid including
|
||||
multiple .abi3.so files in python3-notmuch2
|
||||
|
||||
-- David Bremner <bremner@debian.org> Fri, 10 Dec 2021 09:35:43 -0400
|
||||
|
||||
notmuch (0.34.1-1) unstable; urgency=medium
|
||||
|
||||
* New upstream bugfix release. Fixes a memory deallocation error in
|
||||
libnotmuch.
|
||||
|
||||
-- David Bremner <bremner@debian.org> Wed, 03 Nov 2021 10:20:33 -0300
|
||||
|
||||
notmuch (0.34-1) unstable; urgency=medium
|
||||
|
||||
* New upstream release
|
||||
* Adds s-expression based query parser (man notmuch-sexp-queries).
|
||||
* Bug fix: "notmuch breaks on directory removal", thanks to Joerg
|
||||
Jaspert (Closes: #922536).
|
||||
* Respect notmuch-show-text/html-blocked-images for renderer w3m
|
||||
(Closes: #934082).
|
||||
* Bug fix: "add an option to change the database path", thanks to
|
||||
Michael Gold (Closes: #887041) (actually fixed in 0.32)
|
||||
|
||||
-- David Bremner <bremner@debian.org> Wed, 20 Oct 2021 11:15:23 -0300
|
||||
|
||||
notmuch (0.34~rc0-1) experimental; urgency=medium
|
||||
|
||||
* New upstream release candidate
|
||||
|
||||
-- David Bremner <bremner@debian.org> Fri, 15 Oct 2021 08:50:57 -0300
|
||||
|
||||
notmuch (0.33.2-1) unstable; urgency=medium
|
||||
|
||||
* Upstream fix for flaky/hanging tests in T355-smime
|
||||
|
||||
-- David Bremner <bremner@debian.org> Thu, 30 Sep 2021 08:27:10 -0300
|
||||
|
||||
notmuch (0.33.1-1~bpo11+1) bullseye-backports; urgency=medium
|
||||
|
||||
* Rebuild for bullseye-backports.
|
||||
|
|
5
debian/control
vendored
5
debian/control
vendored
|
@ -23,12 +23,13 @@ Build-Depends:
|
|||
gnupg <!nocheck>,
|
||||
gpgsm <!nocheck>,
|
||||
libgmime-3.0-dev (>= 3.0.3~),
|
||||
libpython3-all-dev,
|
||||
libpython3-dev,
|
||||
libsexp-dev,
|
||||
libtalloc-dev,
|
||||
libxapian-dev,
|
||||
libz-dev,
|
||||
pkg-config,
|
||||
python3-all (>= 3.1.2-7~),
|
||||
python3,
|
||||
python3-cffi,
|
||||
python3-pytest,
|
||||
python3-pytest-cov,
|
||||
|
|
1
debian/libnotmuch5.symbols
vendored
1
debian/libnotmuch5.symbols
vendored
|
@ -114,6 +114,7 @@ libnotmuch.so.5 libnotmuch5 #MINVER#
|
|||
notmuch_query_count_threads@Base 0.10~rc1
|
||||
notmuch_query_count_threads_st@Base 0.21~rc1
|
||||
notmuch_query_create@Base 0.3
|
||||
notmuch_query_create_with_syntax@Base 0.34~rc0
|
||||
notmuch_query_destroy@Base 0.3
|
||||
notmuch_query_get_database@Base 0.21~rc1
|
||||
notmuch_query_get_query_string@Base 0.4
|
||||
|
|
1
debian/notmuch.manpages
vendored
1
debian/notmuch.manpages
vendored
|
@ -17,3 +17,4 @@ usr/share/man/man1/notmuch.1.gz
|
|||
usr/share/man/man5/notmuch-hooks.5.gz
|
||||
usr/share/man/man7/notmuch-properties.7.gz
|
||||
usr/share/man/man7/notmuch-search-terms.7.gz
|
||||
usr/share/man/man7/notmuch-sexp-queries.7.gz
|
||||
|
|
31
devel/check-notmuch-commit
Executable file
31
devel/check-notmuch-commit
Executable file
|
@ -0,0 +1,31 @@
|
|||
#!/bin/sh
|
||||
|
||||
# Usage suggestion:
|
||||
# git rebase -i --exec devel/check-notmuch-commit origin/master
|
||||
|
||||
set -e
|
||||
|
||||
quick=0
|
||||
case "$1" in
|
||||
-q|-Q|--quick)
|
||||
quick=1
|
||||
;;
|
||||
esac
|
||||
|
||||
if [ $quick = 0 ]; then
|
||||
make test
|
||||
fi
|
||||
|
||||
unset uconf
|
||||
for file in $(git diff --name-only --diff-filter=AM HEAD^); do
|
||||
case $file in
|
||||
*.c|*.h|*.cc|*.hh)
|
||||
uncrustify --replace -c "${uconf=$(dirname "$0")/uncrustify.cfg}" "$file"
|
||||
;;
|
||||
*.el)
|
||||
emacs -Q --batch "$file" --eval '(indent-region (point-min) (point-max) nil)' -f save-buffer
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
git diff --quiet
|
|
@ -4,7 +4,7 @@ dir := doc
|
|||
|
||||
# You can set these variables from the command line.
|
||||
SPHINXOPTS := -q
|
||||
SPHINXBUILD = sphinx-build
|
||||
SPHINXBUILD = env LD_LIBRARY_PATH=${NOTMUCH_BUILDDIR}/lib sphinx-build
|
||||
DOCBUILDDIR := $(dir)/_build
|
||||
|
||||
# Internal variables.
|
||||
|
@ -35,7 +35,7 @@ endif
|
|||
|
||||
INFO_INFO_FILES := $(INFO_TEXI_FILES:.texi=.info)
|
||||
|
||||
.PHONY: sphinx-html sphinx-texinfo sphinx-info
|
||||
.PHONY: sphinx-html sphinx-texinfo sphinx-info doc-prereqs
|
||||
|
||||
.PHONY: install-man build-man apidocs install-apidocs
|
||||
|
||||
|
@ -46,15 +46,19 @@ ifeq ($(WITH_EMACS),1)
|
|||
$(DOCBUILDDIR)/.roff.stamp $(DOCBUILDDIR)/.html.stamp $(DOCBUILDDIR)/.texi.stamp : docstring.stamp
|
||||
endif
|
||||
|
||||
ifeq ($(HAVE_PYTHON3_CFFI),1)
|
||||
doc-prereqs: python-cffi-bindings
|
||||
endif
|
||||
|
||||
sphinx-html: $(DOCBUILDDIR)/.html.stamp
|
||||
|
||||
$(DOCBUILDDIR)/.html.stamp: $(ALL_RST_FILES)
|
||||
$(DOCBUILDDIR)/.html.stamp: $(ALL_RST_FILES) doc-prereqs
|
||||
$(SPHINXBUILD) -b html -d $(DOCBUILDDIR)/html_doctrees $(ALLSPHINXOPTS) $(DOCBUILDDIR)/html
|
||||
touch $@
|
||||
|
||||
sphinx-texinfo: $(DOCBUILDDIR)/.texi.stamp
|
||||
|
||||
$(DOCBUILDDIR)/.texi.stamp: $(ALL_RST_FILES)
|
||||
$(DOCBUILDDIR)/.texi.stamp: $(ALL_RST_FILES) doc-prereqs
|
||||
$(SPHINXBUILD) -b texinfo -d $(DOCBUILDDIR)/texinfo_doctrees $(ALLSPHINXOPTS) $(DOCBUILDDIR)/texinfo
|
||||
touch $@
|
||||
|
||||
|
|
|
@ -25,7 +25,7 @@ for pathdir in ['.', '..']:
|
|||
version=infile.read().replace('\n','')
|
||||
|
||||
# for autodoc
|
||||
sys.path.insert(0, os.path.join(location, '..', 'bindings', 'python-cffi', 'notmuch2'))
|
||||
sys.path.insert(0, os.path.join(location, '..', 'bindings', 'python-cffi', 'build', 'stage'))
|
||||
|
||||
# read generated config
|
||||
for pathdir in ['.', '..']:
|
||||
|
@ -159,6 +159,10 @@ man_pages = [
|
|||
u'syntax for notmuch queries',
|
||||
[notmuch_authors], 7),
|
||||
|
||||
('man7/notmuch-sexp-queries', 'notmuch-sexp-queries',
|
||||
u's-expression syntax for notmuch queries',
|
||||
[notmuch_authors], 7),
|
||||
|
||||
('man1/notmuch-show', 'notmuch-show',
|
||||
u'show messages matching the given search terms',
|
||||
[notmuch_authors], 1),
|
||||
|
|
|
@ -24,6 +24,7 @@ Contents:
|
|||
man1/notmuch-restore
|
||||
man1/notmuch-search
|
||||
man7/notmuch-search-terms
|
||||
man7/notmuch-sexp-queries
|
||||
man1/notmuch-show
|
||||
man1/notmuch-tag
|
||||
python-bindings
|
||||
|
|
|
@ -251,9 +251,16 @@ paths are presumed relative to `$HOME` for items in section
|
|||
:any:`notmuch-search-terms(7)` for more information about named
|
||||
queries.
|
||||
|
||||
**squery.<name>**
|
||||
Expansion for named query called <name>, using s-expression syntax. See
|
||||
:any:`notmuch-sexp-queries(7)` for more information about s-expression
|
||||
queries.
|
||||
|
||||
FILES
|
||||
=====
|
||||
|
||||
.. _config_search:
|
||||
|
||||
CONFIGURATION
|
||||
-------------
|
||||
|
||||
|
|
315
doc/man7/notmuch-sexp-queries.rst
Normal file
315
doc/man7/notmuch-sexp-queries.rst
Normal file
|
@ -0,0 +1,315 @@
|
|||
.. _notmuch-sexp-queries(7):
|
||||
|
||||
====================
|
||||
notmuch-sexp-queries
|
||||
====================
|
||||
|
||||
SYNOPSIS
|
||||
========
|
||||
|
||||
**notmuch** *subcommand* ``--query=sexp`` [option ...] ``--`` '(and (to santa) (date december))'
|
||||
|
||||
DESCRIPTION
|
||||
===========
|
||||
|
||||
Notmuch supports an alternative query syntax based on `S-expressions
|
||||
<https://en.wikipedia.org/wiki/S-expression>`_ . It can be selected
|
||||
with the command line ``--query=sexp`` or with the appropriate option
|
||||
to the library function :c:func:`notmuch_query_create_with_syntax`.
|
||||
Support for this syntax is currently optional, you can test if your
|
||||
build of notmuch supports it with
|
||||
|
||||
::
|
||||
|
||||
$ notmuch config get built_with.sexpr_query
|
||||
|
||||
|
||||
S-EXPRESSIONS
|
||||
-------------
|
||||
|
||||
An *s-expression* is either an atom, or list of whitespace delimited
|
||||
s-expressions inside parentheses. Atoms are either
|
||||
|
||||
*basic value*
|
||||
A basic value is an unquoted string containing no whitespace, double quotes, or
|
||||
parentheses.
|
||||
|
||||
*quoted string*
|
||||
Double quotes (") delimit strings possibly containing whitespace
|
||||
or parentheses. These can contain double quote characters by
|
||||
escaping with backslash. E.g. ``"this is a quote \""``.
|
||||
|
||||
S-EXPRESSION QUERIES
|
||||
--------------------
|
||||
|
||||
An s-expression query is either an atom, the empty list, or a
|
||||
*compound query* consisting of a prefix atom (first element) defining
|
||||
a *field*, *logical operation*, or *modifier*, and 0 or more
|
||||
subqueries.
|
||||
|
||||
``*``
|
||||
"*" matches any non-empty string in the current field.
|
||||
|
||||
``()``
|
||||
The empty list matches all messages
|
||||
|
||||
*term*
|
||||
|
||||
Match all messages containing *term*, possibly after stemming or
|
||||
phrase splitting. For discussion of stemming in notmuch see
|
||||
:any:`notmuch-search-terms(7)`. Stemming only applies to unquoted
|
||||
terms (basic values) in s-expression queries. For information on
|
||||
phrase splitting see :any:`fields`.
|
||||
|
||||
``(`` *field* |q1| |q2| ... |qn| ``)``
|
||||
Restrict the queries |q1| to |qn| to *field*, and combine with *and*
|
||||
(for most fields) or *or*. See :any:`fields` for more information.
|
||||
|
||||
``(`` *operator* |q1| |q2| ... |qn| ``)``
|
||||
Combine queries |q1| to |qn|. Currently supported operators are
|
||||
``and``, ``or``, and ``not``. ``(not`` |q1| ... |qn| ``)`` is equivalent
|
||||
to ``(and (not`` |q1| ``) ... (not`` |qn| ``))``.
|
||||
|
||||
``(`` *modifier* |q1| |q2| ... |qn| ``)``
|
||||
Combine queries |q1| to |qn|, and reinterpret the result (e.g. as a regular expression).
|
||||
See :any:`modifiers` for more information.
|
||||
|
||||
``(macro (`` |p1| ... |pn| ``) body)``
|
||||
Define saved query with parameter substitution. The syntax is
|
||||
recognized only in saved s-expression queries (see ``squery.*`` in
|
||||
:any:`notmuch-config(1)`). Parameter names in ``body`` must be
|
||||
prefixed with ``,`` to be expanded (see :any:`macro_examples`).
|
||||
Macros may refer to other macros, but only to their own
|
||||
parameters [#macro-details]_.
|
||||
|
||||
.. _fields:
|
||||
|
||||
FIELDS
|
||||
``````
|
||||
|
||||
*Fields* [#aka-pref]_
|
||||
correspond to attributes of mail messages. Some are inherent (and
|
||||
immutable) like ``subject``, while others ``tag`` and ``property`` are
|
||||
settable by the user. Each concrete field in
|
||||
:any:`the table below <field-table>`
|
||||
is discussed further under "Search prefixes" in
|
||||
:any:`notmuch-search-terms(7)`. The row *user* refers to user defined
|
||||
fields, described in :any:`notmuch-config(1)`.
|
||||
|
||||
Most fields are either *phrase fields* [#aka-prob]_ (which match
|
||||
sequences of words), or *term fields* [#aka-bool]_ (which match exact
|
||||
strings). *Phrase splitting* breaks the term (basic value or quoted
|
||||
string) into words, ignore punctuation. Phrase splitting is applied to
|
||||
terms in phrase (probabilistic) fields. Both phrase splitting and
|
||||
stemming apply only in phrase fields.
|
||||
|
||||
Each term or phrase field has an associated combining operator
|
||||
(``and`` or ``or``) used to combine the queries from each element of
|
||||
the tail of the list. This is generally ``or`` for those fields where
|
||||
a message has one such attribute, and ``and`` otherwise.
|
||||
|
||||
Term or phrase fields can contain arbitrarily complex queries made up
|
||||
from terms, operators, and modifiers, but not other fields.
|
||||
|
||||
.. _field-table:
|
||||
|
||||
.. table:: Fields with supported modifiers
|
||||
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| field | combine | type | expand | wildcard | regex |
|
||||
+============+===========+===========+===========+===========+==========+
|
||||
| *none* | and | | no | yes | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| *user* | and | phrase | no | yes | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| attachment | and | phrase | yes | yes | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| body | and | phrase | no | no | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| date | | range | no | no | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| folder | or | phrase | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| from | and | phrase | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| id | or | term | no | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| is | and | term | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| lastmod | | range | no | no | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| mid | or | term | no | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| mimetype | or | phrase | yes | yes | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| path | or | term | no | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| property | and | term | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| subject | and | phrase | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| tag | and | term | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| thread | or | term | yes | yes | yes |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
| to | and | phrase | yes | yes | no |
|
||||
+------------+-----------+-----------+-----------+-----------+----------+
|
||||
|
||||
.. _modifiers:
|
||||
|
||||
MODIFIERS
|
||||
`````````
|
||||
|
||||
*Modifiers* refer to any prefixes (first elements of compound queries)
|
||||
that are neither operators nor fields.
|
||||
|
||||
``(infix`` *atom* ``)``
|
||||
Interpret *atom* as an infix notmuch query (see
|
||||
:any:`notmuch-search-terms(7)`). Not supported inside fields.
|
||||
|
||||
``(matching`` |q1| |q2| ... |qn| ``)`` ``(of`` |q1| |q2| ... |qn| ``)``
|
||||
Match all messages have the same values of the current field as
|
||||
those matching all of |q1| ... |qn|. Supported in most term [#not-path]_ or
|
||||
phrase fields. Most commonly used in the ``thread`` field.
|
||||
|
||||
``(query`` *atom* ``)``
|
||||
Expand to the saved query named by *atom*. See
|
||||
:any:`notmuch-config(1)` for more. Note that the saved query must
|
||||
be in infix syntax (:any:`notmuch-search-terms(7)`). Not supported
|
||||
inside fields.
|
||||
|
||||
``(regex`` *atom* ``)`` ``(rx`` *atom* ``)``
|
||||
Interpret *atom* as a POSIX.2 regular expression (see
|
||||
:manpage:`regex(7)`). This applies in term fields and a subset [#not-phrase]_ of
|
||||
phrase fields (see :any:`field-table`).
|
||||
|
||||
``(starts-with`` *subword* ``)``
|
||||
Matches any term starting with *subword*. This applies in either
|
||||
phrase or term :any:`fields <fields>`, or outside of fields [#not-body]_. Note that
|
||||
a ``starts-with`` query cannot be part of a phrase. The
|
||||
atom ``*`` is a synonym for ``(starts-with "")``.
|
||||
|
||||
EXAMPLES
|
||||
========
|
||||
|
||||
``Wizard``
|
||||
Match all messages containing the word "wizard", ignoring case.
|
||||
|
||||
``added``
|
||||
Match all messages containing "added", but also those containing "add", "additional",
|
||||
"Additional", "adds", etc... via stemming.
|
||||
|
||||
``(and Bob Marley)``
|
||||
Match messages containing words "Bob" and "Marley", or their stems
|
||||
The words need not be adjacent.
|
||||
|
||||
``(not Bob Marley)``
|
||||
Match messages containing neither "Bob" nor "Marley", nor their stems,
|
||||
|
||||
``"quick fox"`` ``quick-fox`` ``quick@fox``
|
||||
Match the *phrase* "quick" followed by "fox" in phrase fields (or
|
||||
outside a field). Match the literal string in a term field.
|
||||
|
||||
``(folder (of (id 1234@invalid)))``
|
||||
Match any message in the same folder as the one with Message-Id "1234@invalid"
|
||||
|
||||
``(id 1234@invalid blah@test)``
|
||||
Matches Message-Id "1234@invalid" *or* Message-Id "blah@test"
|
||||
|
||||
``(and (infix "date:2009-11-18..2009-11-18") (tag unread))``
|
||||
Match messages in the given date range with tag unread.
|
||||
|
||||
``(starts-with prelim)``
|
||||
Match any words starting with "prelim".
|
||||
|
||||
``(subject quick "brown fox")``
|
||||
Match messages whose subject contains "quick" (anywhere, stemmed) and
|
||||
the phrase "brown fox".
|
||||
|
||||
``(subject (starts-with prelim))``
|
||||
Matches any word starting with "prelim", inside a message subject.
|
||||
|
||||
``(subject (starts-wih quick) "brown fox")``
|
||||
Match messages whose subject contains "quick brown fox", but also
|
||||
"brown fox quicksand".
|
||||
|
||||
``(thread (of (id 1234@invalid)))``
|
||||
Match any message in the same thread as the one with Message-Id "1234@invalid"
|
||||
|
||||
``(thread (matching (from bob@example.com) (to bob@example.com)))``
|
||||
Match any (messages in) a thread containing a message from
|
||||
"bob@example.com" and a (possibly distinct) message to "bob at
|
||||
example.com")
|
||||
|
||||
``(to (or bob@example.com mallory@example.org))`` ``(or (to bob@example.com) (to mallory@example.org))``
|
||||
Match in the "To" or "Cc" headers, "bob@example.com",
|
||||
"mallory@example.org", and also "bob@example.com.au" since it
|
||||
contains the adjacent triple "bob", "example", "com".
|
||||
|
||||
``(not (to *))``
|
||||
Match messages with an empty or invalid 'To' and 'Cc' field.
|
||||
|
||||
``(List *)``
|
||||
Match messages with a non-empty List-Id header, assuming
|
||||
configuration ``index.header.List=List-Id``
|
||||
|
||||
.. _macro_examples:
|
||||
|
||||
MACRO EXAMPLES
|
||||
--------------
|
||||
|
||||
A macro that takes two parameters and applies different fields to them.
|
||||
|
||||
::
|
||||
|
||||
$ notmuch config set squery.TagSubject '(macro (tagname subj) (and (tag ,tagname) (subject ,subj)))'
|
||||
$ notmuch search --query=sexp '(TagSubject inbox maildir)'
|
||||
|
||||
Nested macros are allowed.
|
||||
|
||||
::
|
||||
|
||||
$ notmuch config set squery.Inner '(macro (x) (subject ,x))'
|
||||
$ notmuch config set squery.Outer '(macro (x y) (and (tag ,x) (Inner ,y)))'
|
||||
$ notmuch search --query=sexp '(Outer inbox maildir)'
|
||||
|
||||
Parameters can be re-used to reduce boilerplate. Any field, including
|
||||
user defined fields is permitted within a macro.
|
||||
|
||||
::
|
||||
|
||||
$ notmuch config set squery.About '(macro (name) (or (subject ,name) (List ,name)))'
|
||||
$ notmuch search --query=sexp '(About notmuch)'
|
||||
|
||||
|
||||
NOTES
|
||||
=====
|
||||
|
||||
.. [#macro-details] Technically macros impliment lazy evaluation and
|
||||
lexical scope. There is one top level scope
|
||||
containing all macro definitions, but all
|
||||
parameter definitions are local to a given macro.
|
||||
|
||||
.. [#aka-pref] a.k.a. prefixes
|
||||
|
||||
.. [#aka-prob] a.k.a. probabilistic prefixes
|
||||
|
||||
.. [#aka-bool] a.k.a. boolean prefixes
|
||||
|
||||
.. [#not-phrase] Due to the implemention of phrase fields in Xapian,
|
||||
regex queries could only match individual words.
|
||||
|
||||
.. [#not-body] Due the the way ``body`` is implemented in notmuch,
|
||||
this modifier is not supported in the ``body`` field.
|
||||
|
||||
.. [#not-path] Due to the way recursive ``path`` queries are implemented
|
||||
in notmuch, this modifier is not supported in the
|
||||
``path`` field.
|
||||
|
||||
.. |q1| replace:: :math:`q_1`
|
||||
.. |q2| replace:: :math:`q_2`
|
||||
.. |qn| replace:: :math:`q_n`
|
||||
|
||||
.. |p1| replace:: :math:`p_1`
|
||||
.. |p2| replace:: :math:`p_2`
|
||||
.. |pn| replace:: :math:`p_n`
|
|
@ -217,7 +217,7 @@ requiring external commands."
|
|||
;; harvest if necessary.
|
||||
(notmuch-address-harvest-trigger)))
|
||||
(t
|
||||
(process-lines notmuch-address-command original))))
|
||||
(notmuch--process-lines notmuch-address-command original))))
|
||||
|
||||
(defun notmuch-address-expand-name ()
|
||||
(cond
|
||||
|
|
|
@ -164,7 +164,7 @@ mode."
|
|||
(goto-char (point-max))
|
||||
(insert (format "-- Key %s in message %s:\n"
|
||||
fingerprint id))
|
||||
(call-process notmuch-crypto-gpg-program nil t t
|
||||
(notmuch--call-process notmuch-crypto-gpg-program nil t t
|
||||
"--batch" "--no-tty" "--list-keys" fingerprint))
|
||||
(recenter -1))))
|
||||
|
||||
|
@ -224,7 +224,7 @@ corresponding key when the status button is pressed."
|
|||
(with-current-buffer buffer
|
||||
(goto-char (point-max))
|
||||
(insert (format "--- Retrieving key %s:\n" keyid)))
|
||||
(let ((p (make-process
|
||||
(let ((p (notmuch--make-process
|
||||
:name "notmuch GPG key retrieval"
|
||||
:connection-type 'pipe
|
||||
:buffer buffer
|
||||
|
@ -240,9 +240,9 @@ corresponding key when the status button is pressed."
|
|||
(with-current-buffer buffer
|
||||
(goto-char (point-max))
|
||||
(insert (format "--- Retrieving key %s:\n" keyid))
|
||||
(call-process notmuch-crypto-gpg-program nil t t "--recv-keys" keyid)
|
||||
(notmuch--call-process notmuch-crypto-gpg-program nil t t "--recv-keys" keyid)
|
||||
(insert "\n")
|
||||
(call-process notmuch-crypto-gpg-program nil t t "--list-keys" keyid))
|
||||
(notmuch--call-process notmuch-crypto-gpg-program nil t t "--list-keys" keyid))
|
||||
(recenter -1))
|
||||
(notmuch-show-refresh-view)))))
|
||||
|
||||
|
|
|
@ -42,7 +42,7 @@
|
|||
:group 'notmuch)
|
||||
|
||||
(defcustom notmuch-draft-tags '("+draft")
|
||||
"List of tags changes to apply to a draft message when it is saved in the database.
|
||||
"List of tag changes to apply when saving a draft message in the database.
|
||||
|
||||
Tags starting with \"+\" (or not starting with either \"+\" or
|
||||
\"-\") in the list will be added, and tags starting with \"-\"
|
||||
|
@ -239,7 +239,7 @@ applied to newly inserted messages)."
|
|||
(defun notmuch-draft-resume (id)
|
||||
"Resume editing of message with id ID."
|
||||
;; Used by command `notmuch-show-resume-message'.
|
||||
(let* ((tags (process-lines notmuch-command "search" "--output=tags"
|
||||
(let* ((tags (notmuch--process-lines notmuch-command "search" "--output=tags"
|
||||
"--exclude=false" id))
|
||||
(draft (equal tags (notmuch-update-tags tags notmuch-draft-tags))))
|
||||
(when (or draft
|
||||
|
@ -249,7 +249,7 @@ applied to newly inserted messages)."
|
|||
(setq buffer-read-only nil)
|
||||
(erase-buffer)
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
(notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
(mime-to-mml)
|
||||
(goto-char (point-min))
|
||||
(when (re-search-forward "^$" nil t)
|
||||
|
|
|
@ -144,9 +144,11 @@ a plist. Supported properties are
|
|||
Possible values are `oldest-first', `newest-first'
|
||||
or nil. Nil means use the default sort order.
|
||||
:search-type Specify whether to run the search in search-mode,
|
||||
tree mode or unthreaded mode. Set to 'tree to specify tree
|
||||
mode, 'unthreaded to specify unthreaded mode, and set to nil
|
||||
(or anything except tree and unthreaded) to specify search mode.
|
||||
tree mode or unthreaded mode. Set to `tree' to
|
||||
specify tree mode, 'unthreaded to specify
|
||||
unthreaded mode, and set to nil (or anything
|
||||
except tree and unthreaded) to specify search
|
||||
mode.
|
||||
|
||||
Other accepted forms are a cons cell of the form (NAME . QUERY)
|
||||
or a list of the form (NAME QUERY COUNT-QUERY)."
|
||||
|
@ -494,7 +496,7 @@ diagonal."
|
|||
(widget-get widget :notmuch-search-oldest-first)))))
|
||||
|
||||
(defun notmuch-saved-search-count (search)
|
||||
(car (process-lines notmuch-command "count" search)))
|
||||
(car (notmuch--process-lines notmuch-command "count" search)))
|
||||
|
||||
(defun notmuch-hello-tags-per-line (widest)
|
||||
"Determine how many tags to show per line and how wide they
|
||||
|
@ -567,7 +569,7 @@ options will be handled as specified for
|
|||
(or (plist-get options :filter-count)
|
||||
(plist-get options :filter))))
|
||||
"\n")))
|
||||
(unless (= (call-process-region (point-min) (point-max) notmuch-command
|
||||
(unless (= (notmuch--call-process-region (point-min) (point-max) notmuch-command
|
||||
t t nil "count" "--batch") 0)
|
||||
(notmuch-logged-error
|
||||
"notmuch count --batch failed"
|
||||
|
@ -746,7 +748,7 @@ Complete list of currently available key bindings:
|
|||
(list (cons tag
|
||||
(concat "tag:"
|
||||
(notmuch-escape-boolean-term tag))))))
|
||||
(process-lines notmuch-command "search" "--output=tags" "*")))
|
||||
(notmuch--process-lines notmuch-command "search" "--output=tags" "*")))
|
||||
|
||||
(defun notmuch-hello-insert-header ()
|
||||
"Insert the default notmuch-hello header."
|
||||
|
@ -784,7 +786,7 @@ Complete list of currently available key bindings:
|
|||
:help-echo "Refresh"
|
||||
(notmuch-hello-nice-number
|
||||
(string-to-number
|
||||
(car (process-lines notmuch-command "count")))))
|
||||
(car (notmuch--process-lines notmuch-command "count")))))
|
||||
(widget-insert " messages.\n")))
|
||||
|
||||
(defun notmuch-hello-insert-saved-searches ()
|
||||
|
@ -869,16 +871,16 @@ Supports the following entries in OPTIONS as a plist:
|
|||
(start (point)))
|
||||
(if is-hidden
|
||||
(widget-create 'push-button
|
||||
:notify `(lambda (widget &rest _ignore)
|
||||
(setq notmuch-hello-hidden-sections
|
||||
(delete ,title notmuch-hello-hidden-sections))
|
||||
(notmuch-hello-update))
|
||||
:notify (lambda (&rest _ignore)
|
||||
(setq notmuch-hello-hidden-sections
|
||||
(delete title notmuch-hello-hidden-sections))
|
||||
(notmuch-hello-update))
|
||||
"show")
|
||||
(widget-create 'push-button
|
||||
:notify `(lambda (widget &rest _ignore)
|
||||
(add-to-list 'notmuch-hello-hidden-sections
|
||||
,title)
|
||||
(notmuch-hello-update))
|
||||
:notify (lambda (&rest _ignore)
|
||||
(add-to-list 'notmuch-hello-hidden-sections
|
||||
title)
|
||||
(notmuch-hello-update))
|
||||
"hide"))
|
||||
(widget-insert "\n")
|
||||
(unless is-hidden
|
||||
|
|
|
@ -25,6 +25,10 @@
|
|||
(require 'notmuch-lib)
|
||||
(require 'notmuch-hello)
|
||||
|
||||
(declare-function notmuch-search "notmuch")
|
||||
(declare-function notmuch-tree "notmuch-tree")
|
||||
(declare-function notmuch-unthreaded "notmuch-tree")
|
||||
|
||||
;;;###autoload
|
||||
(defun notmuch-jump-search ()
|
||||
"Jump to a saved search by shortcut key.
|
||||
|
@ -50,11 +54,11 @@ fast way to jump to a saved search from anywhere in Notmuch."
|
|||
(push (list key name
|
||||
(cond
|
||||
((eq (plist-get saved-search :search-type) 'tree)
|
||||
`(lambda () (notmuch-tree ',query)))
|
||||
(lambda () (notmuch-tree query)))
|
||||
((eq (plist-get saved-search :search-type) 'unthreaded)
|
||||
`(lambda () (notmuch-unthreaded ',query)))
|
||||
(lambda () (notmuch-unthreaded query)))
|
||||
(t
|
||||
`(lambda () (notmuch-search ',query ',oldest-first)))))
|
||||
(lambda () (notmuch-search query oldest-first)))))
|
||||
action-map)))))
|
||||
(setq action-map (nreverse action-map))
|
||||
(if action-map
|
||||
|
@ -168,9 +172,10 @@ buffer."
|
|||
(pcase-dolist (`(,key ,_name ,fn) action-map)
|
||||
(when (= (length key) 1)
|
||||
(define-key map key
|
||||
`(lambda () (interactive)
|
||||
(setq notmuch-jump--action ',fn)
|
||||
(exit-minibuffer)))))
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(setq notmuch-jump--action fn)
|
||||
(exit-minibuffer)))))
|
||||
;; By doing this in two passes (and checking if we already have a
|
||||
;; binding) we avoid problems if the user specifies a binding which
|
||||
;; is a prefix of another binding.
|
||||
|
@ -191,12 +196,13 @@ buffer."
|
|||
action-submap)
|
||||
(setq action-submap (nreverse action-submap))
|
||||
(define-key map keystr
|
||||
`(lambda () (interactive)
|
||||
(setq notmuch-jump--action
|
||||
',(apply-partially #'notmuch-jump
|
||||
action-submap
|
||||
new-prompt))
|
||||
(exit-minibuffer)))))))
|
||||
(lambda ()
|
||||
(interactive)
|
||||
(setq notmuch-jump--action
|
||||
(apply-partially #'notmuch-jump
|
||||
action-submap
|
||||
new-prompt))
|
||||
(exit-minibuffer)))))))
|
||||
map))
|
||||
|
||||
(provide 'notmuch-jump)
|
||||
|
|
|
@ -195,7 +195,7 @@ will be signaled.
|
|||
|
||||
Otherwise the output will be returned."
|
||||
(with-temp-buffer
|
||||
(let ((status (apply #'call-process notmuch-command nil t nil args))
|
||||
(let ((status (apply #'notmuch--call-process notmuch-command nil t nil args))
|
||||
(output (buffer-string)))
|
||||
(notmuch-check-exit-status status (cons notmuch-command args) output)
|
||||
output)))
|
||||
|
@ -206,7 +206,7 @@ Otherwise the output will be returned."
|
|||
(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
|
||||
(let ((status (notmuch--call-process notmuch-command nil nil nil
|
||||
"config" "get" "user.primary_email")))
|
||||
(setq notmuch--cli-sane-p (= status 0))))
|
||||
notmuch--cli-sane-p)
|
||||
|
@ -286,7 +286,7 @@ depending on the value of `notmuch-poll-script'."
|
|||
(message "Polling mail...")
|
||||
(if (stringp notmuch-poll-script)
|
||||
(unless (string-empty-p notmuch-poll-script)
|
||||
(unless (equal (call-process notmuch-poll-script nil nil) 0)
|
||||
(unless (equal (notmuch--call-process notmuch-poll-script nil nil) 0)
|
||||
(error "Notmuch: poll script `%s' failed!" notmuch-poll-script)))
|
||||
(notmuch-call-notmuch-process "new"))
|
||||
(message "Polling mail...done"))
|
||||
|
@ -639,7 +639,7 @@ the given type."
|
|||
;; charset is US-ASCII. RFC6657
|
||||
;; complicates this somewhat.
|
||||
'us-ascii)))))
|
||||
(apply #'call-process
|
||||
(apply #'notmuch--call-process
|
||||
notmuch-command nil '(t nil) nil args)
|
||||
(buffer-string))))))
|
||||
(when (and cache data)
|
||||
|
@ -860,6 +860,32 @@ You may need to restart Emacs or upgrade your notmuch package."))
|
|||
;; `notmuch-logged-error' does not return.
|
||||
))))
|
||||
|
||||
(defmacro notmuch--apply-with-env (func &rest args)
|
||||
`(let ((default-directory "~"))
|
||||
(apply ,func ,@args)))
|
||||
|
||||
(defun notmuch--process-lines (program &rest args)
|
||||
"Wrap process-lines, binding DEFAULT-DIRECTORY to a safe
|
||||
default"
|
||||
(notmuch--apply-with-env #'process-lines program args))
|
||||
|
||||
(defun notmuch--make-process (&rest args)
|
||||
"Wrap make-process, binding DEFAULT-DIRECTORY to a safe
|
||||
default"
|
||||
(notmuch--apply-with-env #'make-process args))
|
||||
|
||||
(defun notmuch--call-process-region (start end program
|
||||
&optional delete buffer display
|
||||
&rest args)
|
||||
"Wrap call-process-region, binding DEFAULT-DIRECTORY to a safe
|
||||
default"
|
||||
(notmuch--apply-with-env
|
||||
#'call-process-region start end program delete buffer display args))
|
||||
|
||||
(defun notmuch--call-process (program &optional infile destination display &rest args)
|
||||
"Wrap call-process, binding DEFAULT-DIRECTORY to a safe default"
|
||||
(notmuch--apply-with-env #'call-process program infile destination display args))
|
||||
|
||||
(defun notmuch-call-notmuch--helper (destination args)
|
||||
"Helper for synchronous notmuch invocation commands.
|
||||
|
||||
|
@ -874,9 +900,9 @@ for `call-process'. ARGS is as described for
|
|||
(otherwise
|
||||
(error "Unknown keyword argument: %s" (car args)))))
|
||||
(if (null stdin-string)
|
||||
(apply #'call-process notmuch-command nil destination nil args)
|
||||
(apply #'notmuch--call-process notmuch-command nil destination nil args)
|
||||
(insert stdin-string)
|
||||
(apply #'call-process-region (point-min) (point-max)
|
||||
(apply #'notmuch--call-process-region (point-min) (point-max)
|
||||
notmuch-command t destination nil args))))
|
||||
|
||||
(defun notmuch-call-notmuch-process (&rest args)
|
||||
|
@ -933,7 +959,7 @@ status."
|
|||
(let* ((command (or (executable-find notmuch-command)
|
||||
(error "Command not found: %s" notmuch-command)))
|
||||
(err-buffer (generate-new-buffer " *notmuch-stderr*"))
|
||||
(proc (make-process
|
||||
(proc (notmuch--make-process
|
||||
:name name
|
||||
:buffer buffer
|
||||
:command (cons command args)
|
||||
|
|
|
@ -41,16 +41,17 @@ Three types of values are permitted:
|
|||
- a string: the value of `notmuch-fcc-dirs' is the Fcc header to
|
||||
be used.
|
||||
|
||||
- a list: the folder is chosen based on the From address of the
|
||||
current message using a list of regular expressions and
|
||||
corresponding folders:
|
||||
- an alist: the folder is chosen based on the From address of
|
||||
the current message according to an alist mapping regular
|
||||
expressions to folders or nil:
|
||||
|
||||
((\"Sebastian@SSpaeth.de\" . \"privat\")
|
||||
(\"spaetz@sspaeth.de\" . \"OUTBOX.OSS\")
|
||||
(\".*\" . \"defaultinbox\"))
|
||||
|
||||
If none of the regular expressions match the From address, no
|
||||
Fcc header will be added.
|
||||
If none of the regular expressions match the From address, or
|
||||
if the cdr of the matching entry is nil, then no Fcc header
|
||||
will be added.
|
||||
|
||||
If `notmuch-maildir-use-notmuch-insert' is set (the default) then
|
||||
the header should be of the form \"folder +tag1 -tag2\" where
|
||||
|
@ -74,7 +75,8 @@ directory if it does not exist yet when sending a mail."
|
|||
(const :tag "No FCC header" nil)
|
||||
(string :tag "A single folder")
|
||||
(repeat :tag "A folder based on the From header"
|
||||
(cons regexp (string :tag "Folder"))))
|
||||
(cons regexp (choice (const :tag "No FCC header" nil)
|
||||
(string :tag "Folder")))))
|
||||
:require 'notmuch-fcc-initialization
|
||||
:group 'notmuch-send)
|
||||
|
||||
|
@ -105,13 +107,14 @@ Otherwise set it according to `notmuch-fcc-dirs'."
|
|||
;; Old style - no longer works.
|
||||
(error "Invalid `notmuch-fcc-dirs' setting (old style)"))
|
||||
((listp notmuch-fcc-dirs)
|
||||
(or (seq-some (let ((from (message-field-value "From")))
|
||||
(pcase-lambda (`(,regexp . ,folder))
|
||||
(and (string-match-p regexp from)
|
||||
folder)))
|
||||
notmuch-fcc-dirs)
|
||||
(progn (message "No Fcc header added.")
|
||||
nil)))
|
||||
(if-let ((match (seq-some (let ((from (message-field-value "From")))
|
||||
(pcase-lambda (`(,regexp . ,folder))
|
||||
(and (string-match-p regexp from)
|
||||
(cons t folder))))
|
||||
notmuch-fcc-dirs)))
|
||||
(cdr match)
|
||||
(message "No Fcc header added.")
|
||||
nil))
|
||||
(t
|
||||
(error "Invalid `notmuch-fcc-dirs' setting (neither string nor list)")))))
|
||||
(when subdir
|
||||
|
|
|
@ -474,7 +474,7 @@ the From: address."
|
|||
(with-current-buffer temp-buffer
|
||||
(erase-buffer)
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil t nil
|
||||
(notmuch--call-process notmuch-command nil t nil
|
||||
"show" "--format=raw" id))
|
||||
;; Because we process the messages in reverse order,
|
||||
;; always generate a forwarded subject, then use the
|
||||
|
|
|
@ -48,7 +48,7 @@
|
|||
"Pass the contents of the current buffer to 'muttprint'.
|
||||
|
||||
Optional OUTPUT allows passing a list of flags to muttprint."
|
||||
(apply #'call-process-region (point-min) (point-max)
|
||||
(apply #'notmuch--call-process-region (point-min) (point-max)
|
||||
;; Reads from stdin.
|
||||
"muttprint"
|
||||
nil nil nil
|
||||
|
|
|
@ -279,7 +279,7 @@ position of the message in the thread."
|
|||
(let ((buf (generate-new-buffer (concat "*notmuch-msg-" id "*"))))
|
||||
(with-current-buffer buf
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
(notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
,@body)
|
||||
(kill-buffer buf)))))
|
||||
|
||||
|
@ -719,21 +719,23 @@ will return nil if the CID is unknown or cannot be retrieved."
|
|||
t)
|
||||
|
||||
(defun notmuch-show-insert-part-message/rfc822 (msg part _content-type _nth depth _button)
|
||||
(let* ((message (car (plist-get part :content)))
|
||||
(body (car (plist-get message :body)))
|
||||
(start (point)))
|
||||
;; Override `notmuch-message-headers' to force `From' to be
|
||||
;; displayed.
|
||||
(let ((notmuch-message-headers '("From" "Subject" "To" "Cc" "Date")))
|
||||
(notmuch-show-insert-headers (plist-get message :headers)))
|
||||
;; Blank line after headers to be compatible with the normal
|
||||
;; message display.
|
||||
(insert "\n")
|
||||
;; Show the body
|
||||
(notmuch-show-insert-bodypart msg body depth)
|
||||
(when notmuch-show-indent-multipart
|
||||
(indent-rigidly start (point) 1)))
|
||||
t)
|
||||
(let ((message (car (plist-get part :content))))
|
||||
(and
|
||||
message
|
||||
(let ((body (car (plist-get message :body)))
|
||||
(start (point)))
|
||||
;; Override `notmuch-message-headers' to force `From' to be
|
||||
;; displayed.
|
||||
(let ((notmuch-message-headers '("From" "Subject" "To" "Cc" "Date")))
|
||||
(notmuch-show-insert-headers (plist-get message :headers)))
|
||||
;; Blank line after headers to be compatible with the normal
|
||||
;; message display.
|
||||
(insert "\n")
|
||||
;; Show the body
|
||||
(notmuch-show-insert-bodypart msg body depth)
|
||||
(when notmuch-show-indent-multipart
|
||||
(indent-rigidly start (point) 1))
|
||||
t))))
|
||||
|
||||
(defun notmuch-show-insert-part-text/plain (msg part _content-type _nth depth button)
|
||||
;; For backward compatibility we want to apply the text/plain hook
|
||||
|
@ -2032,7 +2034,7 @@ to show, nil otherwise."
|
|||
(pop-to-buffer-same-window buf)
|
||||
(erase-buffer)
|
||||
(let ((coding-system-for-read 'no-conversion))
|
||||
(call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
(notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id))
|
||||
(goto-char (point-min))
|
||||
(set-buffer-modified-p nil)
|
||||
(setq buffer-read-only t)
|
||||
|
@ -2078,19 +2080,19 @@ message."
|
|||
(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)
|
||||
(setq buffer-read-only t)
|
||||
(unless (zerop exit-code)
|
||||
(pop-to-buffer buf)
|
||||
(message (format "Command '%s' exited abnormally with code %d"
|
||||
shell-command exit-code))))))))
|
||||
(setq buffer-read-only t)
|
||||
(let ((inhibit-read-only t))
|
||||
(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)
|
||||
(unless (zerop exit-code)
|
||||
(pop-to-buffer buf)
|
||||
(message (format "Command '%s' exited abnormally with code %d"
|
||||
shell-command exit-code)))))))))
|
||||
|
||||
(defun notmuch-show-tag-message (&rest tag-changes)
|
||||
"Change tags for the current message.
|
||||
|
|
|
@ -397,7 +397,7 @@ Return all tags if no search terms are given."
|
|||
(split-string
|
||||
(with-output-to-string
|
||||
(with-current-buffer standard-output
|
||||
(apply 'call-process notmuch-command nil t
|
||||
(apply 'notmuch--call-process notmuch-command nil t
|
||||
nil "search" "--output=tags" "--exclude=false" search-terms)))
|
||||
"\n+" t))
|
||||
|
||||
|
@ -553,7 +553,7 @@ and vice versa."
|
|||
name)
|
||||
(mapconcat #'identity tag-change " "))))
|
||||
(push (list key name-string
|
||||
`(lambda () (,tag-function ',tag-change)))
|
||||
(lambda () (funcall tag-function tag-change)))
|
||||
action-map)))
|
||||
(push (list notmuch-tag-jump-reverse-key
|
||||
(if reverse
|
||||
|
|
|
@ -74,24 +74,55 @@
|
|||
notmuch-unthreaded-show-out
|
||||
notmuch-tree-show-out))
|
||||
|
||||
(defcustom notmuch-tree-thread-symbols
|
||||
'((prefix . " ")
|
||||
(top . "─")
|
||||
(top-tee . "┬")
|
||||
(vertical . "│")
|
||||
(vertical-tee . "├")
|
||||
(bottom . "╰")
|
||||
(arrow . "►"))
|
||||
"Strings used to draw trees in notmuch tree results.
|
||||
Symbol keys denote where the corresponding string value is used:
|
||||
`prefix' is used at the top of the tree, followed by `top' if it
|
||||
has no children or `top-tee' if it does; `vertical' is a bar
|
||||
connecting with a response down the list skipping the current
|
||||
one, while `vertical-tee' marks the current message as a reply to
|
||||
the previous one; `bottom' is used at the bottom of threads.
|
||||
Finally, the `arrrow' string in the list is used as a pointer to
|
||||
every message.
|
||||
|
||||
Common customizations include setting `prefix' to \"-\", to see
|
||||
equal-length prefixes, and `arrow' to an empty string or to a
|
||||
different kind of arrow point."
|
||||
:type '(alist :key-type symbol :value-type string)
|
||||
:group 'notmuch-tree)
|
||||
|
||||
(defcustom notmuch-tree-result-format
|
||||
`(("date" . "%12s ")
|
||||
("authors" . "%-20s")
|
||||
((("tree" . "%s")("subject" . "%s")) ." %-54s ")
|
||||
((("tree" . "%s")
|
||||
("subject" . "%s"))
|
||||
. " %-54s ")
|
||||
("tags" . "(%s)"))
|
||||
"Result formatting for tree view. Supported fields are: date,
|
||||
authors, subject, tree, tags. Tree means the thread tree
|
||||
box graphics. The field may also be a list in which case
|
||||
the formatting rules are applied recursively and then the
|
||||
output of all the fields in the list is inserted
|
||||
according to format-string.
|
||||
"Result formatting for tree view.
|
||||
|
||||
Note the author string should not contain
|
||||
whitespace (put it in the neighbouring fields instead).
|
||||
For example:
|
||||
(setq notmuch-tree-result-format \(\(\"authors\" . \"%-40s\"\)
|
||||
\(\"subject\" . \"%s\"\)\)\)"
|
||||
:type '(alist :key-type (string) :value-type (string))
|
||||
Supported fields are: date, authors, subject, tree, tags.
|
||||
|
||||
Tree means the thread tree box graphics. The field may
|
||||
also be a list in which case the formatting rules are
|
||||
applied recursively and then the output of all the fields
|
||||
in the list is inserted according to format-string.
|
||||
|
||||
Note that the author string should not contain whitespace
|
||||
\(put it in the neighbouring fields instead). For example:
|
||||
(setq notmuch-tree-result-format
|
||||
'((\"authors\" . \"%-40s\")
|
||||
(\"subject\" . \"%s\")))"
|
||||
:type '(alist :key-type (choice string
|
||||
(alist :key-type string
|
||||
:value-type string))
|
||||
:value-type string)
|
||||
:group 'notmuch-tree)
|
||||
|
||||
(defcustom notmuch-unthreaded-result-format
|
||||
|
@ -99,19 +130,24 @@ For example:
|
|||
("authors" . "%-20s")
|
||||
((("subject" . "%s")) ." %-54s ")
|
||||
("tags" . "(%s)"))
|
||||
"Result formatting for unthreaded tree view. Supported fields are: date,
|
||||
authors, subject, tree, tags. Tree means the thread tree
|
||||
box graphics. The field may also be a list in which case
|
||||
the formatting rules are applied recursively and then the
|
||||
output of all the fields in the list is inserted
|
||||
according to format-string.
|
||||
"Result formatting for unthreaded tree view.
|
||||
|
||||
Note the author string should not contain
|
||||
whitespace (put it in the neighbouring fields instead).
|
||||
For example:
|
||||
(setq notmuch-tree-result-format \(\(\"authors\" . \"%-40s\"\)
|
||||
\(\"subject\" . \"%s\"\)\)\)"
|
||||
:type '(alist :key-type (string) :value-type (string))
|
||||
Supported fields are: date, authors, subject, tree, tags.
|
||||
|
||||
Tree means the thread tree box graphics. The field may
|
||||
also be a list in which case the formatting rules are
|
||||
applied recursively and then the output of all the fields
|
||||
in the list is inserted according to format-string.
|
||||
|
||||
Note that the author string should not contain whitespace
|
||||
\(put it in the neighbouring fields instead). For example:
|
||||
(setq notmuch-unthreaded-result-format
|
||||
'((\"authors\" . \"%-40s\")
|
||||
(\"subject\" . \"%s\")))"
|
||||
:type '(alist :key-type (choice string
|
||||
(alist :key-type string
|
||||
:value-type string))
|
||||
:value-type string)
|
||||
:group 'notmuch-tree)
|
||||
|
||||
(defun notmuch-tree-result-format ()
|
||||
|
@ -873,6 +909,9 @@ unchanged ADDRESS if parsing fails."
|
|||
((listp field)
|
||||
(format format-string (notmuch-tree-format-field-list field msg)))
|
||||
|
||||
((functionp field)
|
||||
(funcall field format-string msg))
|
||||
|
||||
((string-equal field "date")
|
||||
(let ((face (if match
|
||||
'notmuch-tree-match-date-face
|
||||
|
@ -968,20 +1007,20 @@ message together with all its descendents."
|
|||
(replies (cadr tree)))
|
||||
(cond
|
||||
((and (< 0 depth) (not last))
|
||||
(push "├" tree-status))
|
||||
(push (alist-get 'vertical-tee notmuch-tree-thread-symbols) tree-status))
|
||||
((and (< 0 depth) last)
|
||||
(push "╰" tree-status))
|
||||
(push (alist-get 'bottom notmuch-tree-thread-symbols) tree-status))
|
||||
((and (eq 0 depth) first last)
|
||||
;; Choice between these two variants is a matter of taste.
|
||||
;; (push "─" tree-status))
|
||||
(push " " tree-status))
|
||||
(push (alist-get 'prefix notmuch-tree-thread-symbols) tree-status))
|
||||
((and (eq 0 depth) first (not last))
|
||||
(push "┬" tree-status))
|
||||
(push (alist-get 'top-tee notmuch-tree-thread-symbols) tree-status))
|
||||
((and (eq 0 depth) (not first) last)
|
||||
(push "╰" tree-status))
|
||||
(push (alist-get 'bottom notmuch-tree-thread-symbols) tree-status))
|
||||
((and (eq 0 depth) (not first) (not last))
|
||||
(push "├" tree-status)))
|
||||
(push (concat (if replies "┬" "─") "►") tree-status)
|
||||
(push (alist-get 'vertical-tee notmuch-tree-thread-symbols) tree-status)))
|
||||
(push (concat (alist-get (if replies 'top-tee 'top) notmuch-tree-thread-symbols)
|
||||
(alist-get 'arrow notmuch-tree-thread-symbols))
|
||||
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)))
|
||||
|
@ -990,7 +1029,7 @@ message together with all its descendents."
|
|||
(pop tree-status)
|
||||
(if last
|
||||
(push " " tree-status)
|
||||
(push "│" tree-status))
|
||||
(push (alist-get 'vertical notmuch-tree-thread-symbols) tree-status))
|
||||
(notmuch-tree-insert-thread replies (1+ depth) tree-status)))
|
||||
|
||||
(defun notmuch-tree-insert-thread (thread depth tree-status)
|
||||
|
@ -1098,7 +1137,7 @@ the same as for the function notmuch-tree."
|
|||
(concat " and (" query-context ")"))))
|
||||
(sort-arg (if oldest-first "--sort=oldest-first" "--sort=newest-first"))
|
||||
(message-arg (if unthreaded "--unthreaded" "--entire-thread")))
|
||||
(when (equal (car (process-lines notmuch-command "count" search-args)) "0")
|
||||
(when (equal (car (notmuch--process-lines notmuch-command "count" search-args)) "0")
|
||||
(setq search-args basic-query))
|
||||
(notmuch-tag-clear-cache)
|
||||
(let ((proc (notmuch-start-notmuch
|
||||
|
|
|
@ -88,18 +88,21 @@
|
|||
("authors" . "%-20s ")
|
||||
("subject" . "%s ")
|
||||
("tags" . "(%s)"))
|
||||
"Search result formatting. Supported fields are:
|
||||
date, count, authors, subject, tags
|
||||
"Search result formatting.
|
||||
|
||||
Supported fields are: date, count, authors, subject, tags.
|
||||
For example:
|
||||
(setq notmuch-search-result-format \(\(\"authors\" . \"%-40s\"\)
|
||||
\(\"subject\" . \"%s\"\)\)\)
|
||||
(setq notmuch-search-result-format
|
||||
'((\"authors\" . \"%-40s\")
|
||||
(\"subject\" . \"%s\")))
|
||||
|
||||
Line breaks are permitted in format strings (though this is
|
||||
currently experimental). Note that a line break at the end of an
|
||||
\"authors\" field will get elided if the authors list is long;
|
||||
place it instead at the beginning of the following field. To
|
||||
enter a line break when setting this variable with setq, use \\n.
|
||||
To enter a line break in customize, press \\[quoted-insert] C-j."
|
||||
:type '(alist :key-type (string) :value-type (string))
|
||||
:type '(alist :key-type string :value-type string)
|
||||
:group 'notmuch-search)
|
||||
|
||||
;; The name of this variable `notmuch-init-file' is consistent with the
|
||||
|
@ -830,26 +833,28 @@ non-authors is found, assume that all of the authors match."
|
|||
(insert padding))))
|
||||
|
||||
(defun notmuch-search-insert-field (field format-string result)
|
||||
(cond
|
||||
((string-equal field "date")
|
||||
(insert (propertize (format format-string (plist-get result :date_relative))
|
||||
'face 'notmuch-search-date)))
|
||||
((string-equal field "count")
|
||||
(insert (propertize (format format-string
|
||||
(format "[%s/%s]" (plist-get result :matched)
|
||||
(plist-get result :total)))
|
||||
'face 'notmuch-search-count)))
|
||||
((string-equal field "subject")
|
||||
(insert (propertize (format format-string
|
||||
(notmuch-sanitize (plist-get result :subject)))
|
||||
'face 'notmuch-search-subject)))
|
||||
((string-equal field "authors")
|
||||
(notmuch-search-insert-authors
|
||||
format-string (notmuch-sanitize (plist-get result :authors))))
|
||||
((string-equal field "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)))))))
|
||||
(pcase field
|
||||
((pred functionp)
|
||||
(insert (funcall field format-string result)))
|
||||
("date"
|
||||
(insert (propertize (format format-string (plist-get result :date_relative))
|
||||
'face 'notmuch-search-date)))
|
||||
("count"
|
||||
(insert (propertize (format format-string
|
||||
(format "[%s/%s]" (plist-get result :matched)
|
||||
(plist-get result :total)))
|
||||
'face 'notmuch-search-count)))
|
||||
("subject"
|
||||
(insert (propertize (format format-string
|
||||
(notmuch-sanitize (plist-get result :subject)))
|
||||
'face 'notmuch-search-subject)))
|
||||
("authors"
|
||||
(notmuch-search-insert-authors format-string
|
||||
(notmuch-sanitize (plist-get result :authors))))
|
||||
("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 pos)
|
||||
"Insert RESULT at POS."
|
||||
|
@ -935,7 +940,7 @@ See `notmuch-tag' for information on the format of TAG-CHANGES."
|
|||
PROMPT is the string to prompt with."
|
||||
(let* ((all-tags
|
||||
(mapcar (lambda (tag) (notmuch-escape-boolean-term tag))
|
||||
(process-lines notmuch-command "search" "--output=tags" "*")))
|
||||
(notmuch--process-lines notmuch-command "search" "--output=tags" "*")))
|
||||
(completions
|
||||
(append (list "folder:" "path:" "thread:" "id:" "date:" "from:" "to:"
|
||||
"subject:" "attachment:")
|
||||
|
|
|
@ -63,7 +63,8 @@ libnotmuch_cxx_srcs = \
|
|||
$(dir)/features.cc \
|
||||
$(dir)/prefix.cc \
|
||||
$(dir)/open.cc \
|
||||
$(dir)/init.cc
|
||||
$(dir)/init.cc \
|
||||
$(dir)/parse-sexp.cc
|
||||
|
||||
libnotmuch_modules := $(libnotmuch_c_srcs:.c=.o) $(libnotmuch_cxx_srcs:.cc=.o)
|
||||
|
||||
|
|
|
@ -32,6 +32,8 @@ notmuch_built_with (const char *name)
|
|||
return HAVE_XAPIAN_DB_RETRY_LOCK;
|
||||
} else if (STRNCMP_LITERAL (name, "session_key") == 0) {
|
||||
return true;
|
||||
} else if (STRNCMP_LITERAL (name, "sexpr_query") == 0) {
|
||||
return HAVE_SFSEXP;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
|
|
|
@ -259,7 +259,15 @@ _notmuch_config_load_from_database (notmuch_database_t *notmuch)
|
|||
|
||||
for (; notmuch_config_list_valid (list); notmuch_config_list_move_to_next (list)) {
|
||||
const char *key = notmuch_config_list_key (list);
|
||||
char *normalized_val = _expand_path (list, key, notmuch_config_list_value (list));
|
||||
char *normalized_val = NULL;
|
||||
|
||||
/* If we opened from a given path, do not overwrite it */
|
||||
if (strcmp (key, "database.path") == 0 &&
|
||||
(notmuch->params & NOTMUCH_PARAM_DATABASE) &&
|
||||
notmuch->xapian_db)
|
||||
continue;
|
||||
|
||||
normalized_val = _expand_path (list, key, notmuch_config_list_value (list));
|
||||
_notmuch_string_map_append (notmuch->config, key, normalized_val);
|
||||
talloc_free (normalized_val);
|
||||
}
|
||||
|
@ -432,6 +440,13 @@ _notmuch_config_load_from_file (notmuch_database_t *notmuch,
|
|||
status = NOTMUCH_STATUS_FILE_ERROR;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
/* If we opened from a given path, do not overwrite it */
|
||||
if (strcmp (absolute_key, "database.path") == 0 &&
|
||||
(notmuch->params & NOTMUCH_PARAM_DATABASE) &&
|
||||
notmuch->xapian_db)
|
||||
continue;
|
||||
|
||||
normalized_val = _expand_path (notmuch, absolute_key, val);
|
||||
_notmuch_string_map_set (notmuch->config, absolute_key, normalized_val);
|
||||
g_free (val);
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
|
||||
#include <xapian.h>
|
||||
|
||||
#if HAVE_SFSEXP
|
||||
#include <sexp.h>
|
||||
#endif
|
||||
|
||||
/* Bit masks for _notmuch_database::features. Features are named,
|
||||
* independent aspects of the database schema.
|
||||
*
|
||||
|
@ -186,6 +190,39 @@ operator& (notmuch_field_flag_t a, notmuch_field_flag_t b)
|
|||
Xapian::QueryParser::FLAG_WILDCARD | \
|
||||
Xapian::QueryParser::FLAG_PURE_NOT)
|
||||
|
||||
/*
|
||||
* Which parameters were explicit when the database was opened */
|
||||
typedef enum {
|
||||
NOTMUCH_PARAM_NONE = 0,
|
||||
NOTMUCH_PARAM_DATABASE = 1 << 0,
|
||||
NOTMUCH_PARAM_CONFIG = 1 << 1,
|
||||
NOTMUCH_PARAM_PROFILE = 1 << 2,
|
||||
} notmuch_open_param_t;
|
||||
|
||||
/*
|
||||
* define bitwise operators to hide casts */
|
||||
|
||||
inline notmuch_open_param_t
|
||||
operator| (notmuch_open_param_t a, notmuch_open_param_t b)
|
||||
{
|
||||
return static_cast<notmuch_open_param_t>(
|
||||
static_cast<unsigned>(a) | static_cast<unsigned>(b));
|
||||
}
|
||||
|
||||
inline notmuch_open_param_t&
|
||||
operator|= (notmuch_open_param_t &a, notmuch_open_param_t b)
|
||||
{
|
||||
a = a | b;
|
||||
return a;
|
||||
}
|
||||
|
||||
inline notmuch_open_param_t
|
||||
operator& (notmuch_open_param_t a, notmuch_open_param_t b)
|
||||
{
|
||||
return static_cast<notmuch_open_param_t>(
|
||||
static_cast<unsigned>(a) & static_cast<unsigned>(b));
|
||||
}
|
||||
|
||||
struct _notmuch_database {
|
||||
bool exception_reported;
|
||||
|
||||
|
@ -232,6 +269,7 @@ struct _notmuch_database {
|
|||
*/
|
||||
unsigned long view;
|
||||
Xapian::QueryParser *query_parser;
|
||||
Xapian::Stem *stemmer;
|
||||
Xapian::TermGenerator *term_gen;
|
||||
Xapian::RangeProcessor *value_range_processor;
|
||||
Xapian::RangeProcessor *date_range_processor;
|
||||
|
@ -244,6 +282,9 @@ struct _notmuch_database {
|
|||
|
||||
/* Cached and possibly overridden configuration */
|
||||
notmuch_string_map_t *config;
|
||||
|
||||
/* Track what parameters were specified when opening */
|
||||
notmuch_open_param_t params;
|
||||
};
|
||||
|
||||
/* Prior to database version 3, features were implied by the database
|
||||
|
@ -300,4 +341,38 @@ _notmuch_database_setup_standard_query_fields (notmuch_database_t *notmuch);
|
|||
notmuch_status_t
|
||||
_notmuch_database_setup_user_query_fields (notmuch_database_t *notmuch);
|
||||
|
||||
#if __cplusplus
|
||||
/* query.cc */
|
||||
notmuch_status_t
|
||||
_notmuch_query_string_to_xapian_query (notmuch_database_t *notmuch,
|
||||
std::string query_string,
|
||||
Xapian::Query &output,
|
||||
std::string &msg);
|
||||
/* parse-sexp.cc */
|
||||
notmuch_status_t
|
||||
_notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
|
||||
Xapian::Query &output);
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_query_expand (notmuch_database_t *notmuch, const char *field, Xapian::Query subquery,
|
||||
Xapian::Query &output, std::string &msg);
|
||||
|
||||
/* regexp-fields.cc */
|
||||
notmuch_status_t
|
||||
_notmuch_regexp_to_query (notmuch_database_t *notmuch, Xapian::valueno slot, std::string field,
|
||||
std::string regexp_str,
|
||||
Xapian::Query &output, std::string &msg);
|
||||
|
||||
/* thread-fp.cc */
|
||||
notmuch_status_t
|
||||
_notmuch_query_name_to_query (notmuch_database_t *notmuch, const std::string name,
|
||||
Xapian::Query &output);
|
||||
|
||||
#if HAVE_SFSEXP
|
||||
/* parse-sexp.cc */
|
||||
notmuch_status_t
|
||||
_notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
|
||||
Xapian::Query &output);
|
||||
#endif
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -309,6 +309,8 @@ notmuch_status_to_string (notmuch_status_t status)
|
|||
return "No database found";
|
||||
case NOTMUCH_STATUS_DATABASE_EXISTS:
|
||||
return "Database exists, not recreated";
|
||||
case NOTMUCH_STATUS_BAD_QUERY_SYNTAX:
|
||||
return "Syntax error in query";
|
||||
default:
|
||||
case NOTMUCH_STATUS_LAST_STATUS:
|
||||
return "Unknown error status value";
|
||||
|
|
|
@ -291,11 +291,16 @@ _notmuch_message_file_get_header (notmuch_message_file_t *message,
|
|||
if (value)
|
||||
return value;
|
||||
|
||||
if (strcasecmp (header, "received") == 0) {
|
||||
if (strcasecmp (header, "received") == 0 ||
|
||||
strcasecmp (header, "delivered-to") == 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.
|
||||
* 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.
|
||||
*
|
||||
* Similarly, multiple instances of Delivered-To may be present. We
|
||||
* concatenate them so the one with highest priority may be picked (eg.
|
||||
* primary_email before other_email).
|
||||
*/
|
||||
decoded = _notmuch_message_file_get_combined_header (message, header);
|
||||
} else {
|
||||
|
|
|
@ -58,7 +58,7 @@ NOTMUCH_BEGIN_DECLS
|
|||
* version in Makefile.local.
|
||||
*/
|
||||
#define LIBNOTMUCH_MAJOR_VERSION 5
|
||||
#define LIBNOTMUCH_MINOR_VERSION 4
|
||||
#define LIBNOTMUCH_MINOR_VERSION 5
|
||||
#define LIBNOTMUCH_MICRO_VERSION 0
|
||||
|
||||
|
||||
|
@ -220,6 +220,10 @@ typedef enum _notmuch_status {
|
|||
* Database exists, so not (re)-created
|
||||
*/
|
||||
NOTMUCH_STATUS_DATABASE_EXISTS,
|
||||
/**
|
||||
* Syntax error in query
|
||||
*/
|
||||
NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
|
||||
/**
|
||||
* Not an actual status value. Just a way to find out how many
|
||||
* valid status values there are.
|
||||
|
@ -429,6 +433,8 @@ notmuch_database_open_verbose (const char *path,
|
|||
* @retval NOTMUCH_STATUS_NULL_POINTER: The given \a database
|
||||
* argument is NULL.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_NO_CONFIG: No config file was found. Fatal.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_OUT_OF_MEMORY: Out of memory.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_FILE_ERROR: An error occurred trying to open the
|
||||
|
@ -454,6 +460,9 @@ notmuch_database_open_with_config (const char *database_path,
|
|||
*
|
||||
* For description of arguments, @see notmuch_database_open_with_config
|
||||
*
|
||||
* For errors other then NO_DATABASE and NO_CONFIG, *database is set to
|
||||
* NULL.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_SUCCESS: Successfully loaded configuration.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_NO_CONFIG: No config file was loaded. Not fatal.
|
||||
|
@ -485,6 +494,9 @@ notmuch_database_load_config (const char *database_path,
|
|||
*
|
||||
* For description of arguments, @see notmuch_database_open_with_config
|
||||
*
|
||||
* In case of any failure, this function returns an error status and
|
||||
* sets *database to NULL.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_SUCCESS: Successfully created the database.
|
||||
*
|
||||
* @retval NOTMUCH_STATUS_DATABASE_EXISTS: Database already exists, not created
|
||||
|
@ -961,6 +973,16 @@ notmuch_query_t *
|
|||
notmuch_query_create (notmuch_database_t *database,
|
||||
const char *query_string);
|
||||
|
||||
typedef enum {
|
||||
NOTMUCH_QUERY_SYNTAX_XAPIAN,
|
||||
NOTMUCH_QUERY_SYNTAX_SEXP
|
||||
} notmuch_query_syntax_t;
|
||||
|
||||
notmuch_status_t
|
||||
notmuch_query_create_with_syntax (notmuch_database_t *database,
|
||||
const char *query_string,
|
||||
notmuch_query_syntax_t syntax,
|
||||
notmuch_query_t **output);
|
||||
/**
|
||||
* Sort values for notmuch_query_set_sort.
|
||||
*/
|
||||
|
|
50
lib/open.cc
50
lib/open.cc
|
@ -247,7 +247,7 @@ _choose_database_path (void *ctx,
|
|||
}
|
||||
|
||||
static notmuch_database_t *
|
||||
_alloc_notmuch ()
|
||||
_alloc_notmuch (const char *database_path, const char *config_path, const char *profile)
|
||||
{
|
||||
notmuch_database_t *notmuch;
|
||||
|
||||
|
@ -263,6 +263,15 @@ _alloc_notmuch ()
|
|||
notmuch->transaction_count = 0;
|
||||
notmuch->transaction_threshold = 0;
|
||||
notmuch->view = 1;
|
||||
|
||||
notmuch->params = NOTMUCH_PARAM_NONE;
|
||||
if (database_path)
|
||||
notmuch->params |= NOTMUCH_PARAM_DATABASE;
|
||||
if (config_path)
|
||||
notmuch->params |= NOTMUCH_PARAM_CONFIG;
|
||||
if (profile)
|
||||
notmuch->params |= NOTMUCH_PARAM_PROFILE;
|
||||
|
||||
return notmuch;
|
||||
}
|
||||
|
||||
|
@ -396,8 +405,6 @@ _finish_open (notmuch_database_t *notmuch,
|
|||
" has a newer database format version (%u) than supported by this\n"
|
||||
" version of notmuch (%u).\n",
|
||||
database_path, version, NOTMUCH_DATABASE_VERSION));
|
||||
notmuch_database_destroy (notmuch);
|
||||
notmuch = NULL;
|
||||
status = NOTMUCH_STATUS_FILE_ERROR;
|
||||
goto DONE;
|
||||
}
|
||||
|
@ -414,8 +421,6 @@ _finish_open (notmuch_database_t *notmuch,
|
|||
" requires features (%s)\n"
|
||||
" not supported by this version of notmuch.\n",
|
||||
database_path, incompat_features));
|
||||
notmuch_database_destroy (notmuch);
|
||||
notmuch = NULL;
|
||||
status = NOTMUCH_STATUS_FILE_ERROR;
|
||||
goto DONE;
|
||||
}
|
||||
|
@ -432,7 +437,8 @@ _finish_open (notmuch_database_t *notmuch,
|
|||
"lastmod:");
|
||||
notmuch->query_parser->set_default_op (Xapian::Query::OP_AND);
|
||||
notmuch->query_parser->set_database (*notmuch->xapian_db);
|
||||
notmuch->query_parser->set_stemmer (Xapian::Stem ("english"));
|
||||
notmuch->stemmer = new Xapian::Stem ("english");
|
||||
notmuch->query_parser->set_stemmer (*notmuch->stemmer);
|
||||
notmuch->query_parser->set_stemming_strategy (Xapian::QueryParser::STEM_SOME);
|
||||
notmuch->query_parser->add_rangeprocessor (notmuch->value_range_processor);
|
||||
notmuch->query_parser->add_rangeprocessor (notmuch->date_range_processor);
|
||||
|
@ -488,8 +494,6 @@ _finish_open (notmuch_database_t *notmuch,
|
|||
} catch (const Xapian::Error &error) {
|
||||
IGNORE_RESULT (asprintf (&message, "A Xapian exception occurred opening database: %s\n",
|
||||
error.get_msg ().c_str ()));
|
||||
notmuch_database_destroy (notmuch);
|
||||
notmuch = NULL;
|
||||
status = NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||
}
|
||||
DONE:
|
||||
|
@ -515,7 +519,7 @@ notmuch_database_open_with_config (const char *database_path,
|
|||
|
||||
_notmuch_init ();
|
||||
|
||||
notmuch = _alloc_notmuch ();
|
||||
notmuch = _alloc_notmuch (database_path, config_path, profile);
|
||||
if (! notmuch) {
|
||||
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
goto DONE;
|
||||
|
@ -558,10 +562,13 @@ notmuch_database_open_with_config (const char *database_path,
|
|||
free (message);
|
||||
}
|
||||
|
||||
if (status && notmuch) {
|
||||
notmuch_database_destroy (notmuch);
|
||||
notmuch = NULL;
|
||||
}
|
||||
|
||||
if (database)
|
||||
*database = notmuch;
|
||||
else
|
||||
talloc_free (notmuch);
|
||||
|
||||
if (notmuch)
|
||||
notmuch->open = true;
|
||||
|
@ -612,7 +619,7 @@ notmuch_database_create_with_config (const char *database_path,
|
|||
|
||||
_notmuch_init ();
|
||||
|
||||
notmuch = _alloc_notmuch ();
|
||||
notmuch = _alloc_notmuch (database_path, config_path, profile);
|
||||
if (! notmuch) {
|
||||
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
goto DONE;
|
||||
|
@ -716,10 +723,16 @@ notmuch_database_create_with_config (const char *database_path,
|
|||
else
|
||||
free (message);
|
||||
}
|
||||
if (status && notmuch) {
|
||||
notmuch_database_destroy (notmuch);
|
||||
notmuch = NULL;
|
||||
}
|
||||
|
||||
if (database)
|
||||
*database = notmuch;
|
||||
else
|
||||
talloc_free (notmuch);
|
||||
|
||||
if (notmuch)
|
||||
notmuch->open = true;
|
||||
return status;
|
||||
}
|
||||
|
||||
|
@ -808,7 +821,7 @@ notmuch_database_load_config (const char *database_path,
|
|||
|
||||
_notmuch_init ();
|
||||
|
||||
notmuch = _alloc_notmuch ();
|
||||
notmuch = _alloc_notmuch (database_path, config_path, profile);
|
||||
if (! notmuch) {
|
||||
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
goto DONE;
|
||||
|
@ -867,6 +880,13 @@ notmuch_database_load_config (const char *database_path,
|
|||
if (status_string)
|
||||
*status_string = message;
|
||||
|
||||
if (status &&
|
||||
status != NOTMUCH_STATUS_NO_DATABASE
|
||||
&& status != NOTMUCH_STATUS_NO_CONFIG) {
|
||||
notmuch_database_destroy (notmuch);
|
||||
notmuch = NULL;
|
||||
}
|
||||
|
||||
if (database)
|
||||
*database = notmuch;
|
||||
|
||||
|
|
587
lib/parse-sexp.cc
Normal file
587
lib/parse-sexp.cc
Normal file
|
@ -0,0 +1,587 @@
|
|||
#include "database-private.h"
|
||||
|
||||
#if HAVE_SFSEXP
|
||||
#include "sexp.h"
|
||||
#include "unicode-util.h"
|
||||
|
||||
/* _sexp is used for file scope symbols to avoid clashing with
|
||||
* definitions from sexp.h */
|
||||
|
||||
/* sexp_binding structs attach name to a sexp and a defining
|
||||
* context. The latter allows lazy evaluation of parameters whose
|
||||
* definition contains other parameters. Lazy evaluation is needed
|
||||
* because a primary goal of macros is to change the parent field for
|
||||
* a sexp.
|
||||
*/
|
||||
|
||||
typedef struct sexp_binding {
|
||||
const char *name;
|
||||
const sexp_t *sx;
|
||||
const struct sexp_binding *context;
|
||||
const struct sexp_binding *next;
|
||||
} _sexp_binding_t;
|
||||
|
||||
typedef enum {
|
||||
SEXP_FLAG_NONE = 0,
|
||||
SEXP_FLAG_FIELD = 1 << 0,
|
||||
SEXP_FLAG_BOOLEAN = 1 << 1,
|
||||
SEXP_FLAG_SINGLE = 1 << 2,
|
||||
SEXP_FLAG_WILDCARD = 1 << 3,
|
||||
SEXP_FLAG_REGEX = 1 << 4,
|
||||
SEXP_FLAG_DO_REGEX = 1 << 5,
|
||||
SEXP_FLAG_EXPAND = 1 << 6,
|
||||
SEXP_FLAG_DO_EXPAND = 1 << 7,
|
||||
SEXP_FLAG_ORPHAN = 1 << 8,
|
||||
} _sexp_flag_t;
|
||||
|
||||
/*
|
||||
* define bitwise operators to hide casts */
|
||||
|
||||
inline _sexp_flag_t
|
||||
operator| (_sexp_flag_t a, _sexp_flag_t b)
|
||||
{
|
||||
return static_cast<_sexp_flag_t>(
|
||||
static_cast<unsigned>(a) | static_cast<unsigned>(b));
|
||||
}
|
||||
|
||||
inline _sexp_flag_t
|
||||
operator& (_sexp_flag_t a, _sexp_flag_t b)
|
||||
{
|
||||
return static_cast<_sexp_flag_t>(
|
||||
static_cast<unsigned>(a) & static_cast<unsigned>(b));
|
||||
}
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
Xapian::Query::op xapian_op;
|
||||
Xapian::Query initial;
|
||||
_sexp_flag_t flags;
|
||||
} _sexp_prefix_t;
|
||||
|
||||
static _sexp_prefix_t prefixes[] =
|
||||
{
|
||||
{ "and", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_NONE },
|
||||
{ "attachment", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_EXPAND },
|
||||
{ "body", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD },
|
||||
{ "from", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "folder", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "id", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
|
||||
{ "infix", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_SINGLE | SEXP_FLAG_ORPHAN },
|
||||
{ "is", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "matching", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_DO_EXPAND },
|
||||
{ "mid", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
|
||||
{ "mimetype", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_EXPAND },
|
||||
{ "not", Xapian::Query::OP_AND_NOT, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_NONE },
|
||||
{ "of", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_DO_EXPAND },
|
||||
{ "or", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_NONE },
|
||||
{ "path", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX },
|
||||
{ "property", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "query", Xapian::Query::OP_INVALID, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_SINGLE | SEXP_FLAG_ORPHAN },
|
||||
{ "regex", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_SINGLE | SEXP_FLAG_DO_REGEX },
|
||||
{ "rx", Xapian::Query::OP_INVALID, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_SINGLE | SEXP_FLAG_DO_REGEX },
|
||||
{ "starts-with", Xapian::Query::OP_WILDCARD, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_SINGLE },
|
||||
{ "subject", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "tag", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "thread", Xapian::Query::OP_OR, Xapian::Query::MatchNothing,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_BOOLEAN | SEXP_FLAG_WILDCARD | SEXP_FLAG_REGEX | SEXP_FLAG_EXPAND },
|
||||
{ "to", Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD | SEXP_FLAG_EXPAND },
|
||||
{ }
|
||||
};
|
||||
|
||||
static notmuch_status_t _sexp_to_xapian_query (notmuch_database_t *notmuch,
|
||||
const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env,
|
||||
const sexp_t *sx,
|
||||
Xapian::Query &output);
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_combine_query (notmuch_database_t *notmuch,
|
||||
const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env,
|
||||
Xapian::Query::op operation,
|
||||
Xapian::Query left,
|
||||
const sexp_t *sx,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
Xapian::Query subquery;
|
||||
|
||||
notmuch_status_t status;
|
||||
|
||||
/* if we run out elements, return accumulator */
|
||||
|
||||
if (! sx) {
|
||||
output = left;
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
status = _sexp_to_xapian_query (notmuch, parent, env, sx, subquery);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
return _sexp_combine_query (notmuch,
|
||||
parent,
|
||||
env,
|
||||
operation,
|
||||
Xapian::Query (operation, left, subquery),
|
||||
sx->next, output);
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_parse_phrase (std::string term_prefix, const char *phrase, Xapian::Query &output)
|
||||
{
|
||||
Xapian::Utf8Iterator p (phrase);
|
||||
Xapian::Utf8Iterator end;
|
||||
std::vector<std::string> terms;
|
||||
|
||||
while (p != end) {
|
||||
Xapian::Utf8Iterator start;
|
||||
while (p != end && ! Xapian::Unicode::is_wordchar (*p))
|
||||
p++;
|
||||
|
||||
if (p == end)
|
||||
break;
|
||||
|
||||
start = p;
|
||||
|
||||
while (p != end && Xapian::Unicode::is_wordchar (*p))
|
||||
p++;
|
||||
|
||||
if (p != start) {
|
||||
std::string word (start, p);
|
||||
word = Xapian::Unicode::tolower (word);
|
||||
terms.push_back (term_prefix + word);
|
||||
}
|
||||
}
|
||||
output = Xapian::Query (Xapian::Query::OP_PHRASE, terms.begin (), terms.end ());
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_parse_wildcard (notmuch_database_t *notmuch,
|
||||
const _sexp_prefix_t *parent,
|
||||
unused(const _sexp_binding_t *env),
|
||||
std::string match,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
|
||||
std::string term_prefix = parent ? _notmuch_database_prefix (notmuch, parent->name) : "";
|
||||
|
||||
if (parent && ! (parent->flags & SEXP_FLAG_WILDCARD)) {
|
||||
_notmuch_database_log (notmuch, "'%s' does not support wildcard queries\n", parent->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
output = Xapian::Query (Xapian::Query::OP_WILDCARD,
|
||||
term_prefix + Xapian::Unicode::tolower (match));
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_parse_one_term (notmuch_database_t *notmuch, std::string term_prefix, const sexp_t *sx,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
Xapian::Stem stem = *(notmuch->stemmer);
|
||||
|
||||
if (sx->aty == SEXP_BASIC && unicode_word_utf8 (sx->val)) {
|
||||
std::string term = Xapian::Unicode::tolower (sx->val);
|
||||
|
||||
output = Xapian::Query ("Z" + term_prefix + stem (term));
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
} else {
|
||||
return _sexp_parse_phrase (term_prefix, sx->val, output);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_sexp_parse_regex (notmuch_database_t *notmuch,
|
||||
const _sexp_prefix_t *prefix, const _sexp_prefix_t *parent,
|
||||
unused(const _sexp_binding_t *env),
|
||||
std::string val, Xapian::Query &output)
|
||||
{
|
||||
if (! parent) {
|
||||
_notmuch_database_log (notmuch, "illegal '%s' outside field\n",
|
||||
prefix->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
if (! (parent->flags & SEXP_FLAG_REGEX)) {
|
||||
_notmuch_database_log (notmuch, "'%s' not supported in field '%s'\n",
|
||||
prefix->name, parent->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
std::string msg; /* ignored */
|
||||
|
||||
return _notmuch_regexp_to_query (notmuch, Xapian::BAD_VALUENO, parent->name,
|
||||
val, output, msg);
|
||||
}
|
||||
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_expand_query (notmuch_database_t *notmuch,
|
||||
const _sexp_prefix_t *prefix, const _sexp_prefix_t *parent,
|
||||
unused(const _sexp_binding_t *env), const sexp_t *sx, Xapian::Query &output)
|
||||
{
|
||||
Xapian::Query subquery;
|
||||
notmuch_status_t status;
|
||||
std::string msg;
|
||||
|
||||
if (! (parent->flags & SEXP_FLAG_EXPAND)) {
|
||||
_notmuch_database_log (notmuch, "'%s' unsupported inside '%s'\n", prefix->name, parent->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
status = _sexp_combine_query (notmuch, NULL, NULL, prefix->xapian_op, prefix->initial, sx,
|
||||
subquery);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
status = _notmuch_query_expand (notmuch, parent->name, subquery, output, msg);
|
||||
if (status) {
|
||||
_notmuch_database_log (notmuch, "error expanding query %s\n", msg.c_str ());
|
||||
}
|
||||
return status;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_parse_infix (notmuch_database_t *notmuch, const sexp_t *sx, Xapian::Query &output)
|
||||
{
|
||||
try {
|
||||
output = notmuch->query_parser->parse_query (sx->val, NOTMUCH_QUERY_PARSER_FLAGS);
|
||||
} catch (const Xapian::QueryParserError &error) {
|
||||
_notmuch_database_log (notmuch, "Syntax error in infix query: %s\n", sx->val);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
} catch (const Xapian::Error &error) {
|
||||
if (! notmuch->exception_reported) {
|
||||
_notmuch_database_log (notmuch,
|
||||
"A Xapian exception occurred parsing query: %s\n",
|
||||
error.get_msg ().c_str ());
|
||||
_notmuch_database_log_append (notmuch,
|
||||
"Query string was: %s\n",
|
||||
sx->val);
|
||||
notmuch->exception_reported = true;
|
||||
return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||
}
|
||||
}
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_parse_header (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env, const sexp_t *sx, Xapian::Query &output)
|
||||
{
|
||||
_sexp_prefix_t user_prefix;
|
||||
|
||||
user_prefix.name = sx->list->val;
|
||||
user_prefix.flags = SEXP_FLAG_FIELD | SEXP_FLAG_WILDCARD;
|
||||
|
||||
if (parent) {
|
||||
_notmuch_database_log (notmuch, "nested field: '%s' inside '%s'\n",
|
||||
sx->list->val, parent->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
parent = &user_prefix;
|
||||
|
||||
return _sexp_combine_query (notmuch, parent, env, Xapian::Query::OP_AND, Xapian::Query::MatchAll,
|
||||
sx->list->next, output);
|
||||
}
|
||||
|
||||
static _sexp_binding_t *
|
||||
_sexp_bind (void *ctx, const _sexp_binding_t *env, const char *name, const sexp_t *sx, const
|
||||
_sexp_binding_t *context)
|
||||
{
|
||||
_sexp_binding_t *binding = talloc (ctx, _sexp_binding_t);
|
||||
|
||||
binding->name = talloc_strdup (ctx, name);
|
||||
binding->sx = sx;
|
||||
binding->context = context;
|
||||
binding->next = env;
|
||||
return binding;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
maybe_apply_macro (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env, const sexp_t *sx, const sexp_t *args,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
const sexp_t *params, *param, *arg, *body;
|
||||
void *local = talloc_new (notmuch);
|
||||
_sexp_binding_t *new_env = NULL;
|
||||
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
|
||||
|
||||
if (sx->list->ty != SEXP_VALUE || strcmp (sx->list->val, "macro") != 0) {
|
||||
status = NOTMUCH_STATUS_IGNORED;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
params = sx->list->next;
|
||||
|
||||
if (! params || (params->ty != SEXP_LIST)) {
|
||||
_notmuch_database_log (notmuch, "missing (possibly empty) list of arguments to macro\n");
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
body = params->next;
|
||||
|
||||
if (! body) {
|
||||
_notmuch_database_log (notmuch, "missing body of macro\n");
|
||||
status = NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
for (param = params->list, arg = args;
|
||||
param && arg;
|
||||
param = param->next, arg = arg->next) {
|
||||
if (param->ty != SEXP_VALUE || param->aty != SEXP_BASIC) {
|
||||
_notmuch_database_log (notmuch, "macro parameters must be unquoted atoms\n");
|
||||
status = NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
goto DONE;
|
||||
}
|
||||
new_env = _sexp_bind (local, new_env, param->val, arg, env);
|
||||
}
|
||||
|
||||
if (param && ! arg) {
|
||||
_notmuch_database_log (notmuch, "too few arguments to macro\n");
|
||||
status = NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
if (! param && arg) {
|
||||
_notmuch_database_log (notmuch, "too many arguments to macro\n");
|
||||
status = NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
status = _sexp_to_xapian_query (notmuch, parent, new_env, body, output);
|
||||
|
||||
DONE:
|
||||
if (local)
|
||||
talloc_free (local);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
maybe_saved_squery (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env, const sexp_t *sx, Xapian::Query &output)
|
||||
{
|
||||
char *key;
|
||||
char *expansion = NULL;
|
||||
notmuch_status_t status;
|
||||
sexp_t *saved_sexp;
|
||||
void *local = talloc_new (notmuch);
|
||||
char *buf;
|
||||
|
||||
key = talloc_asprintf (local, "squery.%s", sx->list->val);
|
||||
if (! key) {
|
||||
status = NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
status = notmuch_database_get_config (notmuch, key, &expansion);
|
||||
if (status)
|
||||
goto DONE;
|
||||
if (EMPTY_STRING (expansion)) {
|
||||
status = NOTMUCH_STATUS_IGNORED;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
buf = talloc_strdup (local, expansion);
|
||||
/* XXX TODO: free this memory */
|
||||
saved_sexp = parse_sexp (buf, strlen (expansion));
|
||||
if (! saved_sexp) {
|
||||
_notmuch_database_log (notmuch, "invalid saved s-expression query: '%s'\n", expansion);
|
||||
status = NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
status = maybe_apply_macro (notmuch, parent, env, saved_sexp, sx->list->next, output);
|
||||
if (status == NOTMUCH_STATUS_IGNORED)
|
||||
status = _sexp_to_xapian_query (notmuch, parent, env, saved_sexp, output);
|
||||
|
||||
DONE:
|
||||
if (local)
|
||||
talloc_free (local);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_expand_param (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env, const char *name,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
for (; env; env = env->next) {
|
||||
if (strcmp (name, env->name) == 0) {
|
||||
return _sexp_to_xapian_query (notmuch, parent, env->context, env->sx,
|
||||
output);
|
||||
}
|
||||
}
|
||||
_notmuch_database_log (notmuch, "undefined parameter %s\n", name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
/* Here we expect the s-expression to be a proper list, with first
|
||||
* element defining and operation, or as a special case the empty
|
||||
* list */
|
||||
|
||||
static notmuch_status_t
|
||||
_sexp_to_xapian_query (notmuch_database_t *notmuch, const _sexp_prefix_t *parent,
|
||||
const _sexp_binding_t *env, const sexp_t *sx, Xapian::Query &output)
|
||||
{
|
||||
notmuch_status_t status;
|
||||
|
||||
if (sx->ty == SEXP_VALUE && sx->aty == SEXP_BASIC && sx->val[0] == ',') {
|
||||
return _sexp_expand_param (notmuch, parent, env, sx->val + 1, output);
|
||||
}
|
||||
|
||||
if (sx->ty == SEXP_VALUE) {
|
||||
std::string term_prefix = parent ? _notmuch_database_prefix (notmuch, parent->name) : "";
|
||||
|
||||
if (sx->aty == SEXP_BASIC && strcmp (sx->val, "*") == 0) {
|
||||
return _sexp_parse_wildcard (notmuch, parent, env, "", output);
|
||||
}
|
||||
|
||||
if (parent && (parent->flags & SEXP_FLAG_BOOLEAN)) {
|
||||
output = Xapian::Query (term_prefix + sx->val);
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (parent) {
|
||||
return _sexp_parse_one_term (notmuch, term_prefix, sx, output);
|
||||
} else {
|
||||
Xapian::Query accumulator;
|
||||
for (_sexp_prefix_t *prefix = prefixes; prefix->name; prefix++) {
|
||||
if (prefix->flags & SEXP_FLAG_FIELD) {
|
||||
Xapian::Query subquery;
|
||||
term_prefix = _notmuch_database_prefix (notmuch, prefix->name);
|
||||
status = _sexp_parse_one_term (notmuch, term_prefix, sx, subquery);
|
||||
if (status)
|
||||
return status;
|
||||
accumulator = Xapian::Query (Xapian::Query::OP_OR, accumulator, subquery);
|
||||
}
|
||||
}
|
||||
output = accumulator;
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
}
|
||||
|
||||
/* Empty list */
|
||||
if (! sx->list) {
|
||||
output = Xapian::Query::MatchAll;
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
if (sx->list->ty == SEXP_LIST) {
|
||||
_notmuch_database_log (notmuch, "unexpected list in field/operation position\n",
|
||||
sx->list->val);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
status = maybe_saved_squery (notmuch, parent, env, sx, output);
|
||||
if (status != NOTMUCH_STATUS_IGNORED)
|
||||
return status;
|
||||
|
||||
/* Check for user defined field */
|
||||
if (_notmuch_string_map_get (notmuch->user_prefix, sx->list->val)) {
|
||||
return _sexp_parse_header (notmuch, parent, env, sx, output);
|
||||
}
|
||||
|
||||
if (strcmp (sx->list->val, "macro") == 0) {
|
||||
_notmuch_database_log (notmuch, "macro definition not permitted here\n");
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
for (_sexp_prefix_t *prefix = prefixes; prefix && prefix->name; prefix++) {
|
||||
if (strcmp (prefix->name, sx->list->val) == 0) {
|
||||
if (prefix->flags & SEXP_FLAG_FIELD) {
|
||||
if (parent) {
|
||||
_notmuch_database_log (notmuch, "nested field: '%s' inside '%s'\n",
|
||||
prefix->name, parent->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
parent = prefix;
|
||||
}
|
||||
|
||||
if (parent && (prefix->flags & SEXP_FLAG_ORPHAN)) {
|
||||
_notmuch_database_log (notmuch, "'%s' not supported inside '%s'\n",
|
||||
prefix->name, parent->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
if ((prefix->flags & SEXP_FLAG_SINGLE) &&
|
||||
(! sx->list->next || sx->list->next->next || sx->list->next->ty != SEXP_VALUE)) {
|
||||
_notmuch_database_log (notmuch, "'%s' expects single atom as argument\n",
|
||||
prefix->name);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
if (strcmp (prefix->name, "infix") == 0) {
|
||||
return _sexp_parse_infix (notmuch, sx->list->next, output);
|
||||
}
|
||||
|
||||
if (strcmp (prefix->name, "query") == 0) {
|
||||
return _notmuch_query_name_to_query (notmuch, sx->list->next->val, output);
|
||||
}
|
||||
|
||||
if (prefix->xapian_op == Xapian::Query::OP_WILDCARD)
|
||||
return _sexp_parse_wildcard (notmuch, parent, env, sx->list->next->val, output);
|
||||
|
||||
if (prefix->flags & SEXP_FLAG_DO_REGEX) {
|
||||
return _sexp_parse_regex (notmuch, prefix, parent, env, sx->list->next->val, output);
|
||||
}
|
||||
|
||||
if (prefix->flags & SEXP_FLAG_DO_EXPAND) {
|
||||
return _sexp_expand_query (notmuch, prefix, parent, env, sx->list->next, output);
|
||||
}
|
||||
|
||||
return _sexp_combine_query (notmuch, parent, env, prefix->xapian_op, prefix->initial,
|
||||
sx->list->next, output);
|
||||
}
|
||||
}
|
||||
|
||||
_notmuch_database_log (notmuch, "unknown prefix '%s'\n", sx->list->val);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_sexp_string_to_xapian_query (notmuch_database_t *notmuch, const char *querystr,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
const sexp_t *sx = NULL;
|
||||
char *buf = talloc_strdup (notmuch, querystr);
|
||||
|
||||
sx = parse_sexp (buf, strlen (querystr));
|
||||
if (! sx) {
|
||||
_notmuch_database_log (notmuch, "invalid s-expression: '%s'\n", querystr);
|
||||
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
|
||||
}
|
||||
|
||||
return _sexp_to_xapian_query (notmuch, NULL, NULL, sx, output);
|
||||
}
|
||||
#endif
|
|
@ -24,17 +24,33 @@
|
|||
#include "query-fp.h"
|
||||
#include <iostream>
|
||||
|
||||
Xapian::Query
|
||||
QueryFieldProcessor::operator() (const std::string & name)
|
||||
notmuch_status_t
|
||||
_notmuch_query_name_to_query (notmuch_database_t *notmuch, const std::string name,
|
||||
Xapian::Query &output)
|
||||
{
|
||||
std::string key = "query." + name;
|
||||
char *expansion;
|
||||
notmuch_status_t status;
|
||||
|
||||
status = notmuch_database_get_config (notmuch, key.c_str (), &expansion);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
output = notmuch->query_parser->parse_query (expansion, NOTMUCH_QUERY_PARSER_FLAGS);
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Xapian::Query
|
||||
QueryFieldProcessor::operator() (const std::string & name)
|
||||
{
|
||||
notmuch_status_t status;
|
||||
Xapian::Query output;
|
||||
|
||||
status = _notmuch_query_name_to_query (notmuch, name, output);
|
||||
if (status) {
|
||||
throw Xapian::QueryParserError ("error looking up key" + name);
|
||||
}
|
||||
|
||||
return parser.parse_query (expansion, NOTMUCH_QUERY_PARSER_FLAGS);
|
||||
return output;
|
||||
|
||||
}
|
||||
|
|
239
lib/query.cc
239
lib/query.cc
|
@ -30,6 +30,7 @@ struct _notmuch_query {
|
|||
notmuch_string_list_t *exclude_terms;
|
||||
notmuch_exclude_t omit_excluded;
|
||||
bool parsed;
|
||||
notmuch_query_syntax_t syntax;
|
||||
Xapian::Query xapian_query;
|
||||
std::set<std::string> terms;
|
||||
};
|
||||
|
@ -84,9 +85,9 @@ _notmuch_query_destructor (notmuch_query_t *query)
|
|||
return 0;
|
||||
}
|
||||
|
||||
notmuch_query_t *
|
||||
notmuch_query_create (notmuch_database_t *notmuch,
|
||||
const char *query_string)
|
||||
static notmuch_query_t *
|
||||
_notmuch_query_constructor (notmuch_database_t *notmuch,
|
||||
const char *query_string)
|
||||
{
|
||||
notmuch_query_t *query;
|
||||
|
||||
|
@ -105,7 +106,10 @@ notmuch_query_create (notmuch_database_t *notmuch,
|
|||
|
||||
query->notmuch = notmuch;
|
||||
|
||||
query->query_string = talloc_strdup (query, query_string);
|
||||
if (query_string)
|
||||
query->query_string = talloc_strdup (query, query_string);
|
||||
else
|
||||
query->query_string = NULL;
|
||||
|
||||
query->sort = NOTMUCH_SORT_NEWEST_FIRST;
|
||||
|
||||
|
@ -116,42 +120,142 @@ notmuch_query_create (notmuch_database_t *notmuch,
|
|||
return query;
|
||||
}
|
||||
|
||||
notmuch_query_t *
|
||||
notmuch_query_create (notmuch_database_t *notmuch,
|
||||
const char *query_string)
|
||||
{
|
||||
|
||||
notmuch_query_t *query;
|
||||
notmuch_status_t status;
|
||||
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_string,
|
||||
NOTMUCH_QUERY_SYNTAX_XAPIAN,
|
||||
&query);
|
||||
if (status)
|
||||
return NULL;
|
||||
|
||||
return query;
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
notmuch_query_create_with_syntax (notmuch_database_t *notmuch,
|
||||
const char *query_string,
|
||||
notmuch_query_syntax_t syntax,
|
||||
notmuch_query_t **output)
|
||||
{
|
||||
|
||||
notmuch_query_t *query;
|
||||
|
||||
if (! output)
|
||||
return NOTMUCH_STATUS_NULL_POINTER;
|
||||
|
||||
query = _notmuch_query_constructor (notmuch, query_string);
|
||||
if (! query)
|
||||
return NOTMUCH_STATUS_OUT_OF_MEMORY;
|
||||
|
||||
if (syntax == NOTMUCH_QUERY_SYNTAX_SEXP && ! HAVE_SFSEXP) {
|
||||
_notmuch_database_log (notmuch, "sexp query parser not available");
|
||||
return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
|
||||
query->syntax = syntax;
|
||||
|
||||
*output = query;
|
||||
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static void
|
||||
_notmuch_query_cache_terms (notmuch_query_t *query)
|
||||
{
|
||||
/* Xapian doesn't support skip_to on terms from a query since
|
||||
* they are unordered, so cache a copy of all terms in
|
||||
* something searchable.
|
||||
*/
|
||||
|
||||
for (Xapian::TermIterator t = query->xapian_query.get_terms_begin ();
|
||||
t != query->xapian_query.get_terms_end (); ++t)
|
||||
query->terms.insert (*t);
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_query_string_to_xapian_query (notmuch_database_t *notmuch,
|
||||
std::string query_string,
|
||||
Xapian::Query &output,
|
||||
std::string &msg)
|
||||
{
|
||||
try {
|
||||
if (query_string == "" || query_string == "*") {
|
||||
output = Xapian::Query::MatchAll;
|
||||
} else {
|
||||
output =
|
||||
notmuch->query_parser->
|
||||
parse_query (query_string, NOTMUCH_QUERY_PARSER_FLAGS);
|
||||
}
|
||||
} catch (const Xapian::Error &error) {
|
||||
if (! notmuch->exception_reported) {
|
||||
_notmuch_database_log (notmuch,
|
||||
"A Xapian exception occurred parsing query: %s\n",
|
||||
error.get_msg ().c_str ());
|
||||
_notmuch_database_log_append (notmuch,
|
||||
"Query string was: %s\n",
|
||||
query_string.c_str ());
|
||||
notmuch->exception_reported = true;
|
||||
}
|
||||
|
||||
msg = error.get_msg ();
|
||||
return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||
}
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_notmuch_query_ensure_parsed_xapian (notmuch_query_t *query)
|
||||
{
|
||||
notmuch_status_t status;
|
||||
std::string msg; /* ignored */
|
||||
|
||||
status = _notmuch_query_string_to_xapian_query (query->notmuch, query->query_string,
|
||||
query->xapian_query, msg);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
query->parsed = true;
|
||||
|
||||
_notmuch_query_cache_terms (query);
|
||||
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_notmuch_query_ensure_parsed_sexpr (notmuch_query_t *query)
|
||||
{
|
||||
notmuch_status_t status;
|
||||
|
||||
if (query->parsed)
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
|
||||
status = _notmuch_sexp_string_to_xapian_query (query->notmuch, query->query_string,
|
||||
query->xapian_query);
|
||||
if (status)
|
||||
return status;
|
||||
|
||||
_notmuch_query_cache_terms (query);
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
static notmuch_status_t
|
||||
_notmuch_query_ensure_parsed (notmuch_query_t *query)
|
||||
{
|
||||
if (query->parsed)
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
|
||||
try {
|
||||
query->xapian_query =
|
||||
query->notmuch->query_parser->
|
||||
parse_query (query->query_string, NOTMUCH_QUERY_PARSER_FLAGS);
|
||||
#if HAVE_SFSEXP
|
||||
if (query->syntax == NOTMUCH_QUERY_SYNTAX_SEXP)
|
||||
return _notmuch_query_ensure_parsed_sexpr (query);
|
||||
#endif
|
||||
|
||||
/* Xapian doesn't support skip_to on terms from a query since
|
||||
* they are unordered, so cache a copy of all terms in
|
||||
* something searchable.
|
||||
*/
|
||||
|
||||
for (Xapian::TermIterator t = query->xapian_query.get_terms_begin ();
|
||||
t != query->xapian_query.get_terms_end (); ++t)
|
||||
query->terms.insert (*t);
|
||||
|
||||
query->parsed = true;
|
||||
|
||||
} catch (const Xapian::Error &error) {
|
||||
if (! query->notmuch->exception_reported) {
|
||||
_notmuch_database_log (query->notmuch,
|
||||
"A Xapian exception occurred parsing query: %s\n",
|
||||
error.get_msg ().c_str ());
|
||||
_notmuch_database_log_append (query->notmuch,
|
||||
"Query string was: %s\n",
|
||||
query->query_string);
|
||||
query->notmuch->exception_reported = true;
|
||||
}
|
||||
|
||||
return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||
}
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
return _notmuch_query_ensure_parsed_xapian (query);
|
||||
}
|
||||
|
||||
const char *
|
||||
|
@ -249,7 +353,6 @@ _notmuch_query_search_documents (notmuch_query_t *query,
|
|||
notmuch_messages_t **out)
|
||||
{
|
||||
notmuch_database_t *notmuch = query->notmuch;
|
||||
const char *query_string = query->query_string;
|
||||
notmuch_mset_messages_t *messages;
|
||||
notmuch_status_t status;
|
||||
|
||||
|
@ -279,13 +382,9 @@ _notmuch_query_search_documents (notmuch_query_t *query,
|
|||
Xapian::MSet mset;
|
||||
Xapian::MSetIterator iterator;
|
||||
|
||||
if (strcmp (query_string, "") == 0 ||
|
||||
strcmp (query_string, "*") == 0) {
|
||||
final_query = mail_query;
|
||||
} else {
|
||||
final_query = Xapian::Query (Xapian::Query::OP_AND,
|
||||
mail_query, query->xapian_query);
|
||||
}
|
||||
final_query = Xapian::Query (Xapian::Query::OP_AND,
|
||||
mail_query, query->xapian_query);
|
||||
|
||||
messages->base.excluded_doc_ids = NULL;
|
||||
|
||||
if ((query->omit_excluded != NOTMUCH_EXCLUDE_FALSE) && (query->exclude_terms)) {
|
||||
|
@ -606,7 +705,6 @@ notmuch_status_t
|
|||
_notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsigned *count_out)
|
||||
{
|
||||
notmuch_database_t *notmuch = query->notmuch;
|
||||
const char *query_string = query->query_string;
|
||||
Xapian::doccount count = 0;
|
||||
notmuch_status_t status;
|
||||
|
||||
|
@ -622,13 +720,8 @@ _notmuch_query_count_documents (notmuch_query_t *query, const char *type, unsign
|
|||
Xapian::Query final_query, exclude_query;
|
||||
Xapian::MSet mset;
|
||||
|
||||
if (strcmp (query_string, "") == 0 ||
|
||||
strcmp (query_string, "*") == 0) {
|
||||
final_query = mail_query;
|
||||
} else {
|
||||
final_query = Xapian::Query (Xapian::Query::OP_AND,
|
||||
mail_query, query->xapian_query);
|
||||
}
|
||||
final_query = Xapian::Query (Xapian::Query::OP_AND,
|
||||
mail_query, query->xapian_query);
|
||||
|
||||
exclude_query = _notmuch_exclude_tags (query);
|
||||
|
||||
|
@ -728,3 +821,51 @@ notmuch_query_get_database (const notmuch_query_t *query)
|
|||
{
|
||||
return query->notmuch;
|
||||
}
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_query_expand (notmuch_database_t *notmuch, const char *field, Xapian::Query subquery,
|
||||
Xapian::Query &output, std::string &msg)
|
||||
{
|
||||
std::set<std::string> terms;
|
||||
const std::string term_prefix = _find_prefix (field);
|
||||
|
||||
if (_debug_query ()) {
|
||||
fprintf (stderr, "Expanding subquery:\n%s\n",
|
||||
subquery.get_description ().c_str ());
|
||||
}
|
||||
|
||||
try {
|
||||
Xapian::Enquire enquire (*notmuch->xapian_db);
|
||||
Xapian::MSet mset;
|
||||
|
||||
enquire.set_weighting_scheme (Xapian::BoolWeight ());
|
||||
enquire.set_query (subquery);
|
||||
|
||||
mset = enquire.get_mset (0, notmuch->xapian_db->get_doccount ());
|
||||
|
||||
for (Xapian::MSetIterator iterator = mset.begin (); iterator != mset.end (); iterator++) {
|
||||
Xapian::docid doc_id = *iterator;
|
||||
Xapian::Document doc = notmuch->xapian_db->get_document (doc_id);
|
||||
Xapian::TermIterator i = doc.termlist_begin ();
|
||||
|
||||
for (i.skip_to (term_prefix);
|
||||
i != doc.termlist_end () && ((*i).rfind (term_prefix, 0) == 0); i++) {
|
||||
terms.insert (*i);
|
||||
}
|
||||
}
|
||||
output = Xapian::Query (Xapian::Query::OP_OR, terms.begin (), terms.end ());
|
||||
if (_debug_query ()) {
|
||||
fprintf (stderr, "Expanded query:\n%s\n",
|
||||
subquery.get_description ().c_str ());
|
||||
}
|
||||
|
||||
} catch (const Xapian::Error &error) {
|
||||
_notmuch_database_log (notmuch,
|
||||
"A Xapian exception occurred expanding query: %s\n",
|
||||
error.get_msg ().c_str ());
|
||||
msg = error.get_msg ();
|
||||
return NOTMUCH_STATUS_XAPIAN_EXCEPTION;
|
||||
}
|
||||
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
|
|
@ -26,27 +26,32 @@
|
|||
#include "notmuch-private.h"
|
||||
#include "database-private.h"
|
||||
|
||||
static void
|
||||
compile_regex (regex_t ®exp, const char *str)
|
||||
notmuch_status_t
|
||||
compile_regex (regex_t ®exp, const char *str, std::string &msg)
|
||||
{
|
||||
int err = regcomp (®exp, str, REG_EXTENDED | REG_NOSUB);
|
||||
|
||||
if (err != 0) {
|
||||
size_t len = regerror (err, ®exp, NULL, 0);
|
||||
char *buffer = new char[len];
|
||||
std::string msg = "Regexp error: ";
|
||||
msg = "Regexp error: ";
|
||||
(void) regerror (err, ®exp, buffer, len);
|
||||
msg.append (buffer, len);
|
||||
delete[] buffer;
|
||||
|
||||
throw Xapian::QueryParserError (msg);
|
||||
return NOTMUCH_STATUS_ILLEGAL_ARGUMENT;
|
||||
}
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
RegexpPostingSource::RegexpPostingSource (Xapian::valueno slot, const std::string ®exp)
|
||||
: slot_ (slot)
|
||||
{
|
||||
compile_regex (regexp_, regexp.c_str ());
|
||||
std::string msg;
|
||||
notmuch_status_t status = compile_regex (regexp_, regexp.c_str (), msg);
|
||||
|
||||
if (status)
|
||||
throw Xapian::QueryParserError (msg);
|
||||
}
|
||||
|
||||
RegexpPostingSource::~RegexpPostingSource ()
|
||||
|
@ -141,18 +146,54 @@ _find_slot (std::string prefix)
|
|||
return Xapian::BAD_VALUENO;
|
||||
}
|
||||
|
||||
RegexpFieldProcessor::RegexpFieldProcessor (std::string prefix,
|
||||
RegexpFieldProcessor::RegexpFieldProcessor (std::string field_,
|
||||
notmuch_field_flag_t options_,
|
||||
Xapian::QueryParser &parser_,
|
||||
notmuch_database_t *notmuch_)
|
||||
: slot (_find_slot (prefix)),
|
||||
term_prefix (_find_prefix (prefix.c_str ())),
|
||||
: slot (_find_slot (field_)),
|
||||
field (field_),
|
||||
term_prefix (_find_prefix (field_.c_str ())),
|
||||
options (options_),
|
||||
parser (parser_),
|
||||
notmuch (notmuch_)
|
||||
{
|
||||
};
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_regexp_to_query (notmuch_database_t *notmuch, Xapian::valueno slot, std::string field,
|
||||
std::string regexp_str,
|
||||
Xapian::Query &output, std::string &msg)
|
||||
{
|
||||
regex_t regexp;
|
||||
notmuch_status_t status;
|
||||
|
||||
status = compile_regex (regexp, regexp_str.c_str (), msg);
|
||||
if (status) {
|
||||
_notmuch_database_log_append (notmuch, "error compiling regex %s", msg.c_str ());
|
||||
return status;
|
||||
}
|
||||
|
||||
if (slot == Xapian::BAD_VALUENO)
|
||||
slot = _find_slot (field);
|
||||
|
||||
if (slot == Xapian::BAD_VALUENO) {
|
||||
std::string term_prefix = _find_prefix (field.c_str ());
|
||||
std::vector<std::string> terms;
|
||||
|
||||
for (Xapian::TermIterator it = notmuch->xapian_db->allterms_begin (term_prefix);
|
||||
it != notmuch->xapian_db->allterms_end (); ++it) {
|
||||
if (regexec (®exp, (*it).c_str () + term_prefix.size (),
|
||||
0, NULL, 0) == 0)
|
||||
terms.push_back (*it);
|
||||
}
|
||||
output = Xapian::Query (Xapian::Query::OP_OR, terms.begin (), terms.end ());
|
||||
} else {
|
||||
RegexpPostingSource *postings = new RegexpPostingSource (slot, regexp_str);
|
||||
output = Xapian::Query (postings->release ());
|
||||
}
|
||||
return NOTMUCH_STATUS_SUCCESS;
|
||||
}
|
||||
|
||||
Xapian::Query
|
||||
RegexpFieldProcessor::operator() (const std::string & str)
|
||||
{
|
||||
|
@ -168,23 +209,15 @@ RegexpFieldProcessor::operator() (const std::string & str)
|
|||
|
||||
if (str.at (0) == '/') {
|
||||
if (str.length () > 1 && str.at (str.size () - 1) == '/') {
|
||||
Xapian::Query query;
|
||||
std::string regexp_str = str.substr (1, str.size () - 2);
|
||||
if (slot != Xapian::BAD_VALUENO) {
|
||||
RegexpPostingSource *postings = new RegexpPostingSource (slot, regexp_str);
|
||||
return Xapian::Query (postings->release ());
|
||||
} else {
|
||||
std::vector<std::string> terms;
|
||||
regex_t regexp;
|
||||
std::string msg;
|
||||
notmuch_status_t status;
|
||||
|
||||
compile_regex (regexp, regexp_str.c_str ());
|
||||
for (Xapian::TermIterator it = notmuch->xapian_db->allterms_begin (term_prefix);
|
||||
it != notmuch->xapian_db->allterms_end (); ++it) {
|
||||
if (regexec (®exp, (*it).c_str () + term_prefix.size (),
|
||||
0, NULL, 0) == 0)
|
||||
terms.push_back (*it);
|
||||
}
|
||||
return Xapian::Query (Xapian::Query::OP_OR, terms.begin (), terms.end ());
|
||||
}
|
||||
status = _notmuch_regexp_to_query (notmuch, slot, field, regexp_str, query, msg);
|
||||
if (status)
|
||||
throw Xapian::QueryParserError (msg);
|
||||
return query;
|
||||
} else {
|
||||
throw Xapian::QueryParserError ("unmatched regex delimiter in '" + str + "'");
|
||||
}
|
||||
|
|
|
@ -30,6 +30,11 @@
|
|||
#include "database-private.h"
|
||||
#include "notmuch-private.h"
|
||||
|
||||
notmuch_status_t
|
||||
_notmuch_regex_to_query (notmuch_database_t *notmuch, Xapian::valueno slot, std::string field,
|
||||
std::string regexp_str,
|
||||
Xapian::Query &output, std::string &msg);
|
||||
|
||||
/* A posting source that returns documents where a value matches a
|
||||
* regexp.
|
||||
*/
|
||||
|
@ -64,6 +69,7 @@ public:
|
|||
class RegexpFieldProcessor : public Xapian::FieldProcessor {
|
||||
protected:
|
||||
Xapian::valueno slot;
|
||||
std::string field;
|
||||
std::string term_prefix;
|
||||
notmuch_field_flag_t options;
|
||||
Xapian::QueryParser &parser;
|
||||
|
|
|
@ -34,28 +34,20 @@ ThreadFieldProcessor::operator() (const std::string & str)
|
|||
if (str.size () <= 1 || str.at (str.size () - 1) != '}') {
|
||||
throw Xapian::QueryParserError ("missing } in '" + str + "'");
|
||||
} else {
|
||||
Xapian::Query subquery;
|
||||
Xapian::Query query;
|
||||
std::string msg;
|
||||
std::string subquery_str = str.substr (1, str.size () - 2);
|
||||
notmuch_query_t *subquery = notmuch_query_create (notmuch, subquery_str.c_str ());
|
||||
notmuch_messages_t *messages;
|
||||
std::set<std::string> terms;
|
||||
|
||||
if (! subquery)
|
||||
throw Xapian::QueryParserError ("failed to create subquery for '" + subquery_str +
|
||||
"'");
|
||||
|
||||
status = notmuch_query_search_messages (subquery, &messages);
|
||||
status = _notmuch_query_string_to_xapian_query (notmuch, subquery_str, subquery, msg);
|
||||
if (status)
|
||||
throw Xapian::QueryParserError ("failed to search messages for '" + subquery_str +
|
||||
"'");
|
||||
throw Xapian::QueryParserError (msg);
|
||||
|
||||
for (; notmuch_messages_valid (messages); notmuch_messages_move_to_next (messages)) {
|
||||
std::string term = thread_prefix;
|
||||
notmuch_message_t *message;
|
||||
message = notmuch_messages_get (messages);
|
||||
term += _notmuch_message_get_thread_id_only (message);
|
||||
terms.insert (term);
|
||||
}
|
||||
return Xapian::Query (Xapian::Query::OP_OR, terms.begin (), terms.end ());
|
||||
status = _notmuch_query_expand (notmuch, "thread", subquery, query, msg);
|
||||
if (status)
|
||||
throw Xapian::QueryParserError (msg);
|
||||
|
||||
return query;
|
||||
}
|
||||
} else {
|
||||
/* literal thread id */
|
||||
|
|
|
@ -485,11 +485,11 @@ print_status_gzbytes (const char *loc,
|
|||
|
||||
#include "command-line-arguments.h"
|
||||
|
||||
extern const char *notmuch_requested_db_uuid;
|
||||
extern const notmuch_opt_desc_t notmuch_shared_options [];
|
||||
void notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch);
|
||||
|
||||
void notmuch_process_shared_options (const char *subcommand_name);
|
||||
notmuch_query_syntax_t shared_option_query_syntax ();
|
||||
|
||||
void notmuch_process_shared_options (notmuch_database_t *notmuch, const char *subcommand_name);
|
||||
int notmuch_minimal_options (const char *subcommand_name,
|
||||
int argc, char **argv);
|
||||
|
||||
|
|
|
@ -45,12 +45,7 @@ notmuch_compact_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (notmuch_requested_db_uuid) {
|
||||
fprintf (stderr, "Error: --uuid not implemented for compact\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (NULL, argv[0]);
|
||||
|
||||
if (! quiet)
|
||||
printf ("Compacting database...\n");
|
||||
|
|
|
@ -517,6 +517,7 @@ static const struct config_key
|
|||
{ "index.decrypt", false, NULL },
|
||||
{ "index.header.", true, validate_field_name },
|
||||
{ "query.", true, NULL },
|
||||
{ "squery.", true, validate_field_name },
|
||||
};
|
||||
|
||||
static const config_key_info_t *
|
||||
|
@ -679,6 +680,9 @@ _notmuch_config_list_built_with ()
|
|||
printf ("%sretry_lock=%s\n",
|
||||
BUILT_WITH_PREFIX,
|
||||
notmuch_built_with ("retry_lock") ? "true" : "false");
|
||||
printf ("%ssexpr_query=%s\n",
|
||||
BUILT_WITH_PREFIX,
|
||||
notmuch_built_with ("sexpr_query") ? "true" : "false");
|
||||
}
|
||||
|
||||
static int
|
||||
|
@ -708,10 +712,6 @@ notmuch_config_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (notmuch_requested_db_uuid)
|
||||
fprintf (stderr, "Warning: ignoring --uuid=%s\n",
|
||||
notmuch_requested_db_uuid);
|
||||
|
||||
/* skip at least subcommand argument */
|
||||
argc -= opt_index;
|
||||
argv += opt_index;
|
||||
|
|
|
@ -74,10 +74,12 @@ print_count (notmuch_database_t *notmuch, const char *query_str,
|
|||
int ret = 0;
|
||||
notmuch_status_t status;
|
||||
|
||||
query = notmuch_query_create (notmuch, query_str);
|
||||
if (query == NULL) {
|
||||
fprintf (stderr, "Out of memory\n");
|
||||
return -1;
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_str,
|
||||
shared_option_query_syntax (),
|
||||
&query);
|
||||
if (print_status_database ("notmuch count", notmuch, status)) {
|
||||
ret = -1;
|
||||
goto DONE;
|
||||
}
|
||||
|
||||
for (notmuch_config_values_start (exclude_tags);
|
||||
|
@ -182,7 +184,7 @@ notmuch_count_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
if (input_file_name) {
|
||||
batch = true;
|
||||
|
@ -201,8 +203,6 @@ notmuch_count_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
query_str = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
|
||||
if (query_str == NULL) {
|
||||
fprintf (stderr, "Out of memory.\n");
|
||||
|
|
|
@ -232,11 +232,12 @@ database_dump_file (notmuch_database_t *notmuch, gzFile output,
|
|||
if (! query_str)
|
||||
query_str = "";
|
||||
|
||||
query = notmuch_query_create (notmuch, query_str);
|
||||
if (query == NULL) {
|
||||
fprintf (stderr, "Out of memory\n");
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_str,
|
||||
shared_option_query_syntax (),
|
||||
&query);
|
||||
if (print_status_database ("notmuch dump", notmuch, status))
|
||||
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.
|
||||
*/
|
||||
|
@ -366,8 +367,6 @@ notmuch_dump_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
const char *query_str = NULL;
|
||||
int ret;
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
const char *output_file_name = NULL;
|
||||
int opt_index;
|
||||
|
||||
|
@ -394,7 +393,7 @@ notmuch_dump_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
if (include == 0)
|
||||
include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_TAGS | DUMP_INCLUDE_PROPERTIES;
|
||||
|
|
|
@ -478,7 +478,7 @@ notmuch_insert_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
mail_root = notmuch_config_get (notmuch, NOTMUCH_CONFIG_MAIL_ROOT);
|
||||
|
||||
|
@ -550,8 +550,6 @@ notmuch_insert_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
status = notmuch_process_shared_indexing_options (notmuch);
|
||||
if (status != NOTMUCH_STATUS_SUCCESS) {
|
||||
fprintf (stderr, "Error: Failed to process index options. (%s)\n",
|
||||
|
|
|
@ -1142,7 +1142,7 @@ notmuch_new_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
/* quiet trumps verbose */
|
||||
if (quiet)
|
||||
|
@ -1197,8 +1197,6 @@ notmuch_new_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
if (notmuch_database_get_revision (notmuch, NULL) == 0) {
|
||||
int count = 0;
|
||||
count_files (mail_root, &count, &add_files_state);
|
||||
|
|
|
@ -49,11 +49,11 @@ reindex_query (notmuch_database_t *notmuch, const char *query_string,
|
|||
|
||||
notmuch_status_t ret = NOTMUCH_STATUS_SUCCESS;
|
||||
|
||||
query = notmuch_query_create (notmuch, query_string);
|
||||
if (query == NULL) {
|
||||
fprintf (stderr, "Out of memory.\n");
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_string,
|
||||
shared_option_query_syntax (),
|
||||
&query);
|
||||
if (print_status_database ("notmuch reindex", notmuch, status))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* reindexing is not interested in any special sort order */
|
||||
notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED);
|
||||
|
@ -108,9 +108,7 @@ notmuch_reindex_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
status = notmuch_process_shared_indexing_options (notmuch);
|
||||
if (status != NOTMUCH_STATUS_SUCCESS) {
|
||||
|
|
|
@ -464,8 +464,8 @@ guess_from_in_received_by (notmuch_database_t *notmuch, const char *received)
|
|||
* (last Received: header added) and try to extract from them
|
||||
* indications to which email address this message was delivered.
|
||||
*
|
||||
* The Received: header is special in our get_header function and is
|
||||
* always concatenated.
|
||||
* The Received: header is among special ones in our get_header function
|
||||
* and is always concatenated.
|
||||
*
|
||||
* Return the address that was found, if any, and NULL otherwise.
|
||||
*/
|
||||
|
@ -499,6 +499,9 @@ guess_from_in_received_headers (notmuch_message_t *message)
|
|||
* headers: Envelope-To, X-Original-To, and Delivered-To (searched in
|
||||
* that order).
|
||||
*
|
||||
* The Delivered-To: header is among special ones in our get_header
|
||||
* function and is always concatenated.
|
||||
*
|
||||
* Return the address that was found, if any, and NULL otherwise.
|
||||
*/
|
||||
static const char *
|
||||
|
@ -716,6 +719,7 @@ notmuch_reply_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
};
|
||||
int format = FORMAT_DEFAULT;
|
||||
int reply_all = true;
|
||||
notmuch_status_t status;
|
||||
|
||||
notmuch_opt_desc_t options[] = {
|
||||
{ .opt_keyword = &format, .name = "format", .keywords =
|
||||
|
@ -743,7 +747,7 @@ notmuch_reply_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
notmuch_exit_if_unsupported_format ();
|
||||
|
||||
|
@ -758,13 +762,11 @@ notmuch_reply_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
query = notmuch_query_create (notmuch, query_string);
|
||||
if (query == NULL) {
|
||||
fprintf (stderr, "Out of memory\n");
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_string,
|
||||
shared_option_query_syntax (),
|
||||
&query);
|
||||
if (print_status_database ("notmuch reply", notmuch, status))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
if (do_reply (notmuch, query, ¶ms, format, reply_all) != 0)
|
||||
return EXIT_FAILURE;
|
||||
|
|
|
@ -272,8 +272,7 @@ notmuch_restore_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
goto DONE;
|
||||
}
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
if (include == 0) {
|
||||
include = DUMP_INCLUDE_CONFIG | DUMP_INCLUDE_PROPERTIES | DUMP_INCLUDE_TAGS;
|
||||
|
|
|
@ -56,6 +56,7 @@ typedef struct {
|
|||
int format_sel;
|
||||
sprinter_t *format;
|
||||
int exclude;
|
||||
int query_syntax;
|
||||
notmuch_query_t *query;
|
||||
int sort;
|
||||
int output;
|
||||
|
@ -709,8 +710,6 @@ _notmuch_search_prepare (search_context_t *ctx, int argc, char *argv[])
|
|||
|
||||
notmuch_exit_if_unsupported_format ();
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (ctx->notmuch);
|
||||
|
||||
query_str = query_string_from_args (ctx->notmuch, argc, argv);
|
||||
if (query_str == NULL) {
|
||||
fprintf (stderr, "Out of memory.\n");
|
||||
|
@ -721,11 +720,11 @@ _notmuch_search_prepare (search_context_t *ctx, int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
ctx->query = notmuch_query_create (ctx->notmuch, query_str);
|
||||
if (ctx->query == NULL) {
|
||||
fprintf (stderr, "Out of memory\n");
|
||||
if (print_status_database ("notmuch search", ctx->notmuch,
|
||||
notmuch_query_create_with_syntax (ctx->notmuch, query_str,
|
||||
shared_option_query_syntax (),
|
||||
&ctx->query)))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_query_set_sort (ctx->query, ctx->sort);
|
||||
|
||||
|
@ -771,6 +770,7 @@ static search_context_t search_context = {
|
|||
.format_sel = NOTMUCH_FORMAT_TEXT,
|
||||
.exclude = NOTMUCH_EXCLUDE_TRUE,
|
||||
.sort = NOTMUCH_SORT_NEWEST_FIRST,
|
||||
.query_syntax = NOTMUCH_QUERY_SYNTAX_XAPIAN,
|
||||
.output = 0,
|
||||
.offset = 0,
|
||||
.limit = -1, /* unlimited */
|
||||
|
@ -827,7 +827,7 @@ notmuch_search_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
if (ctx->output != OUTPUT_FILES && ctx->output != OUTPUT_MESSAGES &&
|
||||
ctx->dupe != -1) {
|
||||
|
@ -893,7 +893,7 @@ notmuch_address_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
if (! (ctx->output & (OUTPUT_SENDER | OUTPUT_RECIPIENTS)))
|
||||
ctx->output |= OUTPUT_SENDER;
|
||||
|
|
|
@ -147,10 +147,6 @@ notmuch_setup_command (notmuch_database_t *notmuch,
|
|||
if (notmuch_minimal_options ("setup", argc, argv) < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
if (notmuch_requested_db_uuid)
|
||||
fprintf (stderr, "Warning: ignoring --uuid=%s\n",
|
||||
notmuch_requested_db_uuid);
|
||||
|
||||
config = notmuch_conffile_open (notmuch,
|
||||
notmuch_config_path (notmuch), true);
|
||||
if (! config)
|
||||
|
|
|
@ -1287,7 +1287,7 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
/* explicit decryption implies verification */
|
||||
if (params.crypto.decrypt == NOTMUCH_DECRYPT_NOSTASH ||
|
||||
|
@ -1353,8 +1353,6 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
query_string = query_string_from_args (notmuch, argc - opt_index, argv + opt_index);
|
||||
if (query_string == NULL) {
|
||||
fprintf (stderr, "Out of memory\n");
|
||||
|
@ -1366,11 +1364,11 @@ notmuch_show_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
query = notmuch_query_create (notmuch, query_string);
|
||||
if (query == NULL) {
|
||||
fprintf (stderr, "Out of memory\n");
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_string,
|
||||
shared_option_query_syntax (),
|
||||
&query);
|
||||
if (print_status_database ("notmuch show", notmuch, status))
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
notmuch_query_set_sort (query, sort);
|
||||
|
||||
|
|
|
@ -39,8 +39,8 @@ handle_sigint (unused (int sig))
|
|||
|
||||
|
||||
static char *
|
||||
_optimize_tag_query (void *ctx, const char *orig_query_string,
|
||||
const tag_op_list_t *list)
|
||||
_optimize_tag_query_infix (void *ctx, const char *orig_query_string,
|
||||
const tag_op_list_t *list)
|
||||
{
|
||||
/* This is subtler than it looks. Xapian ignores the '-' operator
|
||||
* at the beginning both queries and parenthesized groups and,
|
||||
|
@ -88,6 +88,33 @@ _optimize_tag_query (void *ctx, const char *orig_query_string,
|
|||
return query_string;
|
||||
}
|
||||
|
||||
static char *
|
||||
_optimize_tag_query (void *ctx, const char *orig_query_string,
|
||||
notmuch_query_syntax_t stx,
|
||||
const tag_op_list_t *list)
|
||||
{
|
||||
char *query_string;
|
||||
|
||||
if (stx == NOTMUCH_QUERY_SYNTAX_XAPIAN)
|
||||
return _optimize_tag_query_infix (ctx, orig_query_string, list);
|
||||
|
||||
/* Don't optimize if there are no tag changes. */
|
||||
if (tag_op_list_size (list) == 0)
|
||||
return talloc_strdup (ctx, orig_query_string);
|
||||
|
||||
query_string = talloc_asprintf (ctx, "(and %s", orig_query_string);
|
||||
for (size_t i = 0; i < tag_op_list_size (list) && query_string; i++) {
|
||||
query_string = talloc_asprintf_append_buffer (
|
||||
query_string, tag_op_list_isremove (list, i) ? " (tag \"%s\")" : " (not (tag \"%s\"))",
|
||||
tag_op_list_tag (list, i));
|
||||
}
|
||||
|
||||
if (query_string)
|
||||
query_string = talloc_strdup_append_buffer (query_string, ")");
|
||||
|
||||
return query_string;
|
||||
}
|
||||
|
||||
/* Tag messages matching 'query_string' according to 'tag_ops'
|
||||
*/
|
||||
static int
|
||||
|
@ -104,7 +131,9 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
|
|||
if (! (flags & TAG_FLAG_REMOVE_ALL)) {
|
||||
/* Optimize the query so it excludes messages that already
|
||||
* have the specified set of tags. */
|
||||
query_string = _optimize_tag_query (ctx, query_string, tag_ops);
|
||||
query_string = _optimize_tag_query (ctx, query_string,
|
||||
shared_option_query_syntax (),
|
||||
tag_ops);
|
||||
if (query_string == NULL) {
|
||||
fprintf (stderr, "Out of memory.\n");
|
||||
return 1;
|
||||
|
@ -112,11 +141,11 @@ tag_query (void *ctx, notmuch_database_t *notmuch, const char *query_string,
|
|||
flags |= TAG_FLAG_PRE_OPTIMIZED;
|
||||
}
|
||||
|
||||
query = notmuch_query_create (notmuch, query_string);
|
||||
if (query == NULL) {
|
||||
fprintf (stderr, "Out of memory.\n");
|
||||
status = notmuch_query_create_with_syntax (notmuch, query_string,
|
||||
shared_option_query_syntax (),
|
||||
&query);
|
||||
if (print_status_database ("notmuch tag", notmuch, status))
|
||||
return 1;
|
||||
}
|
||||
|
||||
/* tagging is not interested in any special sort order */
|
||||
notmuch_query_set_sort (query, NOTMUCH_SORT_UNSORTED);
|
||||
|
@ -220,7 +249,7 @@ notmuch_tag_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
if (opt_index < 0)
|
||||
return EXIT_FAILURE;
|
||||
|
||||
notmuch_process_shared_options (argv[0]);
|
||||
notmuch_process_shared_options (notmuch, argv[0]);
|
||||
|
||||
if (input_file_name) {
|
||||
batch = true;
|
||||
|
@ -261,8 +290,6 @@ notmuch_tag_command (notmuch_database_t *notmuch, int argc, char *argv[])
|
|||
}
|
||||
}
|
||||
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
|
||||
if (print_status_database (
|
||||
"notmuch restore",
|
||||
notmuch,
|
||||
|
|
36
notmuch.c
36
notmuch.c
|
@ -49,22 +49,38 @@ notmuch_command (notmuch_database_t *notmuch, int argc, char *argv[]);
|
|||
static int
|
||||
_help_for (const char *topic);
|
||||
|
||||
static void
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch);
|
||||
|
||||
static bool print_version = false, print_help = false;
|
||||
const char *notmuch_requested_db_uuid = NULL;
|
||||
static const char *notmuch_requested_db_uuid = NULL;
|
||||
static int query_syntax = NOTMUCH_QUERY_SYNTAX_XAPIAN;
|
||||
|
||||
const notmuch_opt_desc_t notmuch_shared_options [] = {
|
||||
{ .opt_bool = &print_version, .name = "version" },
|
||||
{ .opt_bool = &print_help, .name = "help" },
|
||||
{ .opt_string = ¬much_requested_db_uuid, .name = "uuid" },
|
||||
{ .opt_keyword = &query_syntax, .name = "query", .keywords =
|
||||
(notmuch_keyword_t []){ { "infix", NOTMUCH_QUERY_SYNTAX_XAPIAN },
|
||||
{ "sexp", NOTMUCH_QUERY_SYNTAX_SEXP },
|
||||
{ 0, 0 } } },
|
||||
|
||||
{ }
|
||||
};
|
||||
|
||||
notmuch_query_syntax_t
|
||||
shared_option_query_syntax ()
|
||||
{
|
||||
return query_syntax;
|
||||
}
|
||||
|
||||
/* any subcommand wanting to support these options should call
|
||||
* inherit notmuch_shared_options and call
|
||||
* notmuch_process_shared_options (subcommand_name);
|
||||
* notmuch_process_shared_options (notmuch, subcommand_name);
|
||||
* with notmuch = an open database, or NULL.
|
||||
*/
|
||||
void
|
||||
notmuch_process_shared_options (const char *subcommand_name)
|
||||
notmuch_process_shared_options (notmuch_database_t *notmuch, const char *subcommand_name)
|
||||
{
|
||||
if (print_version) {
|
||||
printf ("notmuch " STRINGIFY (NOTMUCH_VERSION) "\n");
|
||||
|
@ -75,6 +91,14 @@ notmuch_process_shared_options (const char *subcommand_name)
|
|||
int ret = _help_for (subcommand_name);
|
||||
exit (ret);
|
||||
}
|
||||
|
||||
if (notmuch) {
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch);
|
||||
} else {
|
||||
if (notmuch_requested_db_uuid)
|
||||
fprintf (stderr, "Warning: ignoring --uuid=%s\n",
|
||||
notmuch_requested_db_uuid);
|
||||
}
|
||||
}
|
||||
|
||||
/* This is suitable for subcommands that do not actually open the
|
||||
|
@ -97,7 +121,7 @@ notmuch_minimal_options (const char *subcommand_name,
|
|||
return -1;
|
||||
|
||||
/* We can't use argv here as it is sometimes NULL */
|
||||
notmuch_process_shared_options (subcommand_name);
|
||||
notmuch_process_shared_options (NULL, subcommand_name);
|
||||
return opt_index;
|
||||
}
|
||||
|
||||
|
@ -280,7 +304,7 @@ be supported in the future.\n", notmuch_format_version);
|
|||
}
|
||||
}
|
||||
|
||||
void
|
||||
static void
|
||||
notmuch_exit_if_unmatched_db_uuid (notmuch_database_t *notmuch)
|
||||
{
|
||||
const char *uuid = NULL;
|
||||
|
@ -480,7 +504,7 @@ main (int argc, char *argv[])
|
|||
if (opt_index < argc)
|
||||
command_name = argv[opt_index];
|
||||
|
||||
notmuch_process_shared_options (command_name);
|
||||
notmuch_process_shared_options (NULL, command_name);
|
||||
|
||||
command = find_command (command_name);
|
||||
/* if command->function is NULL, try external command */
|
||||
|
|
|
@ -51,6 +51,7 @@ cat <<EOF > EXPECTED
|
|||
built_with.compact=something
|
||||
built_with.field_processor=something
|
||||
built_with.retry_lock=something
|
||||
built_with.sexpr_query=something
|
||||
database.autocommit=8000
|
||||
database.mail_root=MAIL_DIR
|
||||
database.path=MAIL_DIR
|
||||
|
|
|
@ -266,7 +266,7 @@ EOF
|
|||
test_expect_equal "${output}+${output2}" "${value}+"
|
||||
|
||||
test_begin_subtest "Config list ($config)"
|
||||
notmuch config list | notmuch_dir_sanitize | \
|
||||
notmuch config list | notmuch_config_sanitize | \
|
||||
sed -e "s/^database.backup_dir=.*$/database.backup_dir/" \
|
||||
-e "s/^database.hook_dir=.*$/database.hook_dir/" \
|
||||
-e "s/^database.path=.*$/database.path/" \
|
||||
|
@ -274,9 +274,10 @@ EOF
|
|||
-e "s,^database.mail_root=CWD/home/env_points_here,database.mail_root=MAIL_DIR," \
|
||||
> OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
built_with.compact=true
|
||||
built_with.field_processor=true
|
||||
built_with.retry_lock=true
|
||||
built_with.compact=something
|
||||
built_with.field_processor=something
|
||||
built_with.retry_lock=something
|
||||
built_with.sexpr_query=something
|
||||
database.autocommit=8000
|
||||
database.backup_dir
|
||||
database.hook_dir
|
||||
|
@ -305,7 +306,23 @@ EOF
|
|||
output2=$(notmuch --config='' config get ${key})
|
||||
notmuch config set ${key}
|
||||
test_expect_equal "${output}+${output2}" "${value}+"
|
||||
;;
|
||||
;&
|
||||
split)
|
||||
test_begin_subtest "'to' header does not crash (python-cffi) ($config)"
|
||||
echo 'notmuch@notmuchmail.org' > EXPECTED
|
||||
test_python <<EOF
|
||||
from notmuch2 import Database
|
||||
db=Database(config=Database.CONFIG.SEARCH)
|
||||
m=db.find('20091117232137.GA7669@griffis1.net')
|
||||
to=m.header('To')
|
||||
print(to)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
;& # fall through
|
||||
esac
|
||||
|
||||
case $config in
|
||||
split|XDG*)
|
||||
esac
|
||||
restore_config
|
||||
rm -rf home/.local
|
||||
|
|
|
@ -154,4 +154,28 @@ print("4: {} messages".format(query.count_messages()))
|
|||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
|
||||
|
||||
test_begin_subtest "and of exact terms (query=sexp)"
|
||||
output=$(notmuch count --query=sexp '(and "wonderful" "wizard")')
|
||||
test_expect_equal "$output" 1
|
||||
|
||||
test_begin_subtest "or of exact terms (query=sexp)"
|
||||
output=$(notmuch count --query=sexp '(or "php" "wizard")')
|
||||
test_expect_equal "$output" 2
|
||||
|
||||
test_begin_subtest "starts-with, case-insensitive (query=sexp)"
|
||||
output=$(notmuch count --query=sexp '(starts-with FreeB)')
|
||||
test_expect_equal "$output" 5
|
||||
|
||||
test_begin_subtest "query that matches no messages (query=sexp)"
|
||||
count=$(notmuch count --query=sexp '(and (from keithp) (to keithp))')
|
||||
test_expect_equal 0 "$count"
|
||||
|
||||
test_begin_subtest "Compound subquery (query=sexp)"
|
||||
output=$(notmuch count --query=sexp '(thread (of (from keithp) (subject Maildir)))')
|
||||
test_expect_equal "$output" 7
|
||||
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
986
test/T081-sexpr-search.sh
Executable file
986
test/T081-sexpr-search.sh
Executable file
|
@ -0,0 +1,986 @@
|
|||
#!/usr/bin/env bash
|
||||
test_description='"notmuch search" in several variations'
|
||||
. $(dirname "$0")/test-lib.sh || exit 1
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -ne 1 ]; then
|
||||
printf "Skipping due to missing sfsexp library\n"
|
||||
test_done
|
||||
fi
|
||||
|
||||
add_email_corpus
|
||||
|
||||
for query in '()' '(not)' '(and)' '(or ())' '(or (not))' '(or (and))' \
|
||||
'(or (and) (or) (not (and)))'; do
|
||||
test_begin_subtest "all messages: $query"
|
||||
notmuch search '*' > EXPECTED
|
||||
notmuch search --query=sexp "$query" > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
done
|
||||
|
||||
for query in '(or)' '(not ())' '(not (not))' '(not (and))' \
|
||||
'(not (or (and) (or) (not (and))))'; do
|
||||
test_begin_subtest "no messages: $query"
|
||||
notmuch search --query=sexp "$query" > OUTPUT
|
||||
test_expect_equal_file /dev/null OUTPUT
|
||||
done
|
||||
|
||||
test_begin_subtest "and of exact terms"
|
||||
notmuch search --query=sexp '(and "wonderful" "wizard")' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "or of exact terms"
|
||||
notmuch search --query=sexp '(or "php" "wizard")' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2010-12-29 [1/1] François Boulogne; [aur-general] Guidelines: cp, mkdir vs install (inbox unread)
|
||||
thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "single term in body"
|
||||
notmuch search --query=sexp 'wizard' | notmuch_search_sanitize>OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "single term in body (case insensitive)"
|
||||
notmuch search --query=sexp 'Wizard' | notmuch_search_sanitize>OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [1/3] Carl Worth| Jan Janak; [notmuch] What a great idea! (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "single term in body, stemmed version"
|
||||
notmuch search arriv > EXPECTED
|
||||
notmuch search --query=sexp arriv > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "single term in body, unstemmed version"
|
||||
notmuch search --query=sexp '"arriv"' > OUTPUT
|
||||
test_expect_equal_file /dev/null OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'subject'"
|
||||
add_message [subject]=subjectsearchtest '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
output=$(notmuch search --query=sexp '(subject subjectsearchtest)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'subject' (case insensitive)"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(subject "Maildir")' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8):"
|
||||
add_message [subject]=utf8-sübjéct '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
output=$(notmuch search --query=sexp '(subject utf8 sübjéct)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8, and):"
|
||||
output=$(notmuch search --query=sexp '(subject (and utf8 sübjéct))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8, and outside):"
|
||||
output=$(notmuch search --query=sexp '(and (subject utf8) (subject sübjéct))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8, or):"
|
||||
notmuch search --query=sexp '(subject (or utf8 subjectsearchtest))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8, or outside):"
|
||||
notmuch search --query=sexp '(or (subject utf8) (subject subjectsearchtest))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; subjectsearchtest (inbox unread)
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'attachment'"
|
||||
notmuch search attachment:notmuch-help.patch > EXPECTED
|
||||
notmuch search --query=sexp '(attachment notmuch-help.patch)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'body'"
|
||||
add_message '[subject]="body search"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [body]=bodysearchtest
|
||||
output=$(notmuch search --query=sexp '(body bodysearchtest)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; body search (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'body' (phrase)"
|
||||
add_message '[subject]="body search (phrase)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="body search (phrase)"'
|
||||
add_message '[subject]="negative result"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="This phrase should not match the body search"'
|
||||
output=$(notmuch search --query=sexp '(body "body search phrase")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; body search (phrase) (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'body' (utf-8):"
|
||||
add_message '[subject]="utf8-message-body-subject"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[body]="message body utf8: bödý"'
|
||||
output=$(notmuch search --query=sexp '(body bödý)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-message-body-subject (inbox unread)"
|
||||
|
||||
add_message "[body]=thebody-1" "[subject]=kryptonite-1"
|
||||
add_message "[body]=nothing-to-see-here-1" "[subject]=thebody-1"
|
||||
|
||||
test_begin_subtest 'search without body: prefix'
|
||||
notmuch search thebody > EXPECTED
|
||||
notmuch search --query=sexp '(and thebody)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest 'negated body: prefix'
|
||||
notmuch search thebody and not body:thebody > EXPECTED
|
||||
notmuch search --query=sexp '(and (not (body thebody)) thebody)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest 'search unprefixed for prefixed term'
|
||||
notmuch search kryptonite > EXPECTED
|
||||
notmuch search --query=sexp '(and kryptonite)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest 'search with body: prefix for term only in subject'
|
||||
notmuch search body:kryptonite > EXPECTED
|
||||
notmuch search --query=sexp '(body kryptonite)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'from'"
|
||||
add_message '[subject]="search by from"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom
|
||||
output=$(notmuch search --query=sexp '(from searchbyfrom)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] searchbyfrom; search by from (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'from' (address)"
|
||||
add_message '[subject]="search by from (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [from]=searchbyfrom@example.com
|
||||
output=$(notmuch search --query=sexp '(from searchbyfrom@example.com)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] searchbyfrom@example.com; search by from (address) (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'from' (name)"
|
||||
add_message '[subject]="search by from (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[from]="Search By From Name <test@example.com>"'
|
||||
output=$(notmuch search --query=sexp '(from "Search By From Name")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'from' (name and address)"
|
||||
output=$(notmuch search --query=sexp '(from "Search By From Name <test@example.com>")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Search By From Name; search by from (name) (inbox unread)"
|
||||
|
||||
add_message '[dir]=bad' '[subject]="To the bone"'
|
||||
add_message '[dir]=.' '[subject]="Top level"'
|
||||
add_message '[dir]=bad/news' '[subject]="Bears"'
|
||||
mkdir -p "${MAIL_DIR}/duplicate/bad/news"
|
||||
cp "$gen_msg_filename" "${MAIL_DIR}/duplicate/bad/news"
|
||||
|
||||
add_message '[dir]=things' '[subject]="These are a few"'
|
||||
add_message '[dir]=things/favorite' '[subject]="Raindrops, whiskers, kettles"'
|
||||
add_message '[dir]=things/bad' '[subject]="Bites, stings, sad feelings"'
|
||||
|
||||
test_begin_subtest "Search by 'folder' (multiple)"
|
||||
output=$(notmuch search --query=sexp '(folder bad bad/news things/bad)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; To the bone (inbox unread)
|
||||
thread:XXX 2001-01-05 [1/1(2)] Notmuch Test Suite; Bears (inbox unread)
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Bites, stings, sad feelings (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'folder': top level."
|
||||
notmuch search folder:'""' > EXPECTED
|
||||
notmuch search --query=sexp '(folder "")' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'id'"
|
||||
add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
output=$(notmuch search --query=sexp "(id ${gen_msg_id})" | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'id' (or)"
|
||||
add_message '[subject]="search by id"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
output=$(notmuch search --query=sexp "(id non-existent-mid ${gen_msg_id})" | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by id (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'is' (multiple)"
|
||||
notmuch tag -inbox tag:searchbytag
|
||||
notmuch search is:inbox AND is:unread | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(is inbox unread)' | notmuch_search_sanitize > OUTPUT
|
||||
notmuch tag +inbox tag:searchbytag
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'mid'"
|
||||
add_message '[subject]="search by mid"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
output=$(notmuch search --query=sexp "(mid ${gen_msg_id})" | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by mid (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'mid' (or)"
|
||||
add_message '[subject]="search by mid"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
output=$(notmuch search --query=sexp "(mid non-existent-mid ${gen_msg_id})" | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by mid (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'mimetype'"
|
||||
notmuch search mimetype:text/html > EXPECTED
|
||||
notmuch search --query=sexp '(mimetype text html)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
QUERYSTR="date:2009-11-18..2009-11-18 and tag:unread"
|
||||
QUERYSTR2="query:test and subject:Maildir"
|
||||
notmuch config set --database query.test "$QUERYSTR"
|
||||
notmuch config set query.test2 "$QUERYSTR2"
|
||||
|
||||
test_begin_subtest "ill-formed named query search"
|
||||
notmuch search --query=sexp '(query)' > OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'query' expects single atom as argument
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "ill-formed named query search 2"
|
||||
notmuch search --query=sexp '(to (query))' > OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'query' not supported inside 'to'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "search named query"
|
||||
notmuch search --query=sexp '(query test)' > OUTPUT
|
||||
notmuch search $QUERYSTR > EXPECTED
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8, phrase-token):"
|
||||
output=$(notmuch search --query=sexp '(subject utf8-sübjéct)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
|
||||
|
||||
test_begin_subtest "search named query with other terms"
|
||||
notmuch search --query=sexp '(and (query test) (subject Maildir))' > OUTPUT
|
||||
notmuch search $QUERYSTR and subject:Maildir > EXPECTED
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "search nested named query"
|
||||
notmuch search --query=sexp '(query test2)' > OUTPUT
|
||||
notmuch search $QUERYSTR2 > EXPECTED
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'subject' (utf-8, quoted string):"
|
||||
output=$(notmuch search --query=sexp '(subject "utf8 sübjéct")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'subject' (combine phrase, term):"
|
||||
output=$(notmuch search --query=sexp '(subject Mac "compatibility issues")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'subject' (combine phrase, term 2):"
|
||||
notmuch search --query=sexp '(subject (or utf8 "compatibility issues"))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-sübjéct (inbox unread)
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; utf8-message-body-subject (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'subject' (combine phrase, term 3):"
|
||||
notmuch search --query=sexp '(subject issues X/Darwin)' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'tag'"
|
||||
add_message '[subject]="search by tag"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
notmuch tag +searchbytag id:${gen_msg_id}
|
||||
output=$(notmuch search --query=sexp '(tag searchbytag)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)"
|
||||
|
||||
test_begin_subtest "Search by 'tag' (multiple)"
|
||||
notmuch tag -inbox tag:searchbytag
|
||||
notmuch search tag:inbox AND tag:unread | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(tag inbox unread)' | notmuch_search_sanitize > OUTPUT
|
||||
notmuch tag +inbox tag:searchbytag
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'tag' and 'subject'"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(and (tag inbox) (subject maildir))' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search by 'thread'"
|
||||
add_message '[subject]="search by thread"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"'
|
||||
thread_id=$(notmuch search id:${gen_msg_id} | sed -e "s/thread:\([a-f0-9]*\).*/\1/")
|
||||
output=$(notmuch search --query=sexp "(thread ${thread_id})" | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by thread (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'to'"
|
||||
add_message '[subject]="search by to"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto
|
||||
output=$(notmuch search --query=sexp '(to searchbyto)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'to' (address)"
|
||||
add_message '[subject]="search by to (address)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' [to]=searchbyto@example.com
|
||||
output=$(notmuch search --query=sexp '(to searchbyto@example.com)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'to' (name)"
|
||||
add_message '[subject]="search by to (name)"' '[date]="Sat, 01 Jan 2000 12:00:00 -0000"' '[to]="Search By To Name <test@example.com>"'
|
||||
output=$(notmuch search --query=sexp '(to "Search By To Name")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search by 'to' (name and address)"
|
||||
output=$(notmuch search --query=sexp '(to "Search By To Name <test@example.com>")' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)"
|
||||
|
||||
test_begin_subtest "starts-with, no prefix"
|
||||
output=$(notmuch search --query=sexp '(starts-with prelim)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2009-11-17 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] preliminary FreeBSD support (attachment inbox unread)"
|
||||
|
||||
test_begin_subtest "starts-with, case-insensitive"
|
||||
notmuch search --query=sexp '(starts-with FreeB)' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [3/4] Alexander Botero-Lowry, Jjgod Jiang; [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
|
||||
thread:XXX 2009-11-17 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] preliminary FreeBSD support (attachment inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, no prefix, all messages"
|
||||
notmuch search --query=sexp '(starts-with "")' | notmuch_search_sanitize > OUTPUT
|
||||
notmuch search '*' | notmuch_search_sanitize > EXPECTED
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, attachment"
|
||||
output=$(notmuch search --query=sexp '(attachment (starts-with not))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" 'thread:XXX 2009-11-18 [2/2] Lars Kellogg-Stedman; [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)'
|
||||
|
||||
test_begin_subtest "starts-with, folder"
|
||||
notmuch search --output=files --query=sexp '(folder (starts-with bad))' | notmuch_dir_sanitize | sed 's/[0-9]*$/XXX/' > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
MAIL_DIR/bad/msg-XXX
|
||||
MAIL_DIR/bad/news/msg-XXX
|
||||
MAIL_DIR/duplicate/bad/news/msg-XXX
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, from"
|
||||
notmuch search --query=sexp '(from (starts-with Mik))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-17 [1/1] Mikhail Gusarov; [notmuch] [PATCH] Handle rename of message file (inbox unread)
|
||||
thread:XXX 2009-11-17 [2/7] Mikhail Gusarov| Lars Kellogg-Stedman, Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
|
||||
thread:XXX 2009-11-17 [2/5] Mikhail Gusarov| Carl Worth, Keith Packard; [notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++ file with gcc 4.4 (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, id"
|
||||
notmuch search --query=sexp --output=messages '(id (starts-with 877))' > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
id:877h1wv7mg.fsf@inf-8657.int-evry.fr
|
||||
id:877htoqdbo.fsf@yoom.home.cworth.org
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, is"
|
||||
output=$(notmuch search --query=sexp '(is (starts-with searchby))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" 'thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)'
|
||||
|
||||
test_begin_subtest "starts-with, mid"
|
||||
notmuch search --query=sexp --output=messages '(mid (starts-with 877))' > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
id:877h1wv7mg.fsf@inf-8657.int-evry.fr
|
||||
id:877htoqdbo.fsf@yoom.home.cworth.org
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, mimetype"
|
||||
notmuch search --query=sexp '(mimetype (starts-with sig))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [2/2] Lars Kellogg-Stedman; [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)
|
||||
thread:XXX 2009-11-18 [4/7] Lars Kellogg-Stedman, Mikhail Gusarov| Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
|
||||
thread:XXX 2009-11-17 [1/3] Adrian Perez de Castro| Keith Packard, Carl Worth; [notmuch] Introducing myself (inbox signed unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
add_message '[subject]="message with properties"'
|
||||
notmuch restore <<EOF
|
||||
#= ${gen_msg_id} foo=bar
|
||||
EOF
|
||||
|
||||
test_begin_subtest "starts-with, property"
|
||||
notmuch search --query=sexp '(property (starts-with foo=))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; message with properties (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, subject"
|
||||
notmuch search --query=sexp '(subject (starts-with FreeB))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2009-11-17 [2/2] Alex Botero-Lowry, Carl Worth; [notmuch] preliminary FreeBSD support (attachment inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, tag"
|
||||
output=$(notmuch search --query=sexp '(tag (starts-with searchby))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" 'thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by tag (inbox searchbytag unread)'
|
||||
|
||||
add_message '[subject]="no tags"'
|
||||
notag_mid=${gen_msg_id}
|
||||
notmuch tag -unread -inbox id:${notag_mid}
|
||||
|
||||
test_begin_subtest "negated starts-with, tag"
|
||||
output=$(notmuch search --query=sexp '(tag (not (starts-with in)))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" 'thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; no tags ()'
|
||||
|
||||
test_begin_subtest "negated starts-with, tag 2"
|
||||
output=$(notmuch search --query=sexp '(not (tag (starts-with in)))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" 'thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; no tags ()'
|
||||
|
||||
test_begin_subtest "negated starts-with, tag 3"
|
||||
output=$(notmuch search --query=sexp '(not (tag (starts-with "")))' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" 'thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; no tags ()'
|
||||
|
||||
test_begin_subtest "starts-with, thread"
|
||||
notmuch search --query=sexp '(thread (starts-with "00"))' > OUTPUT
|
||||
notmuch search '*' > EXPECTED
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, to"
|
||||
notmuch search --query=sexp '(to (starts-with "search"))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (inbox unread)
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (address) (inbox unread)
|
||||
thread:XXX 2000-01-01 [1/1] Notmuch Test Suite; search by to (name) (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "wildcard search for 'is'"
|
||||
notmuch search not id:${notag_mid} > EXPECTED
|
||||
notmuch search --query=sexp '(is *)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "negated wildcard search for 'is'"
|
||||
notmuch search id:${notag_mid} > EXPECTED
|
||||
notmuch search --query=sexp '(not (is *))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "wildcard search for 'property'"
|
||||
notmuch search property:foo=bar > EXPECTED
|
||||
notmuch search --query=sexp '(property *)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "wildcard search for 'tag'"
|
||||
notmuch search not id:${notag_mid} > EXPECTED
|
||||
notmuch search --query=sexp '(tag *)' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "negated wildcard search for 'tag'"
|
||||
notmuch search id:${notag_mid} > EXPECTED
|
||||
notmuch search --query=sexp '(not (tag *))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
add_message '[subject]="message with tag \"*\""'
|
||||
notmuch tag '+*' id:${gen_msg_id}
|
||||
|
||||
test_begin_subtest "search for 'tag' \"*\""
|
||||
output=$(notmuch search --query=sexp --output=messages '(tag "*")')
|
||||
test_expect_equal "$output" "id:$gen_msg_id"
|
||||
|
||||
test_begin_subtest "search for missing / empty to"
|
||||
add_message [to]="undisclosed-recipients:"
|
||||
notmuch search --query=sexp '(not (to *))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; search for missing / empty to (inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Unbalanced parens"
|
||||
# A code 1 indicates the error was handled (a crash will return e.g. 139).
|
||||
test_expect_code 1 "notmuch search --query=sexp '('"
|
||||
|
||||
test_begin_subtest "Unbalanced parens, error message"
|
||||
notmuch search --query=sexp '(' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
invalid s-expression: '('
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "unknown prefix"
|
||||
notmuch search --query=sexp '(foo)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
unknown prefix 'foo'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "list as prefix"
|
||||
notmuch search --query=sexp '((foo))' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
unexpected list in field/operation position
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "illegal nesting"
|
||||
notmuch search --query=sexp '(subject (subject foo))' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
nested field: 'subject' inside 'subject'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, no argument"
|
||||
notmuch search --query=sexp '(starts-with)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'starts-with' expects single atom as argument
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, list argument"
|
||||
notmuch search --query=sexp '(starts-with (stuff))' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'starts-with' expects single atom as argument
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, too many arguments"
|
||||
notmuch search --query=sexp '(starts-with a b c)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'starts-with' expects single atom as argument
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "starts-with, illegal field"
|
||||
notmuch search --query=sexp '(body (starts-with foo))' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'body' does not support wildcard queries
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "wildcard, illegal field"
|
||||
notmuch search --query=sexp '(body *)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'body' does not support wildcard queries
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search, exclude \"deleted\" messages from search"
|
||||
notmuch config set search.exclude_tags deleted
|
||||
generate_message '[subject]="Not deleted"'
|
||||
not_deleted_id=$gen_msg_id
|
||||
generate_message '[subject]="Deleted"'
|
||||
notmuch new > /dev/null
|
||||
notmuch tag +deleted id:$gen_msg_id
|
||||
deleted_id=$gen_msg_id
|
||||
output=$(notmuch search --query=sexp '(subject deleted)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)"
|
||||
|
||||
test_begin_subtest "Search, exclude \"deleted\" messages from message search --exclude=false"
|
||||
output=$(notmuch search --query=sexp --exclude=false --output=messages '(subject deleted)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "id:$not_deleted_id
|
||||
id:$deleted_id"
|
||||
|
||||
test_begin_subtest "Search, exclude \"deleted\" messages from search, overridden"
|
||||
notmuch search --query=sexp '(and (subject deleted) (tag deleted))' | notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Deleted (deleted inbox unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Search, exclude \"deleted\" messages from threads"
|
||||
add_message '[subject]="Not deleted reply"' '[in-reply-to]="<$gen_msg_id>"'
|
||||
output=$(notmuch search --query=sexp '(subject deleted)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)
|
||||
thread:XXX 2001-01-05 [1/2] Notmuch Test Suite; Not deleted reply (deleted inbox unread)"
|
||||
|
||||
test_begin_subtest "Search, don't exclude \"deleted\" messages when --exclude=flag specified"
|
||||
output=$(notmuch search --query=sexp --exclude=flag '(subject deleted)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)
|
||||
thread:XXX 2001-01-05 [1/2] Notmuch Test Suite; Deleted (deleted inbox unread)"
|
||||
|
||||
test_begin_subtest "Search, don't exclude \"deleted\" messages from search if not configured"
|
||||
notmuch config set search.exclude_tags
|
||||
output=$(notmuch search --query=sexp '(subject deleted)' | notmuch_search_sanitize)
|
||||
test_expect_equal "$output" "thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Not deleted (inbox unread)
|
||||
thread:XXX 2001-01-05 [2/2] Notmuch Test Suite; Deleted (deleted inbox unread)"
|
||||
|
||||
test_begin_subtest "regex at top level"
|
||||
notmuch search --query=sexp '(rx foo)' >& OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
illegal 'rx' outside field
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "regex in illegal field"
|
||||
notmuch search --query=sexp '(body (regex foo))' >& OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'regex' not supported in field 'body'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
notmuch search --output=messages from:cworth > cworth.msg-ids
|
||||
|
||||
test_begin_subtest "regexp 'from' search"
|
||||
notmuch search --output=messages --query=sexp '(from (rx cworth))' > OUTPUT
|
||||
test_expect_equal_file cworth.msg-ids OUTPUT
|
||||
|
||||
test_begin_subtest "regexp search for 'from' 2"
|
||||
notmuch search from:/cworth@cworth.org/ and subject:patch | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(and (from (rx cworth@cworth.org)) (subject patch))' \
|
||||
| notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "regexp 'folder' search"
|
||||
notmuch search 'folder:/^bar$/' | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(folder (rx ^bar$))' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "regexp 'id' search"
|
||||
notmuch search --output=messages --query=sexp '(id (rx yoom))' > OUTPUT
|
||||
test_expect_equal_file cworth.msg-ids OUTPUT
|
||||
|
||||
test_begin_subtest "unanchored 'is' search"
|
||||
notmuch search tag:signed or tag:inbox > EXPECTED
|
||||
notmuch search --query=sexp '(is (rx i))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "anchored 'is' search"
|
||||
notmuch search tag:signed > EXPECTED
|
||||
notmuch search --query=sexp '(is (rx ^si))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "combine regexp mid and subject"
|
||||
notmuch search subject:/-C/ and mid:/y..m/ | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(and (subject (rx -C)) (mid (rx y..m)))' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "regexp 'path' search"
|
||||
notmuch search 'path:/^bar$/' | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(path (rx ^bar$))' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "regexp 'property' search"
|
||||
notmuch search property:foo=bar > EXPECTED
|
||||
notmuch search --query=sexp '(property (rx foo=.*))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "anchored 'tag' search"
|
||||
notmuch search tag:signed > EXPECTED
|
||||
notmuch search --query=sexp '(tag (rx ^si))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "regexp 'thread' search"
|
||||
notmuch search --output=threads '*' | grep '7$' > EXPECTED
|
||||
notmuch search --output=threads --query=sexp '(thread (rx 7$))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Basic query that matches no messages"
|
||||
count=$(notmuch count from:keithp and to:keithp)
|
||||
test_expect_equal 0 "$count"
|
||||
|
||||
test_begin_subtest "Same query against threads"
|
||||
notmuch search --query=sexp '(and (thread (of (from keithp))) (thread (matching (to keithp))))' \
|
||||
| notmuch_search_sanitize > OUTPUT
|
||||
cat<<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [7/7] Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Mix thread and non-threads query"
|
||||
notmuch search --query=sexp '(and (thread (matching keithp)) (to keithp))' | notmuch_search_sanitize > OUTPUT
|
||||
cat<<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [1/7] Lars Kellogg-Stedman| Mikhail Gusarov, Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Compound subquery"
|
||||
notmuch search --query=sexp '(thread (of (from keithp) (subject Maildir)))' | notmuch_search_sanitize > OUTPUT
|
||||
cat<<EOF > EXPECTED
|
||||
thread:XXX 2009-11-18 [7/7] Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth; [notmuch] Working with Maildir storage? (inbox signed unread)
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "empty subquery"
|
||||
notmuch search --query=sexp '(thread (of))' 1>OUTPUT 2>&1
|
||||
notmuch search '*' > EXPECTED
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "illegal expansion"
|
||||
notmuch search --query=sexp '(id (of ego))' 1>OUTPUT 2>&1
|
||||
cat<<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'of' unsupported inside 'id'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "(folder (of subquery))"
|
||||
notmuch search --query=sexp --output=messages '(folder (of (id yun3a4cegoa.fsf@aiko.keithp.com)))' > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
id:yun1vjwegii.fsf@aiko.keithp.com
|
||||
id:yun3a4cegoa.fsf@aiko.keithp.com
|
||||
id:1258509400-32511-1-git-send-email-stewart@flamingspork.com
|
||||
id:1258506353-20352-1-git-send-email-stewart@flamingspork.com
|
||||
id:20091118010116.GC25380@dottiness.seas.harvard.edu
|
||||
id:20091118005829.GB25380@dottiness.seas.harvard.edu
|
||||
id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "infix query"
|
||||
notmuch search to:searchbyto | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(infix "to:searchbyto")' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "bad infix query 1"
|
||||
notmuch search --query=sexp '(infix "from:/unbalanced")' 2>&1| notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
Syntax error in infix query: from:/unbalanced
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "bad infix query 2"
|
||||
notmuch search --query=sexp '(infix "thread:{unbalanced")' 2>&1| notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
Syntax error in infix query: thread:{unbalanced
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "bad infix query 3: bad nesting"
|
||||
notmuch search --query=sexp '(subject (infix "tag:inbox"))' 2>&1| notmuch_search_sanitize > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
'infix' not supported inside 'subject'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "infix query that matches no messages"
|
||||
notmuch search --query=sexp '(and (infix "from:keithp") (infix "to:keithp"))' > OUTPUT
|
||||
test_expect_equal_file /dev/null OUTPUT
|
||||
|
||||
test_begin_subtest "compound infix query"
|
||||
notmuch search date:2009-11-18..2009-11-18 and tag:unread > EXPECTED
|
||||
notmuch search --query=sexp '(infix "date:2009-11-18..2009-11-18 and tag:unread")' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "compound infix query 2"
|
||||
notmuch search date:2009-11-18..2009-11-18 and tag:unread > EXPECTED
|
||||
notmuch search --query=sexp '(and (infix "date:2009-11-18..2009-11-18") (infix "tag:unread"))' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "user header (unknown header)"
|
||||
notmuch search --query=sexp '(FooBar)' >& OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
unknown prefix 'FooBar'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "adding user header"
|
||||
test_expect_code 0 "notmuch config set index.header.List \"List-Id\""
|
||||
|
||||
test_begin_subtest "reindexing"
|
||||
test_expect_code 0 'notmuch reindex "*"'
|
||||
|
||||
test_begin_subtest "wildcard search for user header"
|
||||
grep -Ril List-Id ${MAIL_DIR} | sort | notmuch_dir_sanitize > EXPECTED
|
||||
notmuch search --output=files --query=sexp '(List *)' | sort | notmuch_dir_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "wildcard search for user header 2"
|
||||
grep -Ril List-Id ${MAIL_DIR} | sort | notmuch_dir_sanitize > EXPECTED
|
||||
notmuch search --output=files --query=sexp '(List (starts-with not))' | sort | notmuch_dir_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "search for user header"
|
||||
notmuch search List:notmuch | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(List notmuch)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "search for user header (list token)"
|
||||
notmuch search List:notmuch | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(List notmuch.notmuchmail.org)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "search for user header (quoted string)"
|
||||
notmuch search 'List:"notmuch notmuchmail org"' | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(List "notmuch notmuchmail org")' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "search for user header (atoms)"
|
||||
notmuch search 'List:"notmuch notmuchmail org"' | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(List notmuch notmuchmail org)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "check saved query name"
|
||||
test_expect_code 1 "notmuch config set squery.test '(subject utf8-sübjéct)'"
|
||||
|
||||
test_begin_subtest "roundtrip saved query (database)"
|
||||
notmuch config set --database squery.Test '(subject utf8-sübjéct)'
|
||||
output=$(notmuch config get squery.Test)
|
||||
test_expect_equal "$output" '(subject utf8-sübjéct)'
|
||||
|
||||
test_begin_subtest "roundtrip saved query"
|
||||
notmuch config set squery.Test '(subject override subject)'
|
||||
output=$(notmuch config get squery.Test)
|
||||
test_expect_equal "$output" '(subject override subject)'
|
||||
|
||||
test_begin_subtest "unknown saved query"
|
||||
notmuch search --query=sexp '(Unknown foo bar)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
unknown prefix 'Unknown'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "syntax error in saved query"
|
||||
notmuch config set squery.Bad '(Bad'
|
||||
notmuch search --query=sexp '(Bad foo bar)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
invalid saved s-expression query: '(Bad'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search by 'tag' and 'subject'"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.TagSubject '(and (tag inbox) (subject maildir))'
|
||||
notmuch search --query=sexp '(TagSubject)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: illegal nesting"
|
||||
notmuch config set squery.TagSubject '(and (tag inbox) (subject maildir))'
|
||||
notmuch search --query=sexp '(subject (TagSubject))' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
nested field: 'tag' inside 'subject'
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: list as prefix"
|
||||
notmuch config set squery.Bad2 '((and) (tag inbox) (subject maildir))'
|
||||
notmuch search --query=sexp '(Bad2)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
unexpected list in field/operation position
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: bad parameter syntax"
|
||||
notmuch config set squery.Bad3 '(macro a b)'
|
||||
notmuch search --query=sexp '(Bad3)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
missing (possibly empty) list of arguments to macro
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: bad parameter syntax 2"
|
||||
notmuch config set squery.Bad4 '(macro ((a b)) a)'
|
||||
notmuch search --query=sexp '(Bad4 1)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
macro parameters must be unquoted atoms
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: bad parameter syntax 3"
|
||||
notmuch config set squery.Bad5 '(macro (a b) a)'
|
||||
notmuch search --query=sexp '(Bad5 1)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
too few arguments to macro
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: bad parameter syntax 4"
|
||||
notmuch search --query=sexp '(Bad5 1 2 3)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
too many arguments to macro
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Saved Search: macro without body"
|
||||
notmuch config set squery.Bad3 '(macro (a b))'
|
||||
notmuch search --query=sexp '(Bad3)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
missing body of macro
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "macro in query"
|
||||
notmuch search --query=sexp '(macro (a) (and ,b (subject maildir)))' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
macro definition not permitted here
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "zero argument macro"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.TagSubject0 '(macro () (and (tag inbox) (subject maildir)))'
|
||||
notmuch search --query=sexp '(TagSubject0)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "undefined argument"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.Bad6 '(macro (a) (and ,b (subject maildir)))'
|
||||
notmuch search --query=sexp '(Bad6 foo)' >OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
undefined parameter b
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Single argument macro"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.TagSubject1 '(macro (tagname) (and (tag ,tagname) (subject maildir)))'
|
||||
notmuch search --query=sexp '(TagSubject1 inbox)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "Single argument macro, list argument"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.ThingSubject '(macro (thing) (and ,thing (subject maildir)))'
|
||||
notmuch search --query=sexp '(ThingSubject (tag inbox))' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "two argument macro"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.TagSubject2 '(macro (tagname subj) (and (tag ,tagname) (subject ,subj)))'
|
||||
notmuch search --query=sexp '(TagSubject2 inbox maildir)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "nested macros (shadowing)"
|
||||
notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
|
||||
notmuch config set squery.Inner '(macro (x) (subject ,x))'
|
||||
notmuch config set squery.Outer '(macro (x y) (and (tag ,x) (Inner ,y)))'
|
||||
notmuch search --query=sexp '(Outer inbox maildir)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "nested macros (no dynamic scope)"
|
||||
notmuch config set squery.Inner2 '(macro (x) (subject ,y))'
|
||||
notmuch config set squery.Outer2 '(macro (x y) (and (tag ,x) (Inner2 ,y)))'
|
||||
notmuch search --query=sexp '(Outer2 inbox maildir)' > OUTPUT 2>&1
|
||||
cat <<EOF > EXPECTED
|
||||
notmuch search: Syntax error in query
|
||||
undefined parameter y
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "combine macro and user defined header"
|
||||
notmuch config set squery.About '(macro (name) (or (subject ,name) (List ,name)))'
|
||||
notmuch search subject:notmuch or List:notmuch | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(About notmuch)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
|
||||
test_begin_subtest "combine macro and user defined header"
|
||||
notmuch config set squery.About '(macro (name) (or (subject ,name) (List ,name)))'
|
||||
notmuch search subject:notmuch or List:notmuch | notmuch_search_sanitize > EXPECTED
|
||||
notmuch search --query=sexp '(About notmuch)' | notmuch_search_sanitize > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
|
||||
test_done
|
|
@ -325,4 +325,11 @@ cat <<EOF >EXPECTED
|
|||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
if [[ NOTMUCH_HAVE_SFSEXP = 1 ]]; then
|
||||
test_begin_subtest "sexpr query: all messages"
|
||||
notmuch address '*' > EXPECTED
|
||||
notmuch address --query=sexp '()' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
|
@ -2,6 +2,21 @@
|
|||
test_description='"notmuch tag"'
|
||||
. $(dirname "$0")/test-lib.sh || exit 1
|
||||
|
||||
test_query_syntax () {
|
||||
# use a tag with a space to stress the query string munging code.
|
||||
local new_tag="${RANDOM} ${RANDOM}"
|
||||
test_begin_subtest "sexpr query: $1"
|
||||
backup_database
|
||||
notmuch tag --query=sexp "+${new_tag}" -- "$1"
|
||||
notmuch dump > OUTPUT
|
||||
restore_database
|
||||
backup_database
|
||||
notmuch tag "+${new_tag}" -- "$2"
|
||||
notmuch dump > EXPECTED
|
||||
restore_database
|
||||
test_expect_equal_file_nonempty EXPECTED OUTPUT
|
||||
}
|
||||
|
||||
add_message '[subject]=One'
|
||||
add_message '[subject]=Two'
|
||||
|
||||
|
@ -310,4 +325,32 @@ output=$(notmuch tag +something '*' 2>&1 | sed 's/: .*$//' )
|
|||
chmod u+w ${MAIL_DIR}/.notmuch/xapian/*.*
|
||||
test_expect_equal "$output" "A Xapian exception occurred opening database"
|
||||
|
||||
add_email_corpus
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
|
||||
|
||||
test_query_syntax '(and "wonderful" "wizard")' 'wonderful and wizard'
|
||||
test_query_syntax '(or "php" "wizard")' 'php or wizard'
|
||||
test_query_syntax 'wizard' 'wizard'
|
||||
test_query_syntax 'Wizard' 'Wizard'
|
||||
test_query_syntax '(attachment notmuch-help.patch)' 'attachment:notmuch-help.patch'
|
||||
test_query_syntax '(mimetype text/html)' 'mimetype:text/html'
|
||||
|
||||
test_begin_subtest "--batch --query=sexp"
|
||||
notmuch dump --format=batch-tag > backup.tags
|
||||
notmuch tag --batch --query=sexp <<EOF
|
||||
+all -- (or One Two)
|
||||
+none -- (and One Two)
|
||||
EOF
|
||||
notmuch dump > OUTPUT
|
||||
cat <<EOF > EXPECTED
|
||||
#notmuch-dump batch-tag:3 config,properties,tags
|
||||
+all +inbox +tag5 +unread -- id:msg-001@notmuch-test-suite
|
||||
+all +inbox +tag4 +tag5 +unread -- id:msg-002@notmuch-test-suite
|
||||
EOF
|
||||
notmuch restore --format=batch-tag < backup.tags
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
|
@ -2,15 +2,14 @@
|
|||
test_description="\"notmuch reply\" in several variations"
|
||||
. $(dirname "$0")/test-lib.sh || exit 1
|
||||
|
||||
test_begin_subtest "Basic reply"
|
||||
add_message '[from]="Sender <sender@example.com>"' \
|
||||
[to]=test_suite@notmuchmail.org \
|
||||
[subject]=notmuch-reply-test \
|
||||
'[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
|
||||
'[body]="basic reply test"'
|
||||
|
||||
output=$(notmuch reply id:${gen_msg_id} 2>&1 && echo OK)
|
||||
test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
|
||||
cat <<EOF > basic.expected
|
||||
From: Notmuch Test Suite <test_suite@notmuchmail.org>
|
||||
Subject: Re: notmuch-reply-test
|
||||
To: Sender <sender@example.com>
|
||||
In-Reply-To: <${gen_msg_id}>
|
||||
|
@ -18,7 +17,19 @@ References: <${gen_msg_id}>
|
|||
|
||||
On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
|
||||
> basic reply test
|
||||
OK"
|
||||
OK
|
||||
EOF
|
||||
|
||||
test_begin_subtest "Basic reply"
|
||||
notmuch reply id:${gen_msg_id} >OUTPUT 2>&1 && echo OK >> OUTPUT
|
||||
test_expect_equal_file basic.expected OUTPUT
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
|
||||
|
||||
test_begin_subtest "Basic reply (query=sexp)"
|
||||
notmuch reply --query=sexp "(id ${gen_msg_id})" >OUTPUT 2>&1 && echo OK >> OUTPUT
|
||||
test_expect_equal_file basic.expected OUTPUT
|
||||
fi
|
||||
|
||||
test_begin_subtest "Multiple recipients"
|
||||
add_message '[from]="Sender <sender@example.com>"' \
|
||||
|
@ -245,6 +256,26 @@ On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
|
|||
> From guessing
|
||||
OK"
|
||||
|
||||
test_begin_subtest "From guessing: multiple Delivered-To"
|
||||
add_message '[from]="Sender <sender@example.com>"' \
|
||||
'[to]="Recipient <recipient@example.com>"' \
|
||||
'[subject]="From guessing"' \
|
||||
'[date]="Tue, 05 Jan 2010 15:43:56 -0000"' \
|
||||
'[body]="From guessing"' \
|
||||
'[header]="Delivered-To: test_suite_other@notmuchmail.org
|
||||
Delivered-To: test_suite@notmuchmail.org"'
|
||||
|
||||
output=$(notmuch reply id:${gen_msg_id} 2>&1 && echo OK)
|
||||
test_expect_equal "$output" "From: Notmuch Test Suite <test_suite@notmuchmail.org>
|
||||
Subject: Re: From guessing
|
||||
To: Sender <sender@example.com>, Recipient <recipient@example.com>
|
||||
In-Reply-To: <${gen_msg_id}>
|
||||
References: <${gen_msg_id}>
|
||||
|
||||
On Tue, 05 Jan 2010 15:43:56 -0000, Sender <sender@example.com> wrote:
|
||||
> From guessing
|
||||
OK"
|
||||
|
||||
test_begin_subtest "Reply with RFC 2047-encoded headers"
|
||||
add_message '[subject]="=?iso-8859-1?q?=e0=df=e7?="' \
|
||||
'[from]="=?utf-8?q?=e2=98=83?= <snowman@example.com>"' \
|
||||
|
@ -281,7 +312,7 @@ test_expect_equal_json "$output" '
|
|||
"crypto": {},
|
||||
"date_relative": "2010-01-05",
|
||||
"excluded": false,
|
||||
"filename": ["'${MAIL_DIR}'/msg-014"],
|
||||
"filename": ["'${MAIL_DIR}'/msg-015"],
|
||||
"headers": {
|
||||
"Date": "Tue, 05 Jan 2010 15:43:56 +0000",
|
||||
"From": "\u2603 <snowman@example.com>",
|
||||
|
|
|
@ -117,6 +117,19 @@ test_begin_subtest "dump -- from:cworth"
|
|||
notmuch dump -- from:cworth > dump-dash-cworth.actual
|
||||
test_expect_equal_file dump-cworth.expected dump-dash-cworth.actual
|
||||
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
|
||||
|
||||
test_begin_subtest "dump --query=sexp -- '(from cworth)'"
|
||||
notmuch dump --query=sexp -- '(from cworth)' > dump-dash-cworth.actual2
|
||||
test_expect_equal_file_nonempty dump-cworth.expected dump-dash-cworth.actual2
|
||||
|
||||
test_begin_subtest "dump --query=sexp --output=outfile '(from cworth)'"
|
||||
notmuch dump --output=dump-outfile-cworth.actual2 --query=sexp '(from cworth)'
|
||||
test_expect_equal_file dump-cworth.expected dump-outfile-cworth.actual2
|
||||
|
||||
fi
|
||||
|
||||
test_begin_subtest "dump --output=outfile from:cworth"
|
||||
notmuch dump --output=dump-outfile-cworth.actual from:cworth
|
||||
test_expect_equal_file dump-cworth.expected dump-outfile-cworth.actual
|
||||
|
|
|
@ -41,6 +41,20 @@ test_emacs '(notmuch-search "tag:inbox")
|
|||
(test-output)'
|
||||
test_expect_equal_file $EXPECTED/notmuch-search-tag-inbox OUTPUT
|
||||
|
||||
test_begin_subtest "Functions in search-result-format"
|
||||
test_emacs '(let
|
||||
((notmuch-search-result-format
|
||||
(quote ((notmuch-test-result-flags . "%s ")
|
||||
("date" . "%12s ")
|
||||
("count" . "%9s ")
|
||||
("authors" . "%-30s ")
|
||||
("subject" . "%s ")
|
||||
("tags" . "(%s)")))))
|
||||
(notmuch-search "tag:inbox")
|
||||
(notmuch-test-wait)
|
||||
(test-output))'
|
||||
test_expect_equal_file $EXPECTED/search-result-format-function OUTPUT
|
||||
|
||||
test_begin_subtest "Incremental parsing of search results"
|
||||
test_emacs "(cl-letf* (((symbol-function 'orig)
|
||||
(symbol-function 'notmuch-search-process-filter))
|
||||
|
@ -1154,4 +1168,10 @@ This text added by the hook.
|
|||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "notmuch-search with nonexistent CWD"
|
||||
test_emacs '(test-log-error
|
||||
(let ((default-directory "/nonexistent"))
|
||||
(notmuch-search "*")))'
|
||||
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -226,6 +226,7 @@ output=$(notmuch dump | LC_ALL=C sort)
|
|||
expected='#= simple-encrypted@crypto.notmuchmail.org index.decryption=failure
|
||||
#notmuch-dump batch-tag:3 config,properties,tags
|
||||
+encrypted +inbox +unread -- id:basic-encrypted@crypto.notmuchmail.org
|
||||
+encrypted +inbox +unread -- id:encrypted-rfc822-attachment@crypto.notmuchmail.org
|
||||
+encrypted +inbox +unread -- id:encrypted-signed@crypto.notmuchmail.org
|
||||
+encrypted +inbox +unread -- id:simple-encrypted@crypto.notmuchmail.org'
|
||||
test_expect_equal \
|
||||
|
|
|
@ -7,8 +7,14 @@ if [ $NOTMUCH_HAVE_PYTHON3_CFFI -eq 0 -o $NOTMUCH_HAVE_PYTHON3_PYTEST -eq 0 ]; t
|
|||
fi
|
||||
|
||||
|
||||
test_begin_subtest "python cffi tests"
|
||||
test_begin_subtest "python cffi tests (NOTMUCH_CONFIG set)"
|
||||
pytest_dir=$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage
|
||||
printf "[pytest]\nminversion = 3.0\naddopts = -ra\n" > $pytest_dir/pytest.ini
|
||||
test_expect_success "(cd $pytest_dir && ${NOTMUCH_PYTHON} -m pytest --verbose --log-file=$TMP_DIRECTORY/test.output)"
|
||||
|
||||
test_begin_subtest "python cffi tests (NOTMUCH_CONFIG unset)"
|
||||
pytest_dir=$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage
|
||||
printf "[pytest]\nminversion = 3.0\naddopts = -ra\n" > $pytest_dir/pytest.ini
|
||||
unset NOTMUCH_CONFIG
|
||||
test_expect_success "(cd $pytest_dir && ${NOTMUCH_PYTHON} -m pytest --verbose --log-file=$TMP_DIRECTORY/test.output)"
|
||||
test_done
|
||||
|
|
|
@ -68,4 +68,12 @@ test_emacs '(notmuch-hello)
|
|||
notmuch tag -$tag '*'
|
||||
test_expect_equal_file $EXPECTED/notmuch-hello-long-names OUTPUT
|
||||
|
||||
test_begin_subtest "notmuch-hello with nonexistent CWD"
|
||||
test_emacs '
|
||||
(notmuch-hello)
|
||||
(test-log-error
|
||||
(let ((default-directory "/nonexistent"))
|
||||
(notmuch-hello-update)))'
|
||||
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -219,6 +219,12 @@ test_emacs '(notmuch-show "id:basic-encrypted@crypto.notmuchmail.org")
|
|||
(test-visible-output)'
|
||||
test_expect_equal_file $EXPECTED/notmuch-show-decrypted-message OUTPUT
|
||||
|
||||
test_begin_subtest "show encrypted rfc822 message"
|
||||
test_subtest_known_broken
|
||||
test_emacs '(notmuch-show "id:encrypted-rfc822-attachment@crypto.notmuchmail.org")
|
||||
(test-visible-output)'
|
||||
test_expect_code 1 'fgrep "!!!" OUTPUT'
|
||||
|
||||
test_begin_subtest "show undecryptable message"
|
||||
test_emacs '(notmuch-show "id:simple-encrypted@crypto.notmuchmail.org")
|
||||
(test-visible-output)'
|
||||
|
@ -230,4 +236,11 @@ test_emacs '(let ((notmuch-crypto-process-mime nil))
|
|||
(test-visible-output))'
|
||||
test_expect_equal_file $EXPECTED/notmuch-show-decrypted-message-no-crypto OUTPUT
|
||||
|
||||
test_begin_subtest "notmuch-show with nonexistent CWD"
|
||||
tid=$(notmuch search --limit=1 --output=threads '*' | sed s/thread://)
|
||||
test_emacs "(test-log-error
|
||||
(let ((default-directory \"/nonexistent\"))
|
||||
(notmuch-show \"$tid\")))"
|
||||
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -179,4 +179,25 @@ output=$(test_emacs '(notmuch-tree "tag:inbox")
|
|||
(notmuch-show-stash-message-id)')
|
||||
test_expect_equal "$output" "\"Stashed: id:1258493565-13508-1-git-send-email-keithp@keithp.com\""
|
||||
|
||||
test_begin_subtest "Functions in tree-result-format"
|
||||
test_emacs '
|
||||
(let
|
||||
((notmuch-tree-result-format
|
||||
(quote (("date" . "%12s ")
|
||||
("authors" . "%-20s")
|
||||
((("tree" . "%s")
|
||||
("subject" . "%s")) . " %-54s ")
|
||||
(notmuch-test-result-flags . "(%s)")))))
|
||||
(notmuch-tree "tag:inbox")
|
||||
(notmuch-test-wait)
|
||||
(test-output))
|
||||
'
|
||||
test_expect_equal_file $EXPECTED/result-format-function OUTPUT
|
||||
|
||||
test_begin_subtest "notmuch-tree with nonexistent CWD"
|
||||
test_emacs '(test-log-error
|
||||
(let ((default-directory "/nonexistent"))
|
||||
(notmuch-tree "*")))'
|
||||
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -6,6 +6,8 @@ test_description="emacs unthreaded interface"
|
|||
|
||||
test_require_emacs
|
||||
|
||||
EXPECTED=$NOTMUCH_SRCDIR/test/emacs-unthreaded.expected-output
|
||||
|
||||
generate_message "[id]=large-thread-1" '[subject]="large thread"'
|
||||
printf " 2001-01-05 Notmuch Test Suite large thread%43s(inbox unread)\n" >> EXPECTED.unthreaded
|
||||
|
||||
|
@ -34,4 +36,25 @@ output=$(test_emacs '(let ((max-lisp-eval-depth 10))
|
|||
"SUCCESS")' )
|
||||
test_expect_equal "$output" '"SUCCESS"'
|
||||
|
||||
add_email_corpus
|
||||
test_begin_subtest "Functions in unthreaded-result-format"
|
||||
test_emacs '
|
||||
(let
|
||||
((notmuch-unthreaded-result-format
|
||||
(quote (("date" . "%12s ")
|
||||
("authors" . "%-20s")
|
||||
("subject" . "%-54s")
|
||||
(notmuch-test-result-flags . "(%s)")))))
|
||||
(notmuch-unthreaded "tag:inbox")
|
||||
(notmuch-test-wait)
|
||||
(test-output))
|
||||
'
|
||||
test_expect_equal_file $EXPECTED/result-format-function OUTPUT
|
||||
|
||||
test_begin_subtest "notmuch-unthreaded with nonexistent CWD"
|
||||
test_emacs '(test-log-error
|
||||
(let ((default-directory "/nonexistent"))
|
||||
(notmuch-unthreaded "*")))'
|
||||
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
|
||||
|
||||
test_done
|
||||
|
|
|
@ -3,6 +3,13 @@ test_description='"notmuch show"'
|
|||
|
||||
. $(dirname "$0")/test-lib.sh || exit 1
|
||||
|
||||
test_query_syntax () {
|
||||
test_begin_subtest "sexpr query: $1"
|
||||
sexp=$(notmuch show --format=json --query=sexp "$1")
|
||||
infix=$(notmuch show --format=json "$2")
|
||||
test_expect_equal_json "$sexp" "$infix"
|
||||
}
|
||||
|
||||
add_email_corpus
|
||||
|
||||
test_begin_subtest "exit code for show invalid query"
|
||||
|
@ -27,4 +34,15 @@ notmuch show --entire-thread=true --sort=newest-first $QUERY > EXPECTED
|
|||
notmuch show --entire-thread=true --sort=oldest-first $QUERY > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
|
||||
|
||||
test_query_syntax '(and "wonderful" "wizard")' 'wonderful and wizard'
|
||||
test_query_syntax '(or "php" "wizard")' 'php or wizard'
|
||||
test_query_syntax 'wizard' 'wizard'
|
||||
test_query_syntax 'Wizard' 'Wizard'
|
||||
test_query_syntax '(attachment notmuch-help.patch)' 'attachment:notmuch-help.patch'
|
||||
|
||||
fi
|
||||
|
||||
test_done
|
||||
|
|
|
@ -461,7 +461,7 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL%
|
|||
}
|
||||
EOF
|
||||
rm -f ${ovconfig}
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
test.key1 = overridden-home
|
||||
|
@ -488,7 +488,7 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL%
|
|||
}
|
||||
EOF
|
||||
rm -f ${ovconfig}
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
test.key1 = overridden-xdg
|
||||
|
@ -515,7 +515,7 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% work
|
|||
}
|
||||
EOF
|
||||
rm -f ${ovconfig}
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
test.key1 = overridden-xdg-profile
|
||||
|
@ -541,7 +541,7 @@ cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% work
|
|||
}
|
||||
EOF
|
||||
#rm -f ${ovconfig}
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
test.key1 = overridden-profile
|
||||
|
@ -559,7 +559,7 @@ cat c_head - c_tail <<'EOF' | test_C %NULL% '' %NULL%
|
|||
printf("NOT RUN");
|
||||
}
|
||||
EOF
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
== stderr ==
|
||||
|
@ -604,7 +604,7 @@ cat c_head - c_tail <<'EOF' | test_C %NULL% '' %NULL%
|
|||
printf("test.key2 = %s\n", val);
|
||||
}
|
||||
EOF
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
unset NOTMUCH_DATABASE
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
|
@ -783,7 +783,7 @@ cat c_head2 - c_tail <<'EOF' | test_C ${MAIL_DIR} %NULL% %NULL%
|
|||
}
|
||||
EOF
|
||||
rm -f ${ovconfig}
|
||||
NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<'EOF' >EXPECTED
|
||||
== stdout ==
|
||||
test.key1 = overridden-home
|
||||
|
@ -849,4 +849,128 @@ zzzafter afterval
|
|||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
cat <<EOF > c_head3
|
||||
#include <notmuch-test.h>
|
||||
int main (int argc, char **argv) {
|
||||
notmuch_status_t stat;
|
||||
notmuch_database_t *db = NULL;
|
||||
EOF
|
||||
|
||||
cat <<EOF > c_tail3
|
||||
printf("db == NULL: %d\n", db == NULL);
|
||||
}
|
||||
EOF
|
||||
|
||||
test_begin_subtest "open: database set to null on missing config"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
|
||||
notmuch_status_t st = notmuch_database_open_with_config(argv[1],
|
||||
NOTMUCH_DATABASE_MODE_READ_ONLY,
|
||||
"/nonexistent", NULL, &db, NULL);
|
||||
EOF
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 1
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "open: database set to null on missing config (env)"
|
||||
old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG="/nonexistent"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
|
||||
notmuch_status_t st = notmuch_database_open_with_config(argv[1],
|
||||
NOTMUCH_DATABASE_MODE_READ_ONLY,
|
||||
NULL, NULL, &db, NULL);
|
||||
EOF
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 1
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "create: database set to null on missing config"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} "/nonexistent"
|
||||
notmuch_status_t st = notmuch_database_create_with_config(argv[1],argv[2], NULL, &db, NULL);
|
||||
EOF
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 1
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "create: database set to null on missing config (env)"
|
||||
old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG="/nonexistent"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
|
||||
notmuch_status_t st = notmuch_database_create_with_config(argv[1],
|
||||
NULL, NULL, &db, NULL);
|
||||
EOF
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 1
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "load_config: database set non-null on missing config"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR} "/nonexistent"
|
||||
notmuch_status_t st = notmuch_database_load_config(argv[1],argv[2], NULL, &db, NULL);
|
||||
EOF
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 0
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "load_config: database non-null on missing config (env)"
|
||||
old_NOTMUCH_CONFIG=${NOTMUCH_CONFIG}
|
||||
export NOTMUCH_CONFIG="/nonexistent"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
|
||||
notmuch_status_t st = notmuch_database_load_config(argv[1], NULL, NULL, &db, NULL);
|
||||
EOF
|
||||
export NOTMUCH_CONFIG=${old_NOTMUCH_CONFIG}
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 0
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "load_config: database set to NULL on fatal error"
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C
|
||||
notmuch_status_t st = notmuch_database_load_config("relative", NULL, NULL, &db, NULL);
|
||||
EOF
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
db == NULL: 1
|
||||
== stderr ==
|
||||
EOF
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
test_begin_subtest "open: database parameter overrides implicit config"
|
||||
notmuch config set database.path ${MAIL_DIR}/nonexistent
|
||||
cat c_head3 - c_tail3 <<'EOF' | test_C ${MAIL_DIR}
|
||||
const char *path = NULL;
|
||||
notmuch_status_t st = notmuch_database_open_with_config(argv[1],
|
||||
NOTMUCH_DATABASE_MODE_READ_ONLY,
|
||||
NULL, NULL, &db, NULL);
|
||||
printf ("status: %d\n", st);
|
||||
path = notmuch_database_get_path (db);
|
||||
printf ("path: %s\n", path ? path : "(null)");
|
||||
EOF
|
||||
cat <<EOF> EXPECTED
|
||||
== stdout ==
|
||||
status: 0
|
||||
path: MAIL_DIR
|
||||
db == NULL: 0
|
||||
== stderr ==
|
||||
EOF
|
||||
notmuch_dir_sanitize < OUTPUT > OUTPUT.clean
|
||||
test_expect_equal_file EXPECTED OUTPUT.clean
|
||||
|
||||
test_done
|
||||
|
|
|
@ -4,6 +4,21 @@ test_description='reindexing messages'
|
|||
|
||||
add_email_corpus
|
||||
|
||||
|
||||
if [ $NOTMUCH_HAVE_SFSEXP -eq 1 ]; then
|
||||
|
||||
count=$(notmuch count --lastmod '*' | cut -f 3)
|
||||
for query in '()' '(not)' '(and)' '(or ())' '(or (not))' '(or (and))' \
|
||||
'(or (and) (or) (not (and)))'; do
|
||||
test_begin_subtest "reindex all messages: $query"
|
||||
notmuch reindex --query=sexp "$query"
|
||||
output=$(notmuch count --lastmod '*' | cut -f 3)
|
||||
count=$((count + 1))
|
||||
test_expect_equal "$output" "$count"
|
||||
done
|
||||
|
||||
fi
|
||||
|
||||
notmuch tag +usertag1 '*'
|
||||
|
||||
notmuch search '*' 2>1 | notmuch_search_sanitize > initial-threads
|
||||
|
@ -41,6 +56,7 @@ notmuch dump > OUTPUT
|
|||
notmuch tag -attachment2 -encrypted2 -signed2 '*'
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
|
||||
backup_database
|
||||
test_begin_subtest 'reindex moves a message between threads'
|
||||
notmuch search --output=threads id:87iqd9rn3l.fsf@vertex.dottedmag > EXPECTED
|
||||
# re-parent
|
||||
|
@ -48,7 +64,9 @@ sed -i 's/1258471718-6781-1-git-send-email-dottedmag@dottedmag.net/87iqd9rn3l.fs
|
|||
notmuch reindex id:1258471718-6781-2-git-send-email-dottedmag@dottedmag.net
|
||||
notmuch search --output=threads id:1258471718-6781-2-git-send-email-dottedmag@dottedmag.net > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
restore_database
|
||||
|
||||
backup_database
|
||||
test_begin_subtest 'reindex detects removal of all files'
|
||||
notmuch search --output=messages not id:20091117232137.GA7669@griffis1.net> EXPECTED
|
||||
# remove both copies
|
||||
|
@ -56,6 +74,17 @@ mv $MAIL_DIR/cur/51:2,* duplicate-message-2.eml
|
|||
notmuch reindex id:20091117232137.GA7669@griffis1.net
|
||||
notmuch search --output=messages '*' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
restore_database
|
||||
|
||||
backup_database
|
||||
test_begin_subtest 'reindex detects removal of all files'
|
||||
notmuch search --output=messages not id:20091117232137.GA7669@griffis1.net> EXPECTED
|
||||
# remove both copies
|
||||
mv $MAIL_DIR/cur/51:2,* duplicate-message-2.eml
|
||||
notmuch reindex id:20091117232137.GA7669@griffis1.net
|
||||
notmuch search --output=messages '*' > OUTPUT
|
||||
test_expect_equal_file EXPECTED OUTPUT
|
||||
restore_database
|
||||
|
||||
test_begin_subtest "reindex preserves properties"
|
||||
cat <<EOF > prop-dump
|
||||
|
|
52
test/corpora/crypto/encrypted-rfc822-attachment
Normal file
52
test/corpora/crypto/encrypted-rfc822-attachment
Normal file
|
@ -0,0 +1,52 @@
|
|||
Content-Type: multipart/encrypted; protocol="application/pgp-encrypted"; boundary="===============9060418334135509864=="
|
||||
MIME-Version: 1.0
|
||||
From: Notmuch test suite <test_suite@notmuchmail.org>
|
||||
To: test_suite@notmuchmail.org
|
||||
Subject: testing encrypted rfc822 attachments
|
||||
Date: Sat, 03 Jul 2021 16:00:02 -0300
|
||||
Message-ID: <encrypted-rfc822-attachment@crypto.notmuchmail.org>
|
||||
User-Agent: alot/0.9.1
|
||||
|
||||
--===============9060418334135509864==
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Type: application/pgp-encrypted; charset="us-ascii"
|
||||
|
||||
Version: 1
|
||||
--===============9060418334135509864==
|
||||
MIME-Version: 1.0
|
||||
Content-Transfer-Encoding: 7bit
|
||||
Content-Type: application/octet-stream; charset="us-ascii"
|
||||
|
||||
-----BEGIN PGP MESSAGE-----
|
||||
|
||||
hIwDxE023q1UqxYBBACGKSDv5/rcwScSf9n33cZZPPxltQgxkDaClMGY2DARgebE
|
||||
9rpE2O/4eoaEbdu+2shahPLIbD0wuRiGVpMIIloNNucl3f15h1wXPZbTwK7sNxJq
|
||||
HycSf8sT1fkolmC9s9X0r5xHgk0G4klAqr5C3GOk7Y6wsHTYGqzDvBFEB0LvaoUC
|
||||
DANw48DehwaEUQEP/iaiYeMDUsOzFZodKZOlQWese2JPTsRwF7KrTl8C93MrZqAh
|
||||
A1pQjUH9cafj8mwDXA9ZCsYZs7r84IxShI+dUhinBSCq8F61OlLP859wq+wpKU7n
|
||||
PNVA5bfd//4hRFvDT33ZlgeeeCcRo7h4IDjJwFDYsf0Ysqvo+IKipVNXXlAcGYYI
|
||||
DVBucB0fYaVHWRKxw50mo02zKP2/GW6K3p1nxGTf73cKjc9of+VOvvMByODaJ+ne
|
||||
WIuzZMqz0vfQ0UvRVBjlsXtB7VpCqJZWkubqqwwP6+2WOCA/c5LC3z2f/BK90EQh
|
||||
/JrKfDR083UNhu4SjNwL/TF4ET33JHf1u5Gmzqx+eO00pfhVvkyz6LYImkE8ky/+
|
||||
bXyJY8iDq7dxtfqhzZbeNe4fafU/avXxTA5UkWTnYhCqyd2bvAYH3Ep3L7lSv6SQ
|
||||
Tsy0jjTsWJoSq6jRIzJuo2mX2MBKPBfLZs4tH71/4RppECletNnS4ZlxiV4LNrWE
|
||||
LrXQvE1V+mJ82muucIe7w52nf3UWjQqTA73+Ml0aK+lIhbckRIovAw1sGzRrbTEX
|
||||
xLCgz7BYDMhs5mgtfiMAzGox4xGxi56Ge519vdbddan4G92mPlLl1IPOXkO8GyLO
|
||||
D4IiPp5ilPy6uThuxxIFemxxUREbPrfLJNA8W7aRPrHz4YcgZhrAV9I4C+xE0ukB
|
||||
i8MoJeFvbCGPyTwVDn8XfFKynlZYm1f8NIVMSj5JfV8J3Om9jzDp6hx+52iUQEbW
|
||||
C9g4kfPZY8h0RMggdOlZsaR8j26xuW+fEtz7ucJIqfJ/ElTH+4bm8MK2qPZniRWv
|
||||
ej2md4bP4Bo5DXydzxz7O7TBL6/Jsp7pJhHUUb36OnTWvInyg71LPT1QIxdRvXr9
|
||||
vNhrEBDX3MNf7RyXczvBcc+cLRo+zV+T4b8wd2kwXskWgKrGUJEe2TItdsafaQ9B
|
||||
BkuVGu6cIDa6STyCJiOB68KIXiDuADSWJiiR+gZr4eU6vzfhR27LMQt/g+oPW+U4
|
||||
1AvzUl9uXjTMC2vFuTQ4M2g0WmksCNpPpzOu/QlBmRqpP2Fg9UuLv6ITWeCxp439
|
||||
g5NT5KXE2IiruL/DS0KEpWVNe4ayGzRvMawFuU582xbIzXjvilUZrW+p6req+oeF
|
||||
QWTWHGDojTvULQPV2c91lWnLaNXVethfF4hrM5MIL+EwVs3sUXFMr1kX7VNrK0QH
|
||||
Uos7nc4G9xngpdvwF4ImldD83O8qxOVzIfk5Dhz+etTH4HbnnDvmQ/FIYvjzGviR
|
||||
ZeVwdCjv/9TC4yY3nJFKMwGp70jVa1vbmWL68HVNRyAVwnQnu2UlI8UR43iVZyUH
|
||||
ZY0Nr0rbse/pvZyX4//EVOFLUR7K4GG0N4Kz41q5ZB8rI4Fl6QJhgIFJds13iM77
|
||||
n+wqLQE7kllgI32E4U9B
|
||||
=YlXg
|
||||
-----END PGP MESSAGE-----
|
||||
|
||||
--===============9060418334135509864==--
|
53
test/emacs-tree.expected-output/result-format-function
Normal file
53
test/emacs-tree.expected-output/result-format-function
Normal file
|
@ -0,0 +1,53 @@
|
|||
2010-12-29 François Boulogne ─►[aur-general] Guidelines: cp, mkdir vs install ( ui)
|
||||
2010-12-16 Olivier Berger ─►Essai accentué ( ui)
|
||||
2009-11-18 Chris Wilson ─►[notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once ( ui)
|
||||
2009-11-18 Alex Botero-Lowry ┬►[notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (& ui)
|
||||
2009-11-18 Carl Worth ╰─►[notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop ( ui)
|
||||
2009-11-17 Ingmar Vanhassel ┬►[notmuch] [PATCH] Typsos ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Adrian Perez de Cast ┬►[notmuch] Introducing myself ( =ui)
|
||||
2009-11-18 Keith Packard ├─► ... ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Israel Herraiz ┬►[notmuch] New to the list ( ui)
|
||||
2009-11-18 Keith Packard ├─► ... ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Jan Janak ┬►[notmuch] What a great idea! ( ui)
|
||||
2009-11-17 Jan Janak ├─► ... ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Jan Janak ┬►[notmuch] [PATCH] Older versions of install do not support -C. ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Aron Griffis ┬►[notmuch] archive ( ui)
|
||||
2009-11-18 Keith Packard ╰┬► ... ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Keith Packard ┬►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags ( ui)
|
||||
2009-11-18 Carl Worth ╰─►[notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags ( ui)
|
||||
2009-11-17 Lars Kellogg-Stedman ┬►[notmuch] Working with Maildir storage? ( = i)
|
||||
2009-11-17 Mikhail Gusarov ├┬► ... ( =ui)
|
||||
2009-11-17 Lars Kellogg-Stedman │╰┬► ... ( =ui)
|
||||
2009-11-17 Mikhail Gusarov │ ├─► ... ( ui)
|
||||
2009-11-17 Keith Packard │ ╰┬► ... ( ui)
|
||||
2009-11-18 Lars Kellogg-Stedman │ ╰─► ... ( =ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-17 Mikhail Gusarov ┬►[notmuch] [PATCH 1/2] Close message file after parsing message headers ( i)
|
||||
2009-11-17 Mikhail Gusarov ├─►[notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++ file with gcc 4.4 ( ui)
|
||||
2009-11-17 Carl Worth ╰┬►[notmuch] [PATCH 1/2] Close message file after parsing message headers ( ui)
|
||||
2009-11-17 Keith Packard ╰┬► ... ( ui)
|
||||
2009-11-18 Carl Worth ╰─► ... ( ui)
|
||||
2009-11-18 Keith Packard ┬►[notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap ( ui)
|
||||
2009-11-18 Alexander Botero-Low ╰─►[notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap ( ui)
|
||||
2009-11-18 Alexander Botero-Low ─►[notmuch] request for pull ( ui)
|
||||
2009-11-18 Jjgod Jiang ┬►[notmuch] Mac OS X/Darwin compatibility issues ( ui)
|
||||
2009-11-18 Alexander Botero-Low ╰┬► ... ( ui)
|
||||
2009-11-18 Jjgod Jiang ╰┬► ... ( ui)
|
||||
2009-11-18 Alexander Botero-Low ╰─► ... ( ui)
|
||||
2009-11-18 Rolland Santimano ─►[notmuch] Link to mailing list archives ? ( ui)
|
||||
2009-11-18 Jan Janak ─►[notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags ( ui)
|
||||
2009-11-18 Stewart Smith ─►[notmuch] [PATCH] count_files: sort directory in inode order before statting ( ui)
|
||||
2009-11-18 Stewart Smith ─►[notmuch] [PATCH 2/2] Read mail directory in inode number order ( ui)
|
||||
2009-11-18 Stewart Smith ─►[notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. ( ui)
|
||||
2009-11-18 Lars Kellogg-Stedman ┬►[notmuch] "notmuch help" outputs to stderr? (&=ui)
|
||||
2009-11-18 Lars Kellogg-Stedman ╰─► ... (&=ui)
|
||||
2009-11-17 Mikhail Gusarov ─►[notmuch] [PATCH] Handle rename of message file ( ui)
|
||||
2009-11-17 Alex Botero-Lowry ┬►[notmuch] preliminary FreeBSD support (& ui)
|
||||
2009-11-17 Carl Worth ╰─► ... ( ui)
|
||||
End of search results.
|
53
test/emacs-unthreaded.expected-output/result-format-function
Normal file
53
test/emacs-unthreaded.expected-output/result-format-function
Normal file
|
@ -0,0 +1,53 @@
|
|||
2010-12-29 François Boulogne [aur-general] Guidelines: cp, mkdir vs install ( ui)
|
||||
2010-12-16 Olivier Berger Essai accentué ( ui)
|
||||
2009-11-18 Chris Wilson [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once( ui)
|
||||
2009-11-18 Carl Worth [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop( ui)
|
||||
2009-11-18 Carl Worth [notmuch] [PATCH] Typsos ( ui)
|
||||
2009-11-18 Carl Worth [notmuch] Introducing myself ( ui)
|
||||
2009-11-18 Carl Worth [notmuch] New to the list ( ui)
|
||||
2009-11-18 Carl Worth [notmuch] What a great idea! ( ui)
|
||||
2009-11-18 Carl Worth [notmuch] [PATCH] Older versions of install do not support -C.( ui)
|
||||
2009-11-18 Carl Worth [notmuch] archive ( ui)
|
||||
2009-11-18 Carl Worth [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags( ui)
|
||||
2009-11-18 Carl Worth [notmuch] Working with Maildir storage? ( ui)
|
||||
2009-11-18 Carl Worth [notmuch] [PATCH 1/2] Close message file after parsing message headers( ui)
|
||||
2009-11-18 Alexander Botero-Low[notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap( ui)
|
||||
2009-11-18 Keith Packard [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap( ui)
|
||||
2009-11-18 Alexander Botero-Low[notmuch] request for pull ( ui)
|
||||
2009-11-18 Alexander Botero-Low[notmuch] Mac OS X/Darwin compatibility issues ( ui)
|
||||
2009-11-18 Jjgod Jiang [notmuch] Mac OS X/Darwin compatibility issues ( ui)
|
||||
2009-11-18 Alexander Botero-Low[notmuch] Mac OS X/Darwin compatibility issues ( ui)
|
||||
2009-11-18 Rolland Santimano [notmuch] Link to mailing list archives ? ( ui)
|
||||
2009-11-18 Jan Janak [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags( ui)
|
||||
2009-11-18 Jjgod Jiang [notmuch] Mac OS X/Darwin compatibility issues ( ui)
|
||||
2009-11-18 Stewart Smith [notmuch] [PATCH] count_files: sort directory in inode order before statting( ui)
|
||||
2009-11-18 Keith Packard [notmuch] archive ( ui)
|
||||
2009-11-18 Keith Packard [notmuch] Introducing myself ( ui)
|
||||
2009-11-18 Keith Packard [notmuch] New to the list ( ui)
|
||||
2009-11-18 Stewart Smith [notmuch] [PATCH 2/2] Read mail directory in inode number order( ui)
|
||||
2009-11-18 Stewart Smith [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs.( ui)
|
||||
2009-11-18 Lars Kellogg-Stedman[notmuch] "notmuch help" outputs to stderr? (&=ui)
|
||||
2009-11-18 Lars Kellogg-Stedman[notmuch] "notmuch help" outputs to stderr? (&=ui)
|
||||
2009-11-18 Lars Kellogg-Stedman[notmuch] Working with Maildir storage? ( =ui)
|
||||
2009-11-18 Alex Botero-Lowry [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop(& ui)
|
||||
2009-11-17 Ingmar Vanhassel [notmuch] [PATCH] Typsos ( ui)
|
||||
2009-11-17 Aron Griffis [notmuch] archive ( ui)
|
||||
2009-11-17 Adrian Perez de Cast[notmuch] Introducing myself ( =ui)
|
||||
2009-11-17 Israel Herraiz [notmuch] New to the list ( ui)
|
||||
2009-11-17 Jan Janak [notmuch] What a great idea! ( ui)
|
||||
2009-11-17 Jan Janak [notmuch] What a great idea! ( ui)
|
||||
2009-11-17 Jan Janak [notmuch] [PATCH] Older versions of install do not support -C.( ui)
|
||||
2009-11-17 Keith Packard [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags( ui)
|
||||
2009-11-17 Keith Packard [notmuch] Working with Maildir storage? ( ui)
|
||||
2009-11-17 Keith Packard [notmuch] [PATCH 1/2] Close message file after parsing message headers( ui)
|
||||
2009-11-17 Mikhail Gusarov [notmuch] [PATCH] Handle rename of message file ( ui)
|
||||
2009-11-17 Mikhail Gusarov [notmuch] Working with Maildir storage? ( ui)
|
||||
2009-11-17 Lars Kellogg-Stedman[notmuch] Working with Maildir storage? ( =ui)
|
||||
2009-11-17 Carl Worth [notmuch] preliminary FreeBSD support ( ui)
|
||||
2009-11-17 Alex Botero-Lowry [notmuch] preliminary FreeBSD support (& ui)
|
||||
2009-11-17 Mikhail Gusarov [notmuch] Working with Maildir storage? ( =ui)
|
||||
2009-11-17 Lars Kellogg-Stedman[notmuch] Working with Maildir storage? ( =ui)
|
||||
2009-11-17 Carl Worth [notmuch] [PATCH 1/2] Close message file after parsing message headers( ui)
|
||||
2009-11-17 Mikhail Gusarov [notmuch] [PATCH 2/2] Include <stdint.h> to get uint32_t in C++ file with gcc 4.4( ui)
|
||||
2009-11-17 Mikhail Gusarov [notmuch] [PATCH 1/2] Close message file after parsing message headers( ui)
|
||||
End of search results.
|
25
test/emacs.expected-output/search-result-format-function
Normal file
25
test/emacs.expected-output/search-result-format-function
Normal file
|
@ -0,0 +1,25 @@
|
|||
ui 2010-12-29 [1/1] François Boulogne [aur-general] Guidelines: cp, mkdir vs install (inbox unread)
|
||||
ui 2010-12-16 [1/1] Olivier Berger Essai accentué (inbox unread)
|
||||
ui 2009-11-18 [1/1] Chris Wilson [notmuch] [PATCH 1/2] Makefile: evaluate pkg-config once (inbox unread)
|
||||
& ui 2009-11-18 [2/2] Alex Botero-Lowry, Carl Worth [notmuch] [PATCH] Error out if no query is supplied to search instead of going into an infinite loop (attachment inbox unread)
|
||||
ui 2009-11-18 [2/2] Ingmar Vanhassel, Carl Worth [notmuch] [PATCH] Typsos (inbox unread)
|
||||
=ui 2009-11-18 [3/3] Adrian Perez de Castro, Keith Packard, Carl Worth [notmuch] Introducing myself (inbox signed unread)
|
||||
ui 2009-11-18 [3/3] Israel Herraiz, Keith Packard, Carl Worth [notmuch] New to the list (inbox unread)
|
||||
ui 2009-11-18 [3/3] Jan Janak, Carl Worth [notmuch] What a great idea! (inbox unread)
|
||||
ui 2009-11-18 [2/2] Jan Janak, Carl Worth [notmuch] [PATCH] Older versions of install do not support -C. (inbox unread)
|
||||
ui 2009-11-18 [3/3] Aron Griffis, Keith Packard, Carl Worth [notmuch] archive (inbox unread)
|
||||
ui 2009-11-18 [2/2] Keith Packard, Carl Worth [notmuch] [PATCH] Make notmuch-show 'X' (and 'x') commands remove inbox (and unread) tags (inbox unread)
|
||||
=ui 2009-11-18 [7/7] Lars Kellogg-Stedman, Mikhail Gusarov, Keith Packard, Carl Worth [notmuch] Working with Maildir storage? (inbox signed unread)
|
||||
ui 2009-11-18 [5/5] Mikhail Gusarov, Carl Worth, Keith Packard [notmuch] [PATCH 1/2] Close message file after parsing message headers (inbox unread)
|
||||
ui 2009-11-18 [2/2] Keith Packard, Alexander Botero-Lowry [notmuch] [PATCH] Create a default notmuch-show-hook that highlights URLs and uses word-wrap (inbox unread)
|
||||
ui 2009-11-18 [1/1] Alexander Botero-Lowry [notmuch] request for pull (inbox unread)
|
||||
ui 2009-11-18 [4/4] Jjgod Jiang, Alexander Botero-Lowry [notmuch] Mac OS X/Darwin compatibility issues (inbox unread)
|
||||
ui 2009-11-18 [1/1] Rolland Santimano [notmuch] Link to mailing list archives ? (inbox unread)
|
||||
ui 2009-11-18 [1/1] Jan Janak [notmuch] [PATCH] notmuch new: Support for conversion of spool subdirectories into tags (inbox unread)
|
||||
ui 2009-11-18 [1/1] Stewart Smith [notmuch] [PATCH] count_files: sort directory in inode order before statting (inbox unread)
|
||||
ui 2009-11-18 [1/1] Stewart Smith [notmuch] [PATCH 2/2] Read mail directory in inode number order (inbox unread)
|
||||
ui 2009-11-18 [1/1] Stewart Smith [notmuch] [PATCH] Fix linking with gcc to use g++ to link in C++ libs. (inbox unread)
|
||||
&=ui 2009-11-18 [2/2] Lars Kellogg-Stedman [notmuch] "notmuch help" outputs to stderr? (attachment inbox signed unread)
|
||||
ui 2009-11-17 [1/1] Mikhail Gusarov [notmuch] [PATCH] Handle rename of message file (inbox unread)
|
||||
& ui 2009-11-17 [2/2] Alex Botero-Lowry, Carl Worth [notmuch] preliminary FreeBSD support (attachment inbox unread)
|
||||
End of search results.
|
|
@ -122,7 +122,8 @@ const notmuch_opt_desc_t notmuch_shared_options[] = {
|
|||
const char *notmuch_requested_db_uuid = NULL;
|
||||
|
||||
void
|
||||
notmuch_process_shared_options (unused (const char *dummy))
|
||||
notmuch_process_shared_options (unused (notmuch_database_t *notmuch),
|
||||
unused (const char *dummy))
|
||||
{
|
||||
}
|
||||
|
||||
|
|
62
test/smime/0xE0972A47.p12
Normal file
62
test/smime/0xE0972A47.p12
Normal file
|
@ -0,0 +1,62 @@
|
|||
Issuer ...: /CN=Notmuch Test Suite
|
||||
Serial ...: 6F748C94BD0C67A9
|
||||
Subject ..: /CN=Notmuch Test Suite
|
||||
aka ..: test_suite@notmuchmail.org
|
||||
Keygrip ..: 1727B9C7108D50333614F3B1DD0807F624B31130
|
||||
|
||||
-----BEGIN PKCS12-----
|
||||
MIIJ+AIBAzCCCb4GCSqGSIb3DQEHAaCCCa8EggmrMIIJpzCCBAcGCSqGSIb3DQEH
|
||||
BqCCA/gwggP0AgEAMIID7QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIcfMY
|
||||
MS7tOpcCAggAgIIDwFu7ZRNrXCb0eKei44aeBZPRs9YI/5EpMcFuc8j4/8T1HkIt
|
||||
GuRe/HzRmoiLZcAMOzGC/hF8TkHlNeUZ7rOSpCg4UlBVWJS6avTMHHsakDvTV/7q
|
||||
X5VNi4pLUuyEToGTAPHV+s5P/gYYG6mFPkwG/pDDlAcgMhgtuPY/lQp6IS/E6CaR
|
||||
fhcnQiPq9ySTqO7UNwIyMwtAtSHkgBaje8UbOkQch4lg51i97rm9m4EMvklKtjXc
|
||||
Ud4aTEuoZguPmdBdLvF5QxqJf6Bm9lHa1Awhru2gBWQf9TjX8bwK9Xsv8G6gPOwc
|
||||
LVpIR9fMZtgBbc+heeJTjfn6VqEy881ckbkz+38hiN3pbLMuATM7QAY3u3N4whM6
|
||||
Hmfyl3iqba84Pl93zaUzqazAUeFdqcqSpAUGkS4gU6klr9qi3NicaGbry1DySYU7
|
||||
2h4xy3j7eiHxqdWaibdPoBC8CEbPaFj2qnOVsZykxG6zPvbEB+5sJ/a+T6xm1Btx
|
||||
N6vXR7ObbXlpC4pRkS32ehuRbY6wc6H2KKepOMCu7x10tN0Up5ccNxvkT26QIrEE
|
||||
LW296ijCLbsRhWymDtopWAZHcXXIu0fJ4tocSp2c3lojSEYu1jlMXR+Pa4R8EtgZ
|
||||
lb5+NqISxjUlMMWzGDyhrp9ImcsZmpv6N8zPcZVyU+M1/h+p9ur/IOVZU9P1vIKy
|
||||
kcM4pslr0JhLfnZCLZ+3Ux1yKAcndGZFPb1vZ83jyZKR38BVSGu53ODaBJBqSMHu
|
||||
Mv2Na/qzvQBSVJuWF9cAhiVd7v9R/EvT0zmljN4w7l4EXsB5wRsO1wvlL+MhwaET
|
||||
dIHbRH2GD3gERX6oTc3t3cgritVePk70rCxQDxn5zUbjW7dNIlIobAumLHBfgSxR
|
||||
QCE6gxdTm5MW2O9hnfTSQvliVaGU1gd0M3BRiqeNpPPxnloGKnOEODM381F4HxyR
|
||||
CzO2r/2aKJP+U5HxSf4cljp3/Lripxykzfqc9/xZshl+jGixsSSm+Ul916Hpj2Rt
|
||||
j9vHg4H9YfJTGdvzxZcvZCvNSy3ygtjx0++SrI5hGHKjpVJIK2/9Wi39q5s6LkiA
|
||||
RCjvuoBBcQXm++69X7QGWSsGFtwerCGnq3nAxGpHVKVGTvFYMAg6y1RR0zvE0SuM
|
||||
MZegD8w45QyrmiPqSRM7/RtqVdA+r/wiJwWerUBq+mrCvJHB2NRcjiUiCJY1bjRU
|
||||
ATMfB0uZaNInUXiLDGxp2mdBgdFVq7sYTbq+OvprzxeAjIvodxl3J9ThvJnt1fzK
|
||||
RPCJw5COI60ibE3XTTCCBZgGCSqGSIb3DQEHAaCCBYkEggWFMIIFgTCCBX0GCyqG
|
||||
SIb3DQEMCgECoIIE7jCCBOowHAYKKoZIhvcNAQwBAzAOBAiEe8CcxIIv9wICCAAE
|
||||
ggTIujut93lYPUsKc/JNhZhUWS/RHHog6d8ZAjpFvXpyD8Z2z4A4PpgIn8eUSRW5
|
||||
Gwp8izR+16Tj3ht52pJ5Y1x27/S3l3sDlekEZ/33X/AdLFWAXbcibmwtRea1ucKZ
|
||||
ze3DJM7CvuRvVSBG8XubPGi3pZkEjHBGQqgtsTnxlBp0PXl7wxfyT7F6gOH2DGYP
|
||||
bYzNa2fnY8twEcUYhuksI/eh9Zwj9TrF0HWq1hwp0tDCfqutzshSX2GQ/p0raL3B
|
||||
C2stHBjl0OVUfDHpqQ5OJWbQvGcJntECqu4gmSJohunObaUKcN8xs+FzB5czpmsT
|
||||
W/pyR58nc8QhTttByqZN3EerhEogWDZj4tQ6dK8p6bqLO/0qqBehZGchfof5Evwj
|
||||
VFsvVGD8xVLQWWAFnrQs5+U56NQEbmZzN5RCI7FEK2VVOeG03dpXyoAQyxuYrsYU
|
||||
3znmoSleIqDDBFD21YePUcJZ0R8AQsvgV11tdwPWqr1hk0bIazLQ9rappGrTgkK8
|
||||
DFdQKSH1dRvjqtbuDyY7j5PXXJTXthVv9T9N7Vp6qU+pWBQ1Mz30J+fHX2ilEnbi
|
||||
tQ49hwt1+/2Zkmwz3reoEnxYOKzCg/ySIpQ27/Hx4xZ+ecEzX/0IxCkHeAV3V3bB
|
||||
1z8wFxWEh1s9hL6C8lRk/wQ9KsKaxM7BdLw7RjiqEwR4HgeCqMPdCVQQpILARDC8
|
||||
Poz8xUmjv7HyIvvyBUP12YdIj74Jjj0Mm2r/FDj7nsXxkjXMZEMMKK3oVaAMq8Bd
|
||||
cO4VQXDd7bgNzLF9PKxWNjoCuQcPJXwMPqlFoc06BLPstEaR4enafv0Pd4l0pyME
|
||||
YgezyVW+3yFEsbbB2UUs0r7oqxsDFU9/iHf8O3nu3NuKTJkux4uMlOTBKsm6sY7k
|
||||
GduP2UA+WU27jHrf4zQQbkDLG1lJFfcaKzlcOmz5B9iZwugBz9Y28w5f2/12Kqrh
|
||||
4tibFBUG0E85KAb1wnFUNUx06OMX229U1M0E1LHbcUJ9mcRipONPVn0FRi8XzaLK
|
||||
023XRoihuoWhVUiB1OJ2eZW1JnUYRztfa3nfmGjXv4VGkxYlnTkE9z0PAAhf6t5A
|
||||
7Ir0y1JUeOlBITTcojOp6qQ8tMQQ5wRk1oncHiw3WwJvFN6fOa9Q/+4ZmULHz0vV
|
||||
Xl+Qio8B7/4jqZoT4e/gK6U/zHriznLzqp63LjP47eFRXTfuXslaCt7YF75Mq2J6
|
||||
VPA+qfYRw0K5BvDUkr8c+nLP2AiDaEYVBHGdBRTlWO9UkcB1F4cuZZiU5MZbxVrb
|
||||
Db+zGWW6AT+4XTO4z9KmAqgTTv1+BQrLxNI+RG8JfQapUKQyB794F4kXK2yhd1P3
|
||||
XS9cwh24COiqbOpI1nB5qn7cn4RRHW156LWGF+VJFdxR6Wu3vZx/kZGevG9o1ARF
|
||||
z1l9mbGyhwnUJO1EQwjbppvRou1bZuNbuRgLmHKEVPAv+J+7hLXZAnRdwoV0x91t
|
||||
bpmy4qyxA/90DHguIhRVcKsYBrdShY7LXdZArECBhMY9R41D6v1yyhC6fL6PKR5g
|
||||
DaluN2K9TBALzZH7NnNdE14l+56+kLc9Fq8JXsq3rxdeBTsNl09fHPf9w5VLkq4I
|
||||
doNcPPlta0Q0xJNa/RYENCJpAMZdMFIJ558uMXwwVQYJKoZIhvcNAQkUMUgeRgBH
|
||||
AG4AdQBQAEcAIABlAHgAcABvAHIAdABlAGQAIABjAGUAcgB0AGkAZgBpAGMAYQB0
|
||||
AGUAIABlADAAOQA3ADIAYQA0ADcwIwYJKoZIhvcNAQkVMRYEFGFvRs1zg0xjhHdW
|
||||
rw37ZKbglypHMDEwITAJBgUrDgMCGgUABBSluQBa+tVpYVYmB/zAZuPE9NnargQI
|
||||
XWSQTDEONWgCAggA
|
||||
-----END PKCS12-----
|
|
@ -159,6 +159,33 @@ running, quit if it terminated."
|
|||
(lambda (x) `(prog1 ,x (notmuch-post-command)))
|
||||
body)))
|
||||
|
||||
;; For testing functions in
|
||||
;; notmuch-{search,tree,unsorted}-result-format
|
||||
(defun notmuch-test-result-flags (format-string result)
|
||||
(let ((tags-to-letters (quote (("attachment" . "&")
|
||||
("signed" . "=")
|
||||
("unread" . "u")
|
||||
("inbox" . "i"))))
|
||||
(tags (plist-get result :tags)))
|
||||
(format format-string
|
||||
(mapconcat (lambda (t2l)
|
||||
(if (member (car t2l) tags)
|
||||
(cdr t2l)
|
||||
" "))
|
||||
tags-to-letters ""))))
|
||||
|
||||
;; Log any signalled error (and other messages) to MESSAGES
|
||||
;; Log "COMPLETE" if forms complete without error.
|
||||
(defmacro test-log-error (&rest body)
|
||||
`(progn
|
||||
(with-current-buffer "*Messages*"
|
||||
(let ((inhibit-read-only t)) (erase-buffer)))
|
||||
(condition-case err
|
||||
(progn ,@body
|
||||
(message "COMPLETE"))
|
||||
(t (message "%s" err)))
|
||||
(with-current-buffer "*Messages*" (test-output "MESSAGES"))))
|
||||
|
||||
;; For historical reasons, we hide deleted tags by default in the test
|
||||
;; suite
|
||||
(setq notmuch-tag-deleted-formats
|
||||
|
|
|
@ -143,10 +143,8 @@ add_gpgsm_home () {
|
|||
_gnupg_exit () { gpgconf --kill all 2>/dev/null || true; }
|
||||
at_exit_function _gnupg_exit
|
||||
mkdir -p -m 0700 "$GNUPGHOME"
|
||||
openssl pkcs12 -export -passout pass: -inkey "$NOTMUCH_SRCDIR/test/smime/key+cert.pem" \
|
||||
< "$NOTMUCH_SRCDIR/test/smime/test.crt" | \
|
||||
gpgsm --batch --no-tty --no-common-certs-import --pinentry-mode=loopback --passphrase-fd 3 \
|
||||
--disable-dirmngr --import >"$GNUPGHOME"/import.log 2>&1 3<<<''
|
||||
gpgsm --batch --no-tty --no-common-certs-import --pinentry-mode=loopback --passphrase-fd 3 \
|
||||
--disable-dirmngr --import >"$GNUPGHOME"/import.log 2>&1 3<<<'' <$NOTMUCH_SRCDIR/test/smime/0xE0972A47.p12
|
||||
fpr=$(gpgsm --batch --list-key test_suite@notmuchmail.org | sed -n 's/.*fingerprint: //p')
|
||||
echo "$fpr S relax" >> "$GNUPGHOME/trustlist.txt"
|
||||
gpgsm --quiet --batch --no-tty --no-common-certs-import --disable-dirmngr --import < $NOTMUCH_SRCDIR/test/smime/ca.crt
|
||||
|
@ -911,7 +909,7 @@ test_done () {
|
|||
test_python () {
|
||||
# Note: if there is need to print debug information from python program,
|
||||
# use stdout = os.fdopen(6, 'w') or stderr = os.fdopen(7, 'w')
|
||||
PYTHONPATH="$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \
|
||||
PYTHONPATH="$NOTMUCH_BUILDDIR/bindings/python-cffi/build/stage:$NOTMUCH_SRCDIR/bindings/python${PYTHONPATH:+:$PYTHONPATH}" \
|
||||
$NOTMUCH_PYTHON -B - > OUTPUT
|
||||
}
|
||||
|
||||
|
|
|
@ -4,9 +4,16 @@
|
|||
#include <stdbool.h>
|
||||
#include <gmodule.h>
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/* The utf8 encoded string would tokenize as a single word, according
|
||||
* to xapian. */
|
||||
bool unicode_word_utf8 (const char *str);
|
||||
typedef gunichar notmuch_unichar;
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif
|
||||
|
|
|
@ -1 +1 @@
|
|||
0.33.1
|
||||
0.34.2
|
||||
|
|
Loading…
Add table
Reference in a new issue