/bindings/python-cffi/build/
/lib/libnotmuch*.dylib
/lib/libnotmuch.so*
+/nmbug
/notmuch
+/notmuch-git
/notmuch-shared
/releases
/sh.config
# -*- makefile-gmake -*-
.PHONY: all
-all: notmuch notmuch-shared build-man build-info ruby-bindings python-cffi-bindings
+all: notmuch notmuch-shared build-man build-info ruby-bindings python-cffi-bindings notmuch-git nmbug
ifeq ($(MAKECMDGOALS),)
ifeq ($(shell cat .first-build-message 2>/dev/null),)
@NOTMUCH_FIRST_BUILD=1 $(MAKE) --no-print-directory all
$(DETACHED_SIG_FILE): $(TAR_FILE)
gpg --armor --detach-sign $^
+CLEAN := $(CLEAN) notmuch-git
+notmuch-git: notmuch-git.py
+ cp $< $@
+ chmod ugo+x $@
+
+CLEAN := $(CLEAN) nmbug
+nmbug: notmuch-git
+ ln -s $< $@
+
.PHONY: dist
dist: $(TAR_FILE)
+Notmuch 0.37 (2022-08-21)
+=========================
+
+Library
+-------
+
+Fix uninitialized field in message objects.
+
+Improve exception handling and error propagation for message objects.
+
+Sexp Queries
+------------
+
+Add one sided lastmod ranges for sexp queries.
+
+Expand macro parameters inside regex and wildcard modifiers.
+
+Command Line Interface
+----------------------
+
+`notmuch help` now works for external commands.
+
+`NOTMUCH_CONFIG` is now passed to external commands and hooks.
+
+Promote the development tool `nmbug` to a user facing tool
+`notmuch-git`. See notmuch-git(1) for details.
+
+Emacs
+-----
+
+The function `notmuch-mua-mail` now moves point depending on the
+provided arguments.
+
+Restrict what mime types are inlined in replies and on refresh.
+
+The functions in notmuch-query.el are now obsolete and may be removed
+in a future version of Notmuch.
+
+Add some controls for lazy display of message bodies (See "Dealing
+with large messages and threads" in the notmuch-emacs documentation).
+
+Allow the user to select (with '%') a different duplicate message file
+to display.
+
+Use `message-dont-reply-to-names` in `notmuch-message-mode`.
+
+Support custom header-line format for notmuch-show mode.
+
Notmuch 0.36 (2022-04-25)
=========================
NOTMUCH_STATUS_DATABASE_EXISTS,
NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
NOTMUCH_STATUS_NO_MAIL_ROOT,
+ NOTMUCH_STATUS_CLOSED_DATABASE,
NOTMUCH_STATUS_LAST_STATUS
} notmuch_status_t;
typedef enum {
package from Python's standard library. You could e.g. create
this as such::
- notmuch_msg = db.get_message(msgid) # or from a query
+ notmuch_msg = db.find(msgid) # or from a query
parser = email.parser.BytesParser(policy=email.policy.default)
with notmuch_msg.path.open('rb) as fp:
email_msg = parser.parse(fp)
env = os.environ.copy()
env['NOTMUCH_CONFIG'] = str(cfg_fname)
proc = subprocess.run(cmd,
- timeout=5,
+ timeout=120,
env=env)
proc.check_returncode()
return run
# this file should be kept in sync with ../../../version
-__VERSION__ = '0.36'
+__VERSION__ = '0.37'
SOVERSION = '5'
GMimeSignature *sig = NULL;
GMimeCertificate *cert = NULL;
GMimeObject *output = NULL;
- GMimeValidity validity = GMIME_VALIDITY_UNKNOWN;
int len;
g_mime_init ();
cert = g_mime_signature_get_certificate (sig);
if (cert == NULL) return !! fprintf (stderr, "no GMimeCertificate found\n");
#ifdef CHECK_VALIDITY
- validity = g_mime_certificate_get_id_validity (cert);
+ GMimeValidity validity = g_mime_certificate_get_id_validity (cert);
if (validity != GMIME_VALIDITY_FULL) return !! fprintf (stderr, "Got validity %d, expected %d\n", validity, GMIME_VALIDITY_FULL);
#endif
#ifdef CHECK_EMAIL
NOTMUCH_SRCDIR='${NOTMUCH_SRCDIR}'
+# Flags needed to compile and link against Xapian
+NOTMUCH_XAPIAN_CXXFLAGS="${xapian_cxxflags}"
+NOTMUCH_XAPIAN_LDFLAGS="${xapian_ldflags}"
+
# Whether to have Xapian retry lock
NOTMUCH_HAVE_XAPIAN_DB_RETRY_LOCK=${WITH_RETRY_LOCK}
+# Flags needed to compile and link against GMime
+NOTMUCH_GMIME_CFLAGS="${gmime_cflags}"
+NOTMUCH_GMIME_LDFLAGS="${gmime_ldflags}"
+
# Whether GMime can verify X.509 certificate validity
NOTMUCH_GMIME_X509_CERT_VALIDITY=${gmime_x509_cert_validity}
# Whether GMime can verify signatures when decrypting with a session key:
NOTMUCH_GMIME_VERIFY_WITH_SESSION_KEY=${gmime_verify_with_session_key}
+# Flags needed to compile and link against zlib
+NOTMUCH_ZLIB_CFLAGS="${zlib_cflags}"
+NOTMUCH_ZLIB_LDFLAGS="${zlib_ldflags}"
+
# Does the C compiler support the address sanitizer
NOTMUCH_HAVE_ASAN=${have_asan}
# Is the sfsexp library available?
NOTMUCH_HAVE_SFSEXP=${have_sfsexp}
+# And if so, flags needed at compile/link time for sfsexp
+NOTMUCH_SFSEXP_CFLAGS="${sfsexp_cflags}"
+NOTMUCH_SFSEXP_LDFLAGS="${sfsexp_ldflags}"
+
# Platform we are run on
PLATFORM=${platform}
EOF
+notmuch (0.37-1) unstable; urgency=medium
+
+ * New upstream release.
+ * Build-depend on emacs-el to work around #1017698
+
+ -- David Bremner <bremner@debian.org> Wed, 24 Aug 2022 09:12:19 -0700
+
+notmuch (0.37~rc0-3) experimental; urgency=medium
+
+ * Another no-change re-upload with binaries.
+
+ -- David Bremner <bremner@debian.org> Sun, 14 Aug 2022 11:49:21 -0300
+
+notmuch (0.37~rc0-2) experimental; urgency=medium
+
+ * Binary upload for NEW (notmuch-git is a new binary package)
+
+ -- David Bremner <bremner@debian.org> Sun, 14 Aug 2022 10:55:24 -0300
+
+notmuch (0.37~rc0-1) experimental; urgency=medium
+
+ * New upstream release candidate
+
+ -- David Bremner <bremner@debian.org> Sun, 14 Aug 2022 07:28:22 -0300
+
notmuch (0.36-1~bpo11+1) bullseye-backports; urgency=medium
* Rebuild for bullseye-backports.
dpkg-dev (>= 1.17.14),
dtach (>= 0.8) <!nocheck>,
emacs-nox | emacs-gtk | emacs-lucid | emacs25-nox | emacs25 (>=25~) | emacs25-lucid (>=25~) | emacs24-nox | emacs24 (>=24~) | emacs24-lucid (>=24~),
+ emacs-el,
gdb [!ia64 !mips !mips64el !kfreebsd-any !alpha !hppa] <!nocheck>,
+ git <!nocheck>,
gnupg <!nocheck>,
gpgsm <!nocheck>,
libgmime-3.0-dev (>= 3.0.3~),
.
This package contains the notmuch command-line interface
+Package: notmuch-git
+Architecture: all
+Depends:
+ git,
+ notmuch,
+ python3,
+ ${misc:Depends}
+Description: thread-based email index, search and tagging
+ Notmuch is a system for indexing, searching, reading, and tagging
+ large collections of email messages in maildir or mh format. It uses
+ the Xapian library to provide fast, full-text search with a very
+ convenient search syntax.
+ .
+ This package contains a simple tool to save, restore, and synchronize
+ notmuch tags via git repositories.
+
Package: notmuch-doc
Architecture: all
Depends:
--- /dev/null
+notmuch-git /usr/bin
+nmbug /usr/bin
--- /dev/null
+usr/share/man/man1/notmuch-git.1.gz
+usr/share/man/man1/nmbug.1.gz
| Z | notmuch-tree-from-search-current-query | notmuch-tree-from-show-current-query | |
| =!= | | notmuch-show-toggle-elide-non-matching | |
| =#= | | notmuch-show-print-message | |
+| =%= | | notmuch-show-replace-msg | |
| =$= | | notmuch-show-toggle-process-crypto | |
| =*= | notmuch-search-tag-all | notmuch-show-tag-all | notmuch-tree-tag-thread |
| + | notmuch-search-add-tag | notmuch-show-add-tag | notmuch-tree-add-tag |
+++ /dev/null
-#!/usr/bin/env python3
-#
-# Copyright (c) 2011-2014 David Bremner <david@tethera.net>
-# W. Trevor King <wking@tremily.us>
-#
-# This program is free software: you can redistribute it and/or modify
-# it under the terms of the GNU General Public License as published by
-# the Free Software Foundation, either version 3 of the License, or
-# (at your option) any later version.
-#
-# This program is distributed in the hope that it will be useful,
-# but WITHOUT ANY WARRANTY; without even the implied warranty of
-# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
-# GNU General Public License for more details.
-#
-# You should have received a copy of the GNU General Public License
-# along with this program. If not, see https://www.gnu.org/licenses/ .
-
-"""
-Manage notmuch tags with Git
-
-Environment variables:
-
-* NMBGIT specifies the location of the git repository used by nmbug.
- If not specified $HOME/.nmbug is used.
-* NMBPREFIX specifies the prefix in the notmuch database for tags of
- interest to nmbug. If not specified 'notmuch::' is used.
-"""
-
-from __future__ import print_function
-from __future__ import unicode_literals
-
-import codecs as _codecs
-import collections as _collections
-import functools as _functools
-import inspect as _inspect
-import locale as _locale
-import logging as _logging
-import os as _os
-import re as _re
-import shutil as _shutil
-import subprocess as _subprocess
-import sys as _sys
-import tempfile as _tempfile
-import textwrap as _textwrap
-try: # Python 3
- from urllib.parse import quote as _quote
- from urllib.parse import unquote as _unquote
-except ImportError: # Python 2
- from urllib import quote as _quote
- from urllib import unquote as _unquote
-
-
-__version__ = '0.3'
-
-_LOG = _logging.getLogger('nmbug')
-_LOG.setLevel(_logging.WARNING)
-_LOG.addHandler(_logging.StreamHandler())
-
-NMBGIT = _os.path.expanduser(
- _os.getenv('NMBGIT', _os.path.join('~', '.nmbug')))
-_NMBGIT = _os.path.join(NMBGIT, '.git')
-if _os.path.isdir(_NMBGIT):
- NMBGIT = _NMBGIT
-
-TAG_PREFIX = _os.getenv('NMBPREFIX', 'notmuch::')
-_HEX_ESCAPE_REGEX = _re.compile('%[0-9A-F]{2}')
-_TAG_DIRECTORY = 'tags/'
-_TAG_FILE_REGEX = _re.compile(_TAG_DIRECTORY + '(?P<id>[^/]*)/(?P<tag>[^/]*)')
-
-# magic hash for Git (git hash-object -t blob /dev/null)
-_EMPTYBLOB = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
-
-
-try:
- getattr(_tempfile, 'TemporaryDirectory')
-except AttributeError: # Python < 3.2
- class _TemporaryDirectory(object):
- """
- Fallback context manager for Python < 3.2
-
- See PEP 343 for details on context managers [1].
-
- [1]: https://www.python.org/dev/peps/pep-0343/
- """
- def __init__(self, **kwargs):
- self.name = _tempfile.mkdtemp(**kwargs)
-
- def __enter__(self):
- return self.name
-
- def __exit__(self, type, value, traceback):
- _shutil.rmtree(self.name)
-
-
- _tempfile.TemporaryDirectory = _TemporaryDirectory
-
-
-def _hex_quote(string, safe='+@=:,'):
- """
- quote('abc def') -> 'abc%20def'.
-
- Wrap urllib.parse.quote with additional safe characters (in
- addition to letters, digits, and '_.-') and lowercase hex digits
- (e.g. '%3a' instead of '%3A').
- """
- uppercase_escapes = _quote(string, safe)
- return _HEX_ESCAPE_REGEX.sub(
- lambda match: match.group(0).lower(),
- uppercase_escapes)
-
-
-_ENCODED_TAG_PREFIX = _hex_quote(TAG_PREFIX, safe='+@=,') # quote ':'
-
-
-def _xapian_quote(string):
- """
- Quote a string for Xapian's QueryParser.
-
- Xapian uses double-quotes for quoting strings. You can escape
- internal quotes by repeating them [1,2,3].
-
- [1]: https://trac.xapian.org/ticket/128#comment:2
- [2]: https://trac.xapian.org/ticket/128#comment:17
- [3]: https://trac.xapian.org/changeset/13823/svn
- """
- return '"{0}"'.format(string.replace('"', '""'))
-
-
-def _xapian_unquote(string):
- """
- Unquote a Xapian-quoted string.
- """
- if string.startswith('"') and string.endswith('"'):
- return string[1:-1].replace('""', '"')
- return string
-
-
-class SubprocessError(RuntimeError):
- "A subprocess exited with a nonzero status"
- def __init__(self, args, status, stdout=None, stderr=None):
- self.status = status
- self.stdout = stdout
- self.stderr = stderr
- msg = '{args} exited with {status}'.format(args=args, status=status)
- if stderr:
- msg = '{msg}: {stderr}'.format(msg=msg, stderr=stderr)
- super(SubprocessError, self).__init__(msg)
-
-
-class _SubprocessContextManager(object):
- """
- PEP 343 context manager for subprocesses.
-
- 'expect' holds a tuple of acceptable exit codes, otherwise we'll
- raise a SubprocessError in __exit__.
- """
- def __init__(self, process, args, expect=(0,)):
- self._process = process
- self._args = args
- self._expect = expect
-
- def __enter__(self):
- return self._process
-
- def __exit__(self, type, value, traceback):
- for name in ['stdin', 'stdout', 'stderr']:
- stream = getattr(self._process, name)
- if stream:
- stream.close()
- setattr(self._process, name, None)
- status = self._process.wait()
- _LOG.debug(
- 'collect {args} with status {status} (expected {expect})'.format(
- args=self._args, status=status, expect=self._expect))
- if status not in self._expect:
- raise SubprocessError(args=self._args, status=status)
-
- def wait(self):
- return self._process.wait()
-
-
-def _spawn(args, input=None, additional_env=None, wait=False, stdin=None,
- stdout=None, stderr=None, encoding=_locale.getpreferredencoding(),
- expect=(0,), **kwargs):
- """Spawn a subprocess, and optionally wait for it to finish.
-
- This wrapper around subprocess.Popen has two modes, depending on
- the truthiness of 'wait'. If 'wait' is true, we use p.communicate
- internally to write 'input' to the subprocess's stdin and read
- from it's stdout/stderr. If 'wait' is False, we return a
- _SubprocessContextManager instance for fancier handling
- (e.g. piping between processes).
-
- For 'wait' calls when you want to write to the subprocess's stdin,
- you only need to set 'input' to your content. When 'input' is not
- None but 'stdin' is, we'll automatically set 'stdin' to PIPE
- before calling Popen. This avoids having the subprocess
- accidentally inherit the launching process's stdin.
- """
- _LOG.debug('spawn {args} (additional env. var.: {env})'.format(
- args=args, env=additional_env))
- if not stdin and input is not None:
- stdin = _subprocess.PIPE
- if additional_env:
- if not kwargs.get('env'):
- kwargs['env'] = dict(_os.environ)
- kwargs['env'].update(additional_env)
- p = _subprocess.Popen(
- args, stdin=stdin, stdout=stdout, stderr=stderr, **kwargs)
- if wait:
- if hasattr(input, 'encode'):
- input = input.encode(encoding)
- (stdout, stderr) = p.communicate(input=input)
- status = p.wait()
- _LOG.debug(
- 'collect {args} with status {status} (expected {expect})'.format(
- args=args, status=status, expect=expect))
- if stdout is not None:
- stdout = stdout.decode(encoding)
- if stderr is not None:
- stderr = stderr.decode(encoding)
- if status not in expect:
- raise SubprocessError(
- args=args, status=status, stdout=stdout, stderr=stderr)
- return (status, stdout, stderr)
- if p.stdin and not stdin:
- p.stdin.close()
- p.stdin = None
- if p.stdin:
- p.stdin = _codecs.getwriter(encoding=encoding)(stream=p.stdin)
- stream_reader = _codecs.getreader(encoding=encoding)
- if p.stdout:
- p.stdout = stream_reader(stream=p.stdout)
- if p.stderr:
- p.stderr = stream_reader(stream=p.stderr)
- return _SubprocessContextManager(args=args, process=p, expect=expect)
-
-
-def _git(args, **kwargs):
- args = ['git', '--git-dir', NMBGIT] + list(args)
- return _spawn(args=args, **kwargs)
-
-
-def _get_current_branch():
- """Get the name of the current branch.
-
- Return 'None' if we're not on a branch.
- """
- try:
- (status, branch, stderr) = _git(
- args=['symbolic-ref', '--short', 'HEAD'],
- stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, wait=True)
- except SubprocessError as e:
- if 'not a symbolic ref' in e:
- return None
- raise
- return branch.strip()
-
-
-def _get_remote():
- "Get the default remote for the current branch."
- local_branch = _get_current_branch()
- (status, remote, stderr) = _git(
- args=['config', 'branch.{0}.remote'.format(local_branch)],
- stdout=_subprocess.PIPE, wait=True)
- return remote.strip()
-
-
-def get_tags(prefix=None):
- "Get a list of tags with a given prefix."
- if prefix is None:
- prefix = TAG_PREFIX
- (status, stdout, stderr) = _spawn(
- args=['notmuch', 'search', '--output=tags', '*'],
- stdout=_subprocess.PIPE, wait=True)
- return [tag for tag in stdout.splitlines() if tag.startswith(prefix)]
-
-
-def archive(treeish='HEAD', args=()):
- """
- Dump a tar archive of the current nmbug tag set.
-
- Using 'git archive'.
-
- Each tag $tag for message with Message-Id $id is written to
- an empty file
-
- tags/encode($id)/encode($tag)
-
- The encoding preserves alphanumerics, and the characters
- "+-_@=.:," (not the quotes). All other octets are replaced with
- '%' followed by a two digit hex number.
- """
- _git(args=['archive', treeish] + list(args), wait=True)
-
-
-def clone(repository):
- """
- Create a local nmbug repository from a remote source.
-
- This wraps 'git clone', adding some options to avoid creating a
- working tree while preserving remote-tracking branches and
- upstreams.
- """
- with _tempfile.TemporaryDirectory(prefix='nmbug-clone.') as workdir:
- _spawn(
- args=[
- 'git', 'clone', '--no-checkout', '--separate-git-dir', NMBGIT,
- repository, workdir],
- wait=True)
- _git(args=['config', '--unset', 'core.worktree'], wait=True, expect=(0, 5))
- _git(args=['config', 'core.bare', 'true'], wait=True)
- _git(args=['branch', 'config', 'origin/config'], wait=True)
- existing_tags = get_tags()
- if existing_tags:
- _LOG.warning(
- 'Not checking out to avoid clobbering existing tags: {}'.format(
- ', '.join(existing_tags)))
- else:
- checkout()
-
-
-def _is_committed(status):
- return len(status['added']) + len(status['deleted']) == 0
-
-
-def commit(treeish='HEAD', message=None):
- """
- Commit prefix-matching tags from the notmuch database to Git.
- """
- status = get_status()
-
- if _is_committed(status=status):
- _LOG.warning('Nothing to commit')
- return
-
- _git(args=['read-tree', '--empty'], wait=True)
- _git(args=['read-tree', treeish], wait=True)
- try:
- _update_index(status=status)
- (_, tree, _) = _git(
- args=['write-tree'],
- stdout=_subprocess.PIPE,
- wait=True)
- (_, parent, _) = _git(
- args=['rev-parse', treeish],
- stdout=_subprocess.PIPE,
- wait=True)
- (_, commit, _) = _git(
- args=['commit-tree', tree.strip(), '-p', parent.strip()],
- input=message,
- stdout=_subprocess.PIPE,
- wait=True)
- _git(
- args=['update-ref', treeish, commit.strip()],
- stdout=_subprocess.PIPE,
- wait=True)
- except Exception as e:
- _git(args=['read-tree', '--empty'], wait=True)
- _git(args=['read-tree', treeish], wait=True)
- raise
-
-def _update_index(status):
- with _git(
- args=['update-index', '--index-info'],
- stdin=_subprocess.PIPE) as p:
- for id, tags in status['deleted'].items():
- for line in _index_tags_for_message(id=id, status='D', tags=tags):
- p.stdin.write(line)
- for id, tags in status['added'].items():
- for line in _index_tags_for_message(id=id, status='A', tags=tags):
- p.stdin.write(line)
-
-
-def fetch(remote=None):
- """
- Fetch changes from the remote repository.
-
- See 'merge' to bring those changes into notmuch.
- """
- args = ['fetch']
- if remote:
- args.append(remote)
- _git(args=args, wait=True)
-
-
-def checkout():
- """
- Update the notmuch database from Git.
-
- This is mainly useful to discard your changes in notmuch relative
- to Git.
- """
- status = get_status()
- with _spawn(
- args=['notmuch', 'tag', '--batch'], stdin=_subprocess.PIPE) as p:
- for id, tags in status['added'].items():
- p.stdin.write(_batch_line(action='-', id=id, tags=tags))
- for id, tags in status['deleted'].items():
- p.stdin.write(_batch_line(action='+', id=id, tags=tags))
-
-
-def _batch_line(action, id, tags):
- """
- 'notmuch tag --batch' line for adding/removing tags.
-
- Set 'action' to '-' to remove a tag or '+' to add the tags to a
- given message id.
- """
- tag_string = ' '.join(
- '{action}{prefix}{tag}'.format(
- action=action, prefix=_ENCODED_TAG_PREFIX, tag=_hex_quote(tag))
- for tag in tags)
- line = '{tags} -- id:{id}\n'.format(
- tags=tag_string, id=_xapian_quote(string=id))
- return line
-
-
-def _insist_committed():
- "Die if the the notmuch tags don't match the current HEAD."
- status = get_status()
- if not _is_committed(status=status):
- _LOG.error('\n'.join([
- 'Uncommitted changes to {prefix}* tags in notmuch',
- '',
- "For a summary of changes, run 'nmbug status'",
- "To save your changes, run 'nmbug commit' before merging/pull",
- "To discard your changes, run 'nmbug checkout'",
- ]).format(prefix=TAG_PREFIX))
- _sys.exit(1)
-
-
-def pull(repository=None, refspecs=None):
- """
- Pull (merge) remote repository changes to notmuch.
-
- 'pull' is equivalent to 'fetch' followed by 'merge'. We use the
- Git-configured repository for your current branch
- (branch.<name>.repository, likely 'origin', and
- branch.<name>.merge, likely 'master').
- """
- _insist_committed()
- if refspecs and not repository:
- repository = _get_remote()
- args = ['pull']
- if repository:
- args.append(repository)
- if refspecs:
- args.extend(refspecs)
- with _tempfile.TemporaryDirectory(prefix='nmbug-pull.') as workdir:
- for command in [
- ['reset', '--hard'],
- args]:
- _git(
- args=command,
- additional_env={'GIT_WORK_TREE': workdir},
- wait=True)
- checkout()
-
-
-def merge(reference='@{upstream}'):
- """
- Merge changes from 'reference' into HEAD and load the result into notmuch.
-
- The default reference is '@{upstream}'.
- """
- _insist_committed()
- with _tempfile.TemporaryDirectory(prefix='nmbug-merge.') as workdir:
- for command in [
- ['reset', '--hard'],
- ['merge', reference]]:
- _git(
- args=command,
- additional_env={'GIT_WORK_TREE': workdir},
- wait=True)
- checkout()
-
-
-def log(args=()):
- """
- A simple wrapper for 'git log'.
-
- After running 'nmbug fetch', you can inspect the changes with
- 'nmbug log HEAD..@{upstream}'.
- """
- # we don't want output trapping here, because we want the pager.
- args = ['log', '--name-status', '--no-renames'] + list(args)
- with _git(args=args, expect=(0, 1, -13)) as p:
- p.wait()
-
-
-def push(repository=None, refspecs=None):
- "Push the local nmbug Git state to a remote repository."
- if refspecs and not repository:
- repository = _get_remote()
- args = ['push']
- if repository:
- args.append(repository)
- if refspecs:
- args.extend(refspecs)
- _git(args=args, wait=True)
-
-
-def status():
- """
- Show pending updates in notmuch or git repo.
-
- Prints lines of the form
-
- ng Message-Id tag
-
- where n is a single character representing notmuch database status
-
- * A
-
- Tag is present in notmuch database, but not committed to nmbug
- (equivalently, tag has been deleted in nmbug repo, e.g. by a
- pull, but not restored to notmuch database).
-
- * D
-
- Tag is present in nmbug repo, but not restored to notmuch
- database (equivalently, tag has been deleted in notmuch).
-
- * U
-
- Message is unknown (missing from local notmuch database).
-
- The second character (if present) represents a difference between
- local and upstream branches. Typically 'nmbug fetch' needs to be
- run to update this.
-
- * a
-
- Tag is present in upstream, but not in the local Git branch.
-
- * d
-
- Tag is present in local Git branch, but not upstream.
- """
- status = get_status()
- # 'output' is a nested defaultdict for message status:
- # * The outer dict is keyed by message id.
- # * The inner dict is keyed by tag name.
- # * The inner dict values are status strings (' a', 'Dd', ...).
- output = _collections.defaultdict(
- lambda : _collections.defaultdict(lambda : ' '))
- for id, tags in status['added'].items():
- for tag in tags:
- output[id][tag] = 'A'
- for id, tags in status['deleted'].items():
- for tag in tags:
- output[id][tag] = 'D'
- for id, tags in status['missing'].items():
- for tag in tags:
- output[id][tag] = 'U'
- if _is_unmerged():
- for id, tag in _diff_refs(filter='A'):
- output[id][tag] += 'a'
- for id, tag in _diff_refs(filter='D'):
- output[id][tag] += 'd'
- for id, tag_status in sorted(output.items()):
- for tag, status in sorted(tag_status.items()):
- print('{status}\t{id}\t{tag}'.format(
- status=status, id=id, tag=tag))
-
-
-def _is_unmerged(ref='@{upstream}'):
- try:
- (status, fetch_head, stderr) = _git(
- args=['rev-parse', ref],
- stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, wait=True)
- except SubprocessError as e:
- if 'No upstream configured' in e.stderr:
- return
- raise
- (status, base, stderr) = _git(
- args=['merge-base', 'HEAD', ref],
- stdout=_subprocess.PIPE, wait=True)
- return base != fetch_head
-
-
-def get_status():
- status = {
- 'deleted': {},
- 'missing': {},
- }
- index = _index_tags()
- maybe_deleted = _diff_index(index=index, filter='D')
- for id, tags in maybe_deleted.items():
- (_, stdout, stderr) = _spawn(
- args=['notmuch', 'search', '--output=files', 'id:{0}'.format(id)],
- stdout=_subprocess.PIPE,
- wait=True)
- if stdout:
- status['deleted'][id] = tags
- else:
- status['missing'][id] = tags
- status['added'] = _diff_index(index=index, filter='A')
- _os.remove(index)
- return status
-
-
-def _index_tags():
- "Write notmuch tags to the nmbug.index."
- path = _os.path.join(NMBGIT, 'nmbug.index')
- query = ' '.join('tag:"{tag}"'.format(tag=tag) for tag in get_tags())
- prefix = '+{0}'.format(_ENCODED_TAG_PREFIX)
- _git(
- args=['read-tree', '--empty'],
- additional_env={'GIT_INDEX_FILE': path}, wait=True)
- with _spawn(
- args=['notmuch', 'dump', '--format=batch-tag', '--', query],
- stdout=_subprocess.PIPE) as notmuch:
- with _git(
- args=['update-index', '--index-info'],
- stdin=_subprocess.PIPE,
- additional_env={'GIT_INDEX_FILE': path}) as git:
- for line in notmuch.stdout:
- if line.strip().startswith('#'):
- continue
- (tags_string, id) = [_.strip() for _ in line.split(' -- id:')]
- tags = [
- _unquote(tag[len(prefix):])
- for tag in tags_string.split()
- if tag.startswith(prefix)]
- id = _xapian_unquote(string=id)
- for line in _index_tags_for_message(
- id=id, status='A', tags=tags):
- git.stdin.write(line)
- return path
-
-
-def _index_tags_for_message(id, status, tags):
- """
- Update the Git index to either create or delete an empty file.
-
- Neither 'id' nor the tags in 'tags' should be encoded/escaped.
- """
- mode = '100644'
- hash = _EMPTYBLOB
-
- if status == 'D':
- mode = '0'
- hash = '0000000000000000000000000000000000000000'
-
- for tag in tags:
- path = 'tags/{id}/{tag}'.format(
- id=_hex_quote(string=id), tag=_hex_quote(string=tag))
- yield '{mode} {hash}\t{path}\n'.format(mode=mode, hash=hash, path=path)
-
-
-def _diff_index(index, filter):
- """
- Get an {id: {tag, ...}} dict for a given filter.
-
- For example, use 'A' to find added tags, and 'D' to find deleted tags.
- """
- s = _collections.defaultdict(set)
- with _git(
- args=[
- 'diff-index', '--cached', '--diff-filter', filter,
- '--name-only', 'HEAD'],
- additional_env={'GIT_INDEX_FILE': index},
- stdout=_subprocess.PIPE) as p:
- # Once we drop Python < 3.3, we can use 'yield from' here
- for id, tag in _unpack_diff_lines(stream=p.stdout):
- s[id].add(tag)
- return s
-
-
-def _diff_refs(filter, a='HEAD', b='@{upstream}'):
- with _git(
- args=['diff', '--diff-filter', filter, '--name-only', a, b],
- stdout=_subprocess.PIPE) as p:
- # Once we drop Python < 3.3, we can use 'yield from' here
- for id, tag in _unpack_diff_lines(stream=p.stdout):
- yield id, tag
-
-
-def _unpack_diff_lines(stream):
- "Iterate through (id, tag) tuples in a diff stream."
- for line in stream:
- match = _TAG_FILE_REGEX.match(line.strip())
- if not match:
- message = 'non-tag line in diff: {!r}'.format(line.strip())
- if line.startswith(_TAG_DIRECTORY):
- raise ValueError(message)
- _LOG.info(message)
- continue
- id = _unquote(match.group('id'))
- tag = _unquote(match.group('tag'))
- yield (id, tag)
-
-
-def _help(parser, command=None):
- """
- Show help for an nmbug command.
-
- Because some folks prefer:
-
- $ nmbug help COMMAND
-
- to
-
- $ nmbug COMMAND --help
- """
- if command:
- parser.parse_args([command, '--help'])
- else:
- parser.parse_args(['--help'])
-
-
-if __name__ == '__main__':
- import argparse
-
- parser = argparse.ArgumentParser(
- description=__doc__.strip(),
- formatter_class=argparse.RawDescriptionHelpFormatter)
- parser.add_argument(
- '-v', '--version', action='version',
- version='%(prog)s {}'.format(__version__))
- parser.add_argument(
- '-l', '--log-level',
- choices=['critical', 'error', 'warning', 'info', 'debug'],
- help='Log verbosity. Defaults to {!r}.'.format(
- _logging.getLevelName(_LOG.level).lower()))
-
- help = _functools.partial(_help, parser=parser)
- help.__doc__ = _help.__doc__
- subparsers = parser.add_subparsers(
- title='commands',
- description=(
- 'For help on a particular command, run: '
- "'%(prog)s ... <command> --help'."))
- for command in [
- 'archive',
- 'checkout',
- 'clone',
- 'commit',
- 'fetch',
- 'help',
- 'log',
- 'merge',
- 'pull',
- 'push',
- 'status',
- ]:
- func = locals()[command]
- doc = _textwrap.dedent(func.__doc__).strip().replace('%', '%%')
- subparser = subparsers.add_parser(
- command,
- help=doc.splitlines()[0],
- description=doc,
- formatter_class=argparse.RawDescriptionHelpFormatter)
- subparser.set_defaults(func=func)
- if command == 'archive':
- subparser.add_argument(
- 'treeish', metavar='TREE-ISH', nargs='?', default='HEAD',
- help=(
- 'The tree or commit to produce an archive for. Defaults '
- "to 'HEAD'."))
- subparser.add_argument(
- 'args', metavar='ARG', nargs='*',
- help=(
- "Argument passed through to 'git archive'. Set anything "
- 'before <tree-ish>, see git-archive(1) for details.'))
- elif command == 'clone':
- subparser.add_argument(
- 'repository',
- help=(
- 'The (possibly remote) repository to clone from. See the '
- 'URLS section of git-clone(1) for more information on '
- 'specifying repositories.'))
- elif command == 'commit':
- subparser.add_argument(
- 'message', metavar='MESSAGE', default='', nargs='?',
- help='Text for the commit message.')
- elif command == 'fetch':
- subparser.add_argument(
- 'remote', metavar='REMOTE', nargs='?',
- help=(
- 'Override the default configured in branch.<name>.remote '
- 'to fetch from a particular remote repository (e.g. '
- "'origin')."))
- elif command == 'help':
- subparser.add_argument(
- 'command', metavar='COMMAND', nargs='?',
- help='The command to show help for.')
- elif command == 'log':
- subparser.add_argument(
- 'args', metavar='ARG', nargs='*',
- help="Additional argument passed through to 'git log'.")
- elif command == 'merge':
- subparser.add_argument(
- 'reference', metavar='REFERENCE', default='@{upstream}',
- nargs='?',
- help=(
- 'Reference, usually other branch heads, to merge into '
- "our branch. Defaults to '@{upstream}'."))
- elif command == 'pull':
- subparser.add_argument(
- 'repository', metavar='REPOSITORY', default=None, nargs='?',
- help=(
- 'The "remote" repository that is the source of the pull. '
- 'This parameter can be either a URL (see the section GIT '
- 'URLS in git-pull(1)) or the name of a remote (see the '
- 'section REMOTES in git-pull(1)).'))
- subparser.add_argument(
- 'refspecs', metavar='REFSPEC', default=None, nargs='*',
- help=(
- 'Refspec (usually a branch name) to fetch and merge. See '
- 'the <refspec> entry in the OPTIONS section of '
- 'git-pull(1) for other possibilities.'))
- elif command == 'push':
- subparser.add_argument(
- 'repository', metavar='REPOSITORY', default=None, nargs='?',
- help=(
- 'The "remote" repository that is the destination of the '
- 'push. This parameter can be either a URL (see the '
- 'section GIT URLS in git-push(1)) or the name of a remote '
- '(see the section REMOTES in git-push(1)).'))
- subparser.add_argument(
- 'refspecs', metavar='REFSPEC', default=None, nargs='*',
- help=(
- 'Refspec (usually a branch name) to push. See '
- 'the <refspec> entry in the OPTIONS section of '
- 'git-push(1) for other possibilities.'))
-
- args = parser.parse_args()
-
- if args.log_level:
- level = getattr(_logging, args.log_level.upper())
- _LOG.setLevel(level)
-
- if not getattr(args, 'func', None):
- parser.print_usage()
- _sys.exit(1)
-
- if args.func == help:
- arg_names = ['command']
- else:
- (arg_names, varargs, varkw) = _inspect.getargs(args.func.__code__)
- kwargs = {key: getattr(args, key) for key in arg_names if key in args}
- try:
- args.func(**kwargs)
- except SubprocessError as e:
- if _LOG.level == _logging.DEBUG:
- raise # don't mask the traceback
- _LOG.error(str(e))
- _sys.exit(1)
headers: headers,
crypto: crypto,
+ duplicate: integer,
body?: [part] # omitted if --body=false
}
MAN_RST_FILES := $(MAN1_RST) $(MAN5_RST) $(MAN7_RST)
ALL_RST_FILES := $(MAN_RST_FILES) $(srcdir)/doc/notmuch-emacs.rst
+COPY_ROFF1 := $(patsubst %,$(DOCBUILDDIR)/man/man1/%.1,nmbug notmuch-setup)
MAN1_ROFF := $(patsubst $(srcdir)/doc/%,$(DOCBUILDDIR)/man/%,$(MAN1_RST:.rst=.1))
+MAN1_ROFF := $(MAN1_ROFF) $(COPY_ROFF1)
MAN5_ROFF := $(patsubst $(srcdir)/doc/%,$(DOCBUILDDIR)/man/%,$(MAN5_RST:.rst=.5))
MAN7_ROFF := $(patsubst $(srcdir)/doc/%,$(DOCBUILDDIR)/man/%,$(MAN7_RST:.rst=.7))
MAN_ROFF_FILES := $(MAN1_ROFF) $(MAN5_ROFF) $(MAN7_ROFF)
INFO_TEXI_FILES += $(DOCBUILDDIR)/texinfo/notmuch-emacs.texi
endif
-INFO_INFO_FILES := $(INFO_TEXI_FILES:.texi=.info)
+COPY_INFO1 := $(patsubst $(DOCBUILDDIR)/man/man1/%.1,$(DOCBUILDDIR)/texinfo/%.info,$(COPY_ROFF1))
+INFO_INFO_FILES := $(INFO_TEXI_FILES:.texi=.info) $(COPY_INFO1)
.PHONY: sphinx-html sphinx-texinfo sphinx-info
install -m0644 $(filter %.1.gz,$(MAN_GZIP_FILES)) $(DESTDIR)/$(mandir)/man1
install -m0644 $(filter %.5.gz,$(MAN_GZIP_FILES)) $(DESTDIR)/$(mandir)/man5
install -m0644 $(filter %.7.gz,$(MAN_GZIP_FILES)) $(DESTDIR)/$(mandir)/man7
- cd $(DESTDIR)/$(mandir)/man1 && ln -sf notmuch.1.gz notmuch-setup.1.gz
endif
ifneq ($(HAVE_SPHINX)$(HAVE_MAKEINFO),11)
--- /dev/null
+Notmuch Command Line Interface
+==============================
+
+Main commands
+-------------
+
+.. toctree::
+ :titlesonly:
+
+ man1/notmuch
+ man1/notmuch-address
+ man1/notmuch-compact
+ man1/notmuch-config
+ man1/notmuch-count
+ man1/notmuch-dump
+ man1/notmuch-emacs-mua
+ man1/notmuch-git
+ man1/notmuch-insert
+ man1/notmuch-new
+ man1/notmuch-reindex
+ man1/notmuch-reply
+ man1/notmuch-restore
+ man1/notmuch-search
+ man1/notmuch-show
+ man1/notmuch-tag
+ man5/notmuch-hooks
+
+Aliases
+-------
+
+.. toctree::
+ :titlesonly:
+
+ nmbug <man1/notmuch-git>
+ notmuch-setup <man1/notmuch>
+
import sys
import os
+from pathlib import Path
+sys.path.append(str(Path(__file__).parent))
-extensions = [ 'sphinx.ext.autodoc' ]
+extensions = [ 'sphinx.ext.autodoc', 'elisp' ]
# The suffix of source filenames.
source_suffix = '.rst'
# Hacky reimplementation of include to workaround limitations of
# sphinx-doc
lines = ['.. include:: /../emacs/rstdoc.rsti\n\n'] # in the source tree
- for file in ('notmuch.rsti', 'notmuch-lib.rsti', 'notmuch-show.rsti', 'notmuch-tag.rsti', 'notmuch-tree.rsti'):
+ for file in ('notmuch.rsti', 'notmuch-lib.rsti', 'notmuch-hello.rsti', 'notmuch-show.rsti', 'notmuch-tag.rsti', 'notmuch-tree.rsti'):
lines.extend(open(rsti_dir+'/'+file))
rst_epilog = ''.join(lines)
del lines
# a list of builtin themes.
html_theme = 'default'
+# prevent generation of python module index
+html_domain_indices=[]
# Add any paths that contain custom static files (such as style sheets) here,
# relative to this directory. They are copied after the builtin static files,
u'send mail with notmuch and emacs',
[notmuch_authors], 1),
+ ('man1/notmuch-git', 'notmuch-git',
+ u'manage notmuch tags with git',
+ [notmuch_authors], 1),
+
+ ('man1/notmuch-git', 'nmbug',
+ u'manage notmuch bugs with git',
+ [notmuch_authors], 1),
+
('man5/notmuch-hooks', 'notmuch-hooks',
u'hooks for notmuch',
[notmuch_authors], 5),
u'syntax for notmuch queries',
[notmuch_authors], 7),
+ ('man1/notmuch', 'notmuch-setup',
+ u'getting started with notmuch',
+ [notmuch_authors], 1),
+
('man7/notmuch-sexp-queries', 'notmuch-sexp-queries',
u's-expression syntax for notmuch queries',
[notmuch_authors], 7),
x[2], # description
'Miscellaneous' # category
) for x in man_pages]
+
+def setup(app):
+ import docutils.nodes
+ # define nmconfig role and directive for config items.
+ app.add_object_type('nmconfig','nmconfig',
+ indextemplate='pair: configuration item; %s',
+ ref_nodeclass=docutils.nodes.generated,
+ objname='config item' )
--- /dev/null
+# Copyright (C) 2016 Sebastian Wiesner and Flycheck contributors
+
+# This file is not part of GNU Emacs.
+
+# This program is free software: you can redistribute it and/or modify it under
+# the terms of the GNU General Public License as published by the Free Software
+# Foundation, either version 3 of the License, or (at your option) any later
+# version.
+
+# This program is distributed in the hope that it will be useful, but WITHOUT
+# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
+# FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
+# details.
+
+# You should have received a copy of the GNU General Public License along with
+# this program. If not, see <http://www.gnu.org/licenses/>.
+
+
+from collections import namedtuple
+from sphinx import addnodes
+from sphinx.util import ws_re
+from sphinx.roles import XRefRole
+from sphinx.domains import Domain, ObjType
+from sphinx.util.nodes import make_refnode
+from sphinx.directives import ObjectDescription
+
+
+def make_target(cell, name):
+ """Create a target name from ``cell`` and ``name``.
+
+ ``cell`` is the name of a symbol cell, and ``name`` is a symbol name, both
+ as strings.
+
+ The target names are used as cross-reference targets for Sphinx.
+
+ """
+ return '{cell}-{name}'.format(cell=cell, name=name)
+
+
+def to_mode_name(symbol_name):
+ """Convert ``symbol_name`` to a mode name.
+
+ Split at ``-`` and titlecase each part.
+
+ """
+ return ' '.join(p.title() for p in symbol_name.split('-'))
+
+
+class Cell(namedtuple('Cell', 'objtype docname')):
+ """A cell in a symbol.
+
+ A cell holds the object type and the document name of the description for
+ the cell.
+
+ Cell objects are used within symbol entries in the domain data.
+
+ """
+
+ pass
+
+
+class KeySequence(namedtuple('KeySequence', 'keys')):
+ """A key sequence."""
+
+ PREFIX_KEYS = {'C-u'}
+ PREFIX_KEYS.update('M-{}'.format(n) for n in range(10))
+
+ @classmethod
+ def fromstring(cls, s):
+ return cls(s.split())
+
+ @property
+ def command_name(self):
+ """The command name in this key sequence.
+
+ Return ``None`` for key sequences that are no command invocations with
+ ``M-x``.
+
+ """
+ try:
+ return self.keys[self.keys.index('M-x') + 1]
+ except ValueError:
+ return None
+
+ @property
+ def has_prefix(self):
+ """Whether this key sequence has a prefix."""
+ return self.keys[0] in self.PREFIX_KEYS
+
+ def __str__(self):
+ return ' '.join(self.keys)
+
+
+class EmacsLispSymbol(ObjectDescription):
+ """An abstract base class for directives documenting symbols.
+
+ Provide target and index generation and registration of documented symbols
+ within the domain data.
+
+ Deriving classes must have a ``cell`` attribute which refers to the cell
+ the documentation goes in, and a ``label`` attribute which provides a
+ human-readable name for what is documented, used in the index entry.
+
+ """
+
+ cell_for_objtype = {
+ 'defcustom': 'variable',
+ 'defconst': 'variable',
+ 'defvar': 'variable',
+ 'defface': 'face'
+ }
+
+ category_for_objtype = {
+ 'defcustom': 'Emacs variable (customizable)',
+ 'defconst': 'Emacs constant',
+ 'defvar': 'Emacs variable',
+ 'defface': 'Emacs face'
+ }
+
+ @property
+ def cell(self):
+ """The cell in which to store symbol metadata."""
+ return self.cell_for_objtype[self.objtype]
+
+ @property
+ def label(self):
+ """The label for the documented object type."""
+ return self.objtype
+
+ @property
+ def category(self):
+ """Index category"""
+ return self.category_for_objtype[self.objtype]
+
+ def handle_signature(self, signature, signode):
+ """Create nodes in ``signode`` for the ``signature``.
+
+ ``signode`` is a docutils node to which to add the nodes, and
+ ``signature`` is the symbol name.
+
+ Add the object type label before the symbol name and return
+ ``signature``.
+
+ """
+ label = self.label + ' '
+ signode += addnodes.desc_annotation(label, label)
+ signode += addnodes.desc_name(signature, signature)
+ return signature
+
+ def _add_index(self, name, target):
+ index_text = '{name}; {label}'.format(
+ name=name, label=self.category)
+ self.indexnode['entries'].append(
+ ('pair', index_text, target, '', None))
+
+ def _add_target(self, name, sig, signode):
+ target = make_target(self.cell, name)
+ if target not in self.state.document.ids:
+ signode['names'].append(name)
+ signode['ids'].append(target)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
+
+ obarray = self.env.domaindata['el']['obarray']
+ symbol = obarray.setdefault(name, {})
+ if self.cell in symbol:
+ self.state_machine.reporter.warning(
+ 'duplicate description of %s %s, ' % (self.objtype, name)
+ + 'other instance in '
+ + self.env.doc2path(symbol[self.cell].docname),
+ line=self.lineno)
+ symbol[self.cell] = Cell(self.objtype, self.env.docname)
+
+ return target
+
+ def add_target_and_index(self, name, sig, signode):
+ target = self._add_target(name, sig, signode)
+ self._add_index(name, target)
+
+
+class EmacsLispMinorMode(EmacsLispSymbol):
+ cell = 'function'
+ label = 'Minor Mode'
+
+ def handle_signature(self, signature, signode):
+ """Create nodes in ``signode`` for the ``signature``.
+
+ ``signode`` is a docutils node to which to add the nodes, and
+ ``signature`` is the symbol name.
+
+ Add the object type label before the symbol name and return
+ ``signature``.
+
+ """
+ label = self.label + ' '
+ signode += addnodes.desc_annotation(label, label)
+ signode += addnodes.desc_name(signature, to_mode_name(signature))
+ return signature
+
+ def _add_index(self, name, target):
+ return super()._add_index(to_mode_name(name), target)
+
+
+class EmacsLispFunction(EmacsLispSymbol):
+ """A directive to document Emacs Lisp functions."""
+
+ cell_for_objtype = {
+ 'defun': 'function',
+ 'defmacro': 'function'
+ }
+
+ def handle_signature(self, signature, signode):
+ function_name, *args = ws_re.split(signature)
+ label = self.label + ' '
+ signode += addnodes.desc_annotation(label, label)
+ signode += addnodes.desc_name(function_name, function_name)
+ for arg in args:
+ is_keyword = arg.startswith('&')
+ node = (addnodes.desc_annotation
+ if is_keyword
+ else addnodes.desc_addname)
+ signode += node(' ' + arg, ' ' + arg)
+
+ return function_name
+
+
+class EmacsLispKey(ObjectDescription):
+ """A directive to document interactive commands via their bindings."""
+
+ label = 'Emacs command'
+
+ def handle_signature(self, signature, signode):
+ """Create nodes to ``signode`` for ``signature``.
+
+ ``signode`` is a docutils node to which to add the nodes, and
+ ``signature`` is the symbol name.
+ """
+ key_sequence = KeySequence.fromstring(signature)
+ signode += addnodes.desc_name(signature, str(key_sequence))
+ return str(key_sequence)
+
+ def _add_command_target_and_index(self, name, sig, signode):
+ target_name = make_target('function', name)
+ if target_name not in self.state.document.ids:
+ signode['names'].append(name)
+ signode['ids'].append(target_name)
+ self.state.document.note_explicit_target(signode)
+
+ obarray = self.env.domaindata['el']['obarray']
+ symbol = obarray.setdefault(name, {})
+ if 'function' in symbol:
+ self.state_machine.reporter.warning(
+ 'duplicate description of %s %s, ' % (self.objtype, name)
+ + 'other instance in '
+ + self.env.doc2path(symbol['function'].docname),
+ line=self.lineno)
+ symbol['function'] = Cell(self.objtype, self.env.docname)
+
+ index_text = '{name}; {label}'.format(name=name, label=self.label)
+ self.indexnode['entries'].append(
+ ('pair', index_text, target_name, '', None))
+
+ def _add_binding_target_and_index(self, binding, sig, signode):
+ reftarget = make_target('key', binding)
+
+ if reftarget not in self.state.document.ids:
+ signode['names'].append(reftarget)
+ signode['ids'].append(reftarget)
+ signode['first'] = (not self.names)
+ self.state.document.note_explicit_target(signode)
+
+ keymap = self.env.domaindata['el']['keymap']
+ if binding in keymap:
+ self.state_machine.reporter.warning(
+ 'duplicate description of binding %s, ' % binding
+ + 'other instance in '
+ + self.env.doc2path(keymap[binding]),
+ line=self.lineno)
+ keymap[binding] = self.env.docname
+
+ index_text = '{name}; Emacs key binding'.format(name=binding)
+ self.indexnode['entries'].append(
+ ('pair', index_text, reftarget, '', None))
+
+ def add_target_and_index(self, name, sig, signode):
+ # If unprefixed M-x command index as function and not as key binding
+ sequence = KeySequence.fromstring(name)
+ if sequence.command_name and not sequence.has_prefix:
+ self._add_command_target_and_index(sequence.command_name,
+ sig, signode)
+ else:
+ self._add_binding_target_and_index(name, sig, signode)
+
+
+class XRefModeRole(XRefRole):
+ """A role to cross-reference a minor mode.
+
+ Like a normal cross-reference role but appends ``-mode`` to the reference
+ target and title-cases the symbol name like Emacs does when referring to
+ modes.
+
+ """
+
+ fix_parens = False
+ lowercase = False
+
+ def process_link(self, env, refnode, has_explicit_title, title, target):
+ refnode['reftype'] = 'minor-mode'
+ target = target + '-mode'
+ return (title if has_explicit_title else to_mode_name(target), target)
+
+
+class EmacsLispDomain(Domain):
+ """A domain to document Emacs Lisp code."""
+
+ name = 'el'
+ label = ''
+
+ object_types = {
+ # TODO: Set search prio for object types
+ # Types for user-facing options and commands
+ 'minor-mode': ObjType('minor-mode', 'function', 'mode',
+ cell='function'),
+ 'define-key': ObjType('key binding', cell='interactive'),
+ 'defcustom': ObjType('defcustom', 'defcustom', cell='variable'),
+ 'defface': ObjType('defface', 'defface', cell='face'),
+ # Object types for code
+ 'defun': ObjType('defun', 'defun', cell='function'),
+ 'defmacro': ObjType('defmacro', 'defmacro', cell='function'),
+ 'defvar': ObjType('defvar', 'defvar', cell='variable'),
+ 'defconst': ObjType('defconst', 'defconst', cell='variable')
+ }
+ directives = {
+ 'minor-mode': EmacsLispMinorMode,
+ 'define-key': EmacsLispKey,
+ 'defcustom': EmacsLispSymbol,
+ 'defvar': EmacsLispSymbol,
+ 'defconst': EmacsLispSymbol,
+ 'defface': EmacsLispSymbol,
+ 'defun': EmacsLispFunction,
+ 'defmacro': EmacsLispFunction
+ }
+ roles = {
+ 'mode': XRefModeRole(),
+ 'defvar': XRefRole(),
+ 'defconst': XRefRole(),
+ 'defcustom': XRefRole(),
+ 'defface': XRefRole(),
+ 'defun': XRefRole(),
+ 'defmacro': XRefRole()
+ }
+
+ data_version = 1
+ initial_data = {
+ # Our domain data attempts to somewhat mirror the semantics of Emacs
+ # Lisp, so we have an obarray which holds symbols which in turn have
+ # function, variable, face, etc. cells, and a keymap which holds the
+ # documentation for key bindings.
+ 'obarray': {},
+ 'keymap': {}
+ }
+
+ def clear_doc(self, docname):
+ """Clear all cells documented ``docname``."""
+ for symbol in self.data['obarray'].values():
+ for cell in list(symbol.keys()):
+ if docname == symbol[cell].docname:
+ del symbol[cell]
+ for binding in list(self.data['keymap']):
+ if self.data['keymap'][binding] == docname:
+ del self.data['keymap'][binding]
+
+ def resolve_xref(self, env, fromdocname, builder,
+ objtype, target, node, contnode):
+ """Resolve a cross reference to ``target``."""
+ if objtype == 'key':
+ todocname = self.data['keymap'].get(target)
+ if not todocname:
+ return None
+ reftarget = make_target('key', target)
+ else:
+ cell = self.object_types[objtype].attrs['cell']
+ symbol = self.data['obarray'].get(target, {})
+ if cell not in symbol:
+ return None
+ reftarget = make_target(cell, target)
+ todocname = symbol[cell].docname
+
+ return make_refnode(builder, fromdocname, todocname,
+ reftarget, contnode, target)
+
+ def resolve_any_xref(self, env, fromdocname, builder,
+ target, node, contnode):
+ """Return all possible cross references for ``target``."""
+ nodes = ((objtype, self.resolve_xref(env, fromdocname, builder,
+ objtype, target, node, contnode))
+ for objtype in ['key', 'defun', 'defvar', 'defface'])
+ return [('el:{}'.format(objtype), node) for (objtype, node) in nodes
+ if node is not None]
+
+ def merge_warn_duplicate(self, objname, our_docname, their_docname):
+ self.env.warn(
+ their_docname,
+ "Duplicate declaration: '{}' also defined in '{}'.\n".format(
+ objname, their_docname))
+
+ def merge_keymapdata(self, docnames, our_keymap, their_keymap):
+ for key, docname in their_keymap.items():
+ if docname in docnames:
+ if key in our_keymap:
+ our_docname = our_keymap[key]
+ self.merge_warn_duplicate(key, our_docname, docname)
+ else:
+ our_keymap[key] = docname
+
+ def merge_obarraydata(self, docnames, our_obarray, their_obarray):
+ for objname, their_cells in their_obarray.items():
+ our_cells = our_obarray.setdefault(objname, dict())
+ for cellname, their_cell in their_cells.items():
+ if their_cell.docname in docnames:
+ our_cell = our_cells.get(cellname)
+ if our_cell:
+ self.merge_warn_duplicate(objname, our_cell.docname,
+ their_cell.docname)
+ else:
+ our_cells[cellname] = their_cell
+
+ def merge_domaindata(self, docnames, otherdata):
+ self.merge_keymapdata(docnames, self.data['keymap'],
+ otherdata['keymap'])
+ self.merge_obarraydata(docnames, self.data['obarray'],
+ otherdata['obarray'])
+
+ def get_objects(self):
+ """Get all documented symbols for use in the search index."""
+ for name, symbol in self.data['obarray'].items():
+ for cellname, cell in symbol.items():
+ yield (name, name, cell.objtype, cell.docname,
+ make_target(cellname, name),
+ self.object_types[cell.objtype].attrs['searchprio'])
+
+
+def setup(app):
+ app.add_domain(EmacsLispDomain)
+ return {'version': '0.1', 'parallel_read_safe': True}
Welcome to notmuch's documentation!
===================================
-Contents:
+Content
+-------
.. toctree::
:titlesonly:
- man1/notmuch
- man1/notmuch-address
- man1/notmuch-compact
- man1/notmuch-config
- man1/notmuch-count
- man1/notmuch-dump
+ command-line
+ queries
notmuch-emacs
- man1/notmuch-emacs-mua
- man5/notmuch-hooks
- man1/notmuch-insert
- man1/notmuch-new
- man7/notmuch-properties
- man1/notmuch-reindex
- man1/notmuch-reply
- man1/notmuch-restore
- man1/notmuch-search
- man7/notmuch-search-terms
- man7/notmuch-sexp-queries
- man1/notmuch-show
- man1/notmuch-tag
python-bindings
-
-Indices and tables
-==================
+
+Index and search
+-----------------
* :ref:`genindex`
-* :ref:`modindex`
* :ref:`search`
paths are presumed relative to `$HOME` for items in section
**database**.
-built_with.<name>
+.. nmconfig:: built_with.<name>
+
Compile time feature <name>. Current possibilities include
"retry_lock" (configure option, included by default).
(since notmuch 0.30, "compact" and "field_processor" are
always included.)
-database.autocommit
+.. nmconfig:: database.autocommit
How often to commit transactions to disk. `0` means wait until
command completes, otherwise an integer `n` specifies to commit to
History: this configuration value was introduced in notmuch 0.33.
-database.backup_dir
+.. nmconfig:: database.backup_dir
+
Directory to store tag dumps when upgrading database.
History: this configuration value was introduced in notmuch 0.32.
Default: A sibling directory of the Xapian database called
`backups`.
-database.hook_dir
+.. nmconfig:: database.hook_dir
+
Directory containing hooks run by notmuch commands. See
:any:`notmuch-hooks(5)`.
Default: See HOOKS, below.
-.. _database.mail_root:
+.. nmconfig:: database.mail_root
-database.mail_root
The top-level directory where your mail currently exists and to
where mail will be delivered in the future. Files should be
individual email messages.
History: this configuration value was introduced in notmuch 0.32.
Default: For compatibility with older configurations, the value of
- database.path is used if **database.mail\_root** is unset.
+ database.path is used if :nmconfig:`database.mail_root` is unset.
+
+.. nmconfig:: database.path
-database.path
Notmuch will store its database here, (in
- sub-directory named ``.notmuch`` if **database.mail\_root**
+ sub-directory named ``.notmuch`` if :nmconfig:`database.mail_root`
is unset).
Default: see :ref:`database`
-.. _index.decrypt:
+.. nmconfig:: git.path
+
+ Default location for git repository for :any:`notmuch-git`.
+
+.. nmconfig:: git.safe_fraction
+
+ Some :any:`notmuch-git` operations check that the fraction of
+ messages changed (in the database or in git, as appropriate) is not
+ too large. This item controls what fraction of total messages is
+ considered "not too large".
+
+.. nmconfig:: git.tag_prefix
+
+ Default tag prefix (filter) for :any:`notmuch-git`.
+
+.. nmconfig:: index.decrypt
-index.decrypt
Policy for decrypting encrypted messages during indexing. Must be
one of: ``false``, ``auto``, ``nostash``, or ``true``.
.. _index.header:
-index.header.<prefix>
+.. nmconfig:: index.header.<prefix>
+
Define the query prefix <prefix>, based on a mail header. For
example ``index.header.List=List-Id`` will add a probabilistic
prefix ``List:`` that searches the ``List-Id`` field. User
supported. See :any:`notmuch-search-terms(7)` for a list of existing
prefixes, and an explanation of probabilistic prefixes.
-.. _maildir.synchronize_flags:
+.. nmconfig:: maildir.synchronize_flags
-maildir.synchronize\_flags
If true, then the following maildir flags (in message filenames)
will be synchronized with the corresponding notmuch tags:
Default: ``true``.
-.. _new.ignore:
+.. nmconfig:: new.ignore
-new.ignore
A list to specify files and directories that will not be searched
for messages by :any:`notmuch-new(1)`. Each entry in the list is either:
Default: empty list.
-.. _new.tags:
+.. nmconfig:: new.tags
-new.tags
A list of tags that will be added to all messages incorporated by
**notmuch new**.
Default: ``unread;inbox``.
-query.<name>
+.. nmconfig:: query.<name>
+
Expansion for named query called <name>. See
:any:`notmuch-search-terms(7)` for more information about named
queries.
-search.exclude\_tags
+.. nmconfig:: search.exclude_tags
+
A list of tags that will be excluded from search results by
default. Using an excluded tag in a query will override that
exclusion.
Default: empty list. Note that :any:`notmuch-setup(1)` puts
``deleted;spam`` here when creating new configuration file.
-.. _show.extra_headers:
-
-show.extra\_headers
+.. nmconfig:: show.extra_headers
By default :any:`notmuch-show(1)` includes the following headers
in structured output if they are present in the message:
Default: empty list.
-squery.<name>
+.. nmconfig:: 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.
-user.name
+.. nmconfig:: user.name
+
Your full name.
Default: ``$NAME`` variable if set, otherwise read from
``/etc/passwd``.
-user.other\_email
+.. nmconfig:: user.other_email
+
A list of other email addresses at which you receive email
- (see also, :ref:`user.primary_email <user.primary_email>`).
+ (see also, :nmconfig:`user.primary_email`)
Default: not set.
-.. _user.primary_email:
+.. nmconfig:: user.primary_email
-user.primary\_email
Your primary email address.
Default: ``$EMAIL`` variable if set, otherwise constructed from
--- /dev/null
+.. _notmuch-git(1):
+
+===========
+notmuch-git
+===========
+
+SYNOPSIS
+========
+
+**notmuch** **git** [-h] [-N] [-C *repo*] [-p *prefix*] [-v] [-l *log level*] *subcommand*
+
+**nmbug** [-h] [-C *repo*] [-p *prefix*] [-v] [-l *log level*] *subcommand*
+
+DESCRIPTION
+===========
+
+Manage notmuch tags with Git.
+
+OPTIONS
+-------
+
+Supported options for `notmuch git` include
+
+.. program:: notmuch-git
+
+.. option:: -h, --help
+
+ show help message and exit
+
+.. option:: -N, --nmbug
+
+ Set defaults for :option:`--tag-prefix` and :option:`--git-dir` suitable for the
+ :any:`notmuch` bug tracker
+
+.. option:: -C <repo>, --git-dir <repo>
+
+ Operate on git repository *repo*. See :ref:`repo_location` for
+ defaults.
+
+.. option:: -p <prefix>, --tag-prefix <prefix>
+
+ Operate only on tags with prefix *prefix*. See :ref:`prefix_val` for
+ defaults.
+
+.. option:: -v, --version
+
+ show notmuch-git's version number and exit
+
+.. option:: -l <level>, --log-level <level>
+
+ Log verbosity, one of: `critical`, `error`, `warning`, `info`,
+ `debug`. Defaults to `warning`.
+
+SUBCOMMANDS
+-----------
+
+For help on a particular subcommand, run: 'notmuch-git ... <command> --help'.
+
+.. program:: notmuch-git
+
+.. option:: archive [tree-ish] [arg ...]
+
+Dump a tar archive of a committed tag set using 'git archive'. See
+:any:`format` for details of the archive contents.
+
+ .. describe:: tree-ish
+
+ The tree or commit to produce an archive for. Defaults to 'HEAD'.
+
+ .. describe:: arg
+
+ If present, any optional arguments are passed through to
+ :manpage:`git-archive(1)`. Arguments to `git-archive` are reordered
+ so that *tree-ish* comes last.
+
+.. option:: checkout [-f|--force]
+
+Update the notmuch database from Git.
+
+This is mainly useful to discard your changes in notmuch relative
+to Git.
+
+ .. describe:: [-f|--force]
+
+ Override checks that prevent modifying tags for large fractions of
+ messages in the database. See also :nmconfig:`git.safe_fraction`.
+
+.. option:: clone <repository>
+
+Create a local `notmuch git` repository from a remote source.
+
+This wraps 'git clone', adding some options to avoid creating a
+working tree while preserving remote-tracking branches and
+upstreams.
+
+ .. describe:: repository
+
+ The (possibly remote) repository to clone from. See the URLS
+ section of :manpage:`git-clone(1)` for more information on
+ specifying repositories.
+
+.. option:: commit [-f|--force] [message]
+
+Commit prefix-matching tags from the notmuch database to Git.
+
+ .. describe:: message
+
+ Optional text for the commit message.
+
+ .. describe:: -f|--force
+
+ Override checks that prevent modifying tags for large fractions of
+ messages in the database. See also :nmconfig:`git.safe_fraction`.
+
+.. option:: fetch [remote]
+
+Fetch changes from the remote repository.
+
+ .. describe:: remote
+
+ Override the default configured in `branch.<name>.remote` to fetch
+ from a particular remote repository (e.g. `origin`).
+
+.. option:: help
+
+Show brief help for an `notmuch git` command.
+
+.. option:: init [--format-version=N]
+
+Create an empty `notmuch git` repository.
+
+This wraps 'git init' with a few extra steps to support subsequent
+status and commit commands.
+
+ .. describe:: --format-version=N
+
+ Create a repo in format version N. By default :any:`notmuch-git`
+ uses the highest supported version, which is the best choice for
+ most use-cases.
+
+.. option:: log [arg ...]
+
+A wrapper for 'git log'.
+
+ .. describe:: arg
+
+ Additional arguments are passed through to 'git log'.
+
+After running `notmuch git fetch`, you can inspect the changes with
+
+::
+
+ $ notmuch git log HEAD..@{upstream}
+
+.. option:: merge [reference]
+
+Merge changes from 'reference' into HEAD and load the result into notmuch.
+
+ .. describe:: reference
+
+ Reference, usually other branch heads, to merge into our
+ branch. Defaults to `@{upstream}`.
+
+.. option:: pull [repository] [refspec ...]
+
+Pull (merge) remote repository changes to notmuch.
+
+**pull** is equivalent to **fetch** followed by **merge**. We use the
+Git-configured repository for your current branch
+(`branch.<name>.repository`, likely `origin`, and `branch.<name>.merge`,
+likely `master` or `main`).
+
+ .. describe:: repository
+
+ The "remote" repository that is the source of the pull. This parameter
+ can be either a URL (see the section GIT URLS in :manpage:`git-pull(1)`) or the
+ name of a remote (see the section REMOTES in :manpage:`git-pull(1)`).
+
+ .. describe:: refspec
+
+ Refspec (usually a branch name) to fetch and merge. See the
+ *refspec* entry in the OPTIONS section of :manpage:`git-pull(1`) for
+ other possibilities.
+
+.. option:: push [repository] [refspec]
+
+Push the local `notmuch git` Git state to a remote repository.
+
+ .. describe:: repository
+
+ The "remote" repository that is the destination of the push. This
+ parameter can be either a URL (see the section GIT URLS in
+ :manpage:`git-push(1)`) or the name of a remote (see the section
+ REMOTES in :manpage:`git-push(1)`).
+
+ .. describe:: refspec
+
+ Refspec (usually a branch name) to push. See the *refspec* entry in the OPTIONS section of
+ :manpage:`git-push(1)` for other possibilities.
+
+.. option:: status
+
+Show pending updates in notmuch or git repo.
+
+Prints lines of the form
+
+| ng Message-Id tag
+
+where n is a single character representing notmuch database status
+
+ .. describe:: A
+
+ Tag is present in notmuch database, but not committed to nmbug
+ (equivalently, tag has been deleted in nmbug repo, e.g. by a
+ pull, but not restored to notmuch database).
+
+ .. describe:: D
+
+ Tag is present in nmbug repo, but not restored to notmuch
+ database (equivalently, tag has been deleted in notmuch).
+
+ .. describe:: U
+
+ Message is unknown (missing from local notmuch database).
+
+The second character *g* (if present) represents a difference between
+local and upstream branches. Typically `notmuch git fetch` needs to be
+run to update this.
+
+ .. describe:: a
+
+ Tag is present in upstream, but not in the local Git branch.
+
+ .. describe:: d
+
+ Tag is present in local Git branch, but not upstream.
+
+.. _format:
+
+REPOSITORY CONTENTS
+===================
+
+The tags are stored in the git repo (and exported) as a set of empty
+files. These empty files are contained within a directory named after
+the message-id.
+
+In what follows `encode()` represents a POSIX filesystem safe
+encoding. The encoding preserves alphanumerics, and the characters
+`+-_@=.,:`. All other octets are replaced with `%` followed by a two
+digit hex number.
+
+Currently :any:`notmuch-git` can read any format version, but can only
+create (via :any:`init`) :ref:`version 1 <format_version_1>` repositories.
+
+.. _format_version_0:
+
+Version 0
+---------
+
+This is the legacy format created by the `nmbug` tool prior to release
+0.37. For a message with Message-Id *id*, for each tag *tag*, there
+is an empty file with path
+
+ tags/ `encode` (*id*) / `encode` (*tag*)
+
+.. _format_version_1:
+
+Version 1
+---------
+
+In format version 1 and later, the format version is contained in a
+top level file called FORMAT.
+
+For a message with Message-Id *id*, for each tag *tag*, there
+is an empty file with path
+
+ tags/ `hash1` (*id*) / `hash2` (*id*) `encode` (*id*) / `encode` (*tag*)
+
+The hash functions each represent one byte of the `blake2b` hex
+digest.
+
+Compared to :ref:`version 0 <format_version_0>`, this reduces the
+number of subdirectories within each directory.
+
+.. _repo_location:
+
+REPOSITORY LOCATION
+===================
+
+:any:`notmuch-git` uses the first of the following with a non-empty
+value to locate the git repository.
+
+- Option :option:`--git-dir`.
+
+- Environment variable :envvar:`NOTMUCH_GIT_DIR`.
+
+- Configuration item :nmconfig:`git.path`
+
+- If invoked as `nmbug` or with the :option:`--nmbug` option,
+ :code:`$HOME/.nmbug`; otherwise
+ :code:`$XDG_DATA_HOME/notmuch/$NOTMUCH_PROFILE/git`.
+
+.. _prefix_val:
+
+PREFIX VALUE
+============
+
+:any:`notmuch-git` uses the first of the following with a non-null
+value to define the tag prefix.
+
+- Option :option:`--tag-prefix`.
+
+- Environment variable :envvar:`NOTMUCH_GIT_PREFIX`.
+
+- Configuration item :nmconfig:`git.tag_prefix`.
+
+- If invoked as `nmbug` or with the :option:`--nmbug` option,
+ :code:`notmuch::`, otherwise the empty string.
+
+ENVIRONMENT
+===========
+
+Variable :envvar:`NOTMUCH_PROFILE` influences :ref:`repo_location`.
+If it is unset, 'default' is assumed.
+
+.. envvar:: NOTMUCH_GIT_DIR
+
+ Default location of git repository. Overriden by :option:`--git-dir`.
+
+.. envvar:: NOTMUCH_GIT_PREFIX
+
+ Default tag prefix (filter). Overriden by :option:`--tag-prefix`.
+
+SEE ALSO
+========
+
+:any:`notmuch(1)`,
+:any:`notmuch-dump(1)`,
+:any:`notmuch-restore(1)`,
+:any:`notmuch-tag(1)`
**notmuch insert** reads a message from standard input and delivers it
into the maildir directory given by configuration option
-:ref:`database.mail_root <database.mail_root>`, then incorporates the message into the notmuch
+:nmconfig:`database.mail_root`, then incorporates the message into the notmuch
database. It is an alternative to using a separate tool to deliver the
message then running :any:`notmuch-new(1)` afterwards.
The new message will be tagged with the tags specified by the
-:ref:`new.tags <new.tags>` configuration option, then by operations specified on the
+:nmconfig:`new.tags` configuration option, then by operations specified on the
command-line: tags prefixed by '+' are added while those prefixed by '-'
are removed.
.. option:: --folder=<folder>
Deliver the message to the specified folder, relative to the
- top-level directory given by the value of **database.mail_root**. The
+ top-level directory given by the value of :nmconfig:`database.mail_root`. The
default is the empty string, which means delivering to the
top-level directory.
``--decrypt=nostash`` without considering the security of your
index.
- See also :ref:`index.decrypt <index.decrypt>` in :any:`notmuch-config(1)`.
+ See also :nmconfig:`index.decrypt` in :any:`notmuch-config(1)`.
CONFIGURATION
=============
Indexing is influenced by the configuration options
-:ref:`index.decrypt <index.decrypt>` and :ref:`index.header
-<index.header>`. Tagging
-is controlled by :ref:`new.tags <new.tags>` and
-:ref:`maildir.synchronize_flags <maildir.synchronize_flags>`. See
+:nmconfig:`index.decrypt` and :nmconfig:`index.header.\<prefix\>`. Tagging
+is controlled by options :nmconfig:`new.tags` and
+:nmconfig:`maildir.synchronize_flags`. See
:any:`notmuch-config(1)` for details.
EXIT STATUS
=============
Indexing is influenced by the configuration options
-:ref:`index.decrypt <index.decrypt>`, :ref:`index.header
-<index.header>`, and :ref:`new.ignore <new.ignore>`. Tagging
-is controlled by :ref:`new.tags <new.tags>` and
-:ref:`maildir.synchronize_flags <maildir.synchronize_flags>`. See
+:nmconfig:`index.decrypt`, :nmconfig:`index.header.\<prefix\>`
+and :nmconfig:`new.ignore`. Tagging
+is controlled by :nmconfig:`new.tags` and
+:nmconfig:`maildir.synchronize_flags`. See
:any:`notmuch-config(1)` for details.
EXIT STATUS
.. program:: reply
+.. option:: --duplicate=N
+
+ Reply to duplicate number N. The numbering starts from 1, and
+ matches the order used by :option:`show --duplicate` and
+ :option:`search --output=files <search --output>`.
+
.. option:: --format=(default|json|sexp|headers-only)
default
.. program:: show
+.. option:: --duplicate=N
+
+ Output duplicate number N. The numbering starts from 1, and matches
+ the order used by :option:`search --duplicate` and
+ :option:`search --output=files <search --output>`
+
.. option:: --entire-thread=(true|false)
If true, **notmuch show** outputs all messages in the thread of
=============
Structured output (json / sexp) is influenced by the configuration
-option :ref:`show.extra_headers <show.extra_headers>`. See
+option :nmconfig:`show.extra_headers`. See
:any:`notmuch-config(1)` for details.
EXIT STATUS
The :any:`notmuch-config(1)` command can be used to get or set
settings in the notmuch configuration file.
-CUSTOM COMMANDS
----------------
+EXTERNAL COMMANDS
+-----------------
If the given command is not known to notmuch, notmuch tries to execute
the external **notmuch-<subcommand>** in :envvar:`PATH` instead. This
allows users to have their own notmuch related tools to be run via the
notmuch command. By design, this does not allow notmuch's own commands
-to be overridden using external commands.
+to be overridden using external commands. The environment variable
+:envvar:`NOTMUCH_CONFIG` will be set according to :option:`--config`,
+if the latter is present.
OPTION SYNTAX
-------------
notmuch --config:alt-config config get user.name
notmuch --config alt-config config get user.name
+.. _duplicate-files:
+
+DUPLICATE MESSAGE FILES
+=======================
+
+Notmuch considers the :mailheader:`Message-ID` to be the primary
+identifier of message. Per :rfc:`5322` the :mailheader:`Message-ID` is
+supposed to be globally unique, but this fails in two distinct
+ways. When you receive copies of a message via a mechanism like
+:mailheader:`Cc` or via a mailing list, the copies are typically
+interchangeable. In the case of some broken mail sending software, the
+same :mailheader:`Message-ID` is used for completely unrelated
+messages. The options :option:`search --duplicate` and :option:`show
+--duplicate` options provide the user with control over which message
+file is displayed. Front ends will need to provide their own
+interface, see e.g. the Emacs front-end :any:`emacs-show-duplicates`.
+
ENVIRONMENT
===========
Term or phrase fields can contain arbitrarily complex queries made up
from terms, operators, and modifiers, but not other fields.
+Range fields take one or two arguments specifying lower and upper
+bounds. One argument is interpreted as identical upper and lower
+bounds. Either upper or lower bound may be specified as ``""`` or
+``*`` to specify the lowest possible lower bound or highest possible
+upper bound.
+
.. _field-table:
.. table:: Fields with supported modifiers
``(not Bob Marley)``
- Match messages containing neither "Bob" nor "Marley", nor their stems,
+ Match messages containing neither "Bob" nor "Marley", nor their stems.
``"quick fox"`` ``quick-fox`` ``quick@fox``
``(folder (of (id 1234@invalid)))``
- Match any message in the same folder as the one with Message-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"
+ 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.
+``(and (date 2009-11-18 2009-11-18) (tag unread))``
+
+ Match messages in the given date range with tag unread.
+
+``(and (date 2009-11-18 *) (tag unread))``
+
+ Match messages from 2009-11-18 or later with tag unread.
+
+``(and (date * 2009-11-18) (tag unread))``
+
+ Match messages from 2009-11-18 or earlier with tag unread.
+
``(starts-with prelim)``
Match any words starting with "prelim".
``(thread (of (id 1234@invalid)))``
- Match any message in the same thread as the one with Message-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")
+ "bob\@example.com" and a (possibly distinct) message to
+ "bob\@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
+ 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 *))``
``(List *)``
Match messages with a non-empty List-Id header, assuming
- configuration ``index.header.List=List-Id``
+ configuration ``index.header.List=List-Id``.
.. _macro_examples:
-=============
-notmuch-emacs
-=============
+.. _notmuch-emacs:
+
+==========================
+Emacs Frontend for Notmuch
+==========================
About this Manual
=================
| Customize Notmuch or this page.
You can change the overall appearance of the notmuch-hello screen by
-customizing the variable :index:`notmuch-hello-sections`.
+customizing the variables
+
+.. el:defcustom:: notmuch-hello-sections
+
+ |docstring::notmuch-hello-sections|
+
+.. el:defcustom:: notmuch-hello-thousands-separator
+
+ |docstring::notmuch-hello-thousands-separator|
+
+.. el:defcustom:: notmuch-show-logo
+
+ |docstring::notmuch-show-logo|
+
+.. el:defcustom:: notmuch-column-control
+
+ Controls the number of columns for saved searches/tags in notmuch view.
+
+ This variable has three potential types of values:
+
+ .. describe:: t
+
+ Automatically calculate the number of columns possible based
+ on the tags to be shown and the window width.
+
+ .. describe:: integer <n>
+
+ A lower bound on the number of characters that will
+ be used to display each column.
+
+ .. describe:: float <f>
+ A fraction of the window width that is the lower bound on the
+ number of characters that should be used for each column.
+ So:
+
+ - if you would like two columns of tags, set this to 0.5.
+
+ - if you would like a single column of tags, set this to 1.0.
+
+ - if you would like tags to be 30 characters wide, set this to 30.
+
+ - if you don't want to worry about all of this nonsense, leave
+ this set to `t`.
+
+.. el:defcustom:: notmuch-show-empty-saved-searches
+
+ |docstring::notmuch-show-empty-saved-searches|
notmuch-hello key bindings
--------------------------
-``<tab>``
+.. el:define-key:: <tab>
+
Move to the next widget (button or text entry field)
-``<backtab>``
+.. el:define-key:: <backtab>
+
Move to the previous widget.
-``<return>``
+.. el:define-key:: <return>
+
Activate the current widget.
-``g`` ``=``
+.. el:define-key:: g
+ =
+
Refresh the buffer; mainly update the counts of messages for various
saved searches.
-``G``
+.. el:define-key:: G
+
Import mail, See :ref:`importing`
-``m``
+.. el:define-key:: m
+
Compose a message
-``s``
+.. el:define-key:: s
+
Search the notmuch database using :ref:`notmuch-search`
-``v``
+.. el:define-key:: v
+
Print notmuch version
-``q``
+.. el:define-key:: q
+
Quit
.. _saved-searches:
``tag:inbox`` to access the inbox and ``tag:unread`` to access all
unread mail, but there are several options for customization:
-:index:`notmuch-saved-searches`
+.. el:defcustom:: notmuch-saved-searches
+
The list of saved searches, including names, queries, and
additional per-query options.
-:index:`notmuch-saved-search-sort-function`
+.. el:defcustom:: notmuch-saved-search-sort-function
+
This variable controls how saved searches should be sorted. A value
of ``nil`` displays the saved searches in the order they are stored
in ‘notmuch-saved-searches’.
-:index:`notmuch-column-control`
- Controls the number of columns for displaying saved-searches/tags
-
Search Box
----------
The search box lets the user enter a Notmuch query. See section
“Description” in Notmuch Query Syntax, for more info on Notmuch query
syntax. A history of recent searches is also displayed by default. The
-latter is controlled by the variable :index:`notmuch-hello-recent-searches-max`.
+latter is controlled by the variable `notmuch-hello-recent-searches-max`.
+
+.. el:defcustom:: notmuch-hello-recent-searches-max
+
+ |docstring::notmuch-hello-recent-searches-max|
Known Tags
----------
individual tag defined in the database. This can be controlled via the
following variables.
-:index:`notmuch-hello-tag-list-make-query`
+.. el:defcustom:: notmuch-hello-tag-list-make-query
+
Control how to construct a search (“virtual folder”) from a given
tag.
-:index:`notmuch-hello-hide-tags`
- Which tags not to display at all.
+.. el:defcustom:: notmuch-hello-hide-tags
-:index:`notmuch-column-control`
- Controls the number of columns for displaying saved-searches/tags
+ Which tags not to display at all.
.. _notmuch-search:
menu of results that the user can explore further by pressing
``<return>`` on the appropriate line.
-``n,C-n,<down>``
+.. el:define-key:: n
+ C-n
+ <down>
+
Move to next line
-``p,C-p,<up>``
+.. el:define-key::
+ p
+ C-p
+ <up>
+
Move to previous line
-``<return>``
+.. el:define-key:: <return>
+
Open thread on current line in :ref:`notmuch-show` mode
-``g`` ``=``
+.. el:define-key:: g
+ =
+
Refresh the buffer
-``?``
+.. el:define-key:: ?
+
Display full set of key bindings
The presentation of results can be controlled by the following
variables.
-:index:`notmuch-search-result-format`
- Control how each thread of messages is presented in the
- ``notmuch-show-mode`` buffer
+.. el:defcustom:: notmuch-search-result-format
+
+ |docstring::notmuch-search-result-format|
+
+ If the car of an element in notmuch-search-result-format is a
+ function, insert the result of calling the function into the buffer.
+
+ This allows a user to generate custom fields in the output of a
+ search result. For example, with the following settings, the first
+ few characters on each line of the search result are used to show
+ information about some significant tags associated with the thread.
+
+ .. code:: lisp
+
+ (defun -notmuch-result-flags (format-string result)
+ (let ((tags-to-letters '(("flagged" . "!")
+ ("unread" . "u")
+ ("mine" . "m")
+ ("sent" . "s")
+ ("replied" . "r")))
+ (tags (plist-get result :tags)))
+ (format format-string
+ (mapconcat (lambda (t2l)
+ (if (member (car t2l) tags)
+ (cdr t2l)
+ " "))
+ tags-to-letters ""))))
+
+ (setq notmuch-search-result-format '((-notmuch-result-flags . "%s ")
+ ("date" . "%12s ")
+ ("count" . "%9s ")
+ ("authors" . "%-30s ")
+ ("subject" . "%s ")
+ ("tags" . "(%s)")))
+
+ See also :el:defcustom:`notmuch-tree-result-format` and
+ :el:defcustom:`notmuch-unthreaded-result-format`.
+
+.. el:defcustom:: notmuch-search-oldest-first
-:index:`notmuch-search-oldest-first`
Display the oldest threads at the top of the buffer
It is also possible to customize how the name of buffers containing
search results is formatted using the following variables:
-:index:`notmuch-search-buffer-name-format`
+.. el:defcustom:: notmuch-search-buffer-name-format
+
|docstring::notmuch-search-buffer-name-format|
-:index:`notmuch-saved-search-buffer-name-format`
+.. el:defcustom:: notmuch-saved-search-buffer-name-format
+
|docstring::notmuch-saved-search-buffer-name-format|
these parts visible by clicking with the mouse button or by
pressing RET after positioning the cursor on a hidden part.
-``<space>``
+.. el:define-key:: <space>
+
Scroll the current message (if necessary),
advance to the next message, or advance to the next thread (if
already on the last message of a thread).
-``c``
+.. el:define-key:: c
+
:ref:`show-copy`
-``N``
+.. el:define-key:: N
+
Move to next message
-``P``
+.. el:define-key:: P
+
Move to previous message (or start of current message)
-``n``
+.. el:define-key:: n
+
Move to next matching message
-``p``
+.. el:define-key:: p
+
Move to previous matching message
-``+,-``
+.. el:define-key:: +
+ -
+
Add or remove arbitrary tags from the current message.
-``?``
+.. el:define-key:: !
+
+ |docstring::notmuch-show-toggle-elide-non-matching|
+
+.. el:define-key:: ?
+
Display full set of key bindings
-Display of messages can be controlled by the following variables
+Display of messages can be controlled by the following variables; see also :ref:`show-large`.
+
+.. el:defcustom:: notmuch-message-headers
-:index:`notmuch-message-headers`
|docstring::notmuch-message-headers|
-:index:`notmuch-message-headers-visible`
+.. el:defcustom:: notmuch-message-headers-visible
+
|docstring::notmuch-message-headers-visible|
-:index:`notmuch-show-header-line`
+.. el:defcustom:: notmuch-show-header-line
+
|docstring::notmuch-show-header-line|
+.. el:defcustom:: notmuch-multipart/alternative-discouraged
+
+ Which mime types to hide by default for multipart messages.
+
+ Can either be a list of mime types (as strings) or a function
+ mapping a plist representing the current message to such a list.
+ The following example function would discourage `text/html` and
+ `multipart/related` generally, but discourage `text/plain` should
+ the message be sent from `whatever@example.com`.
+
+ .. code:: lisp
+
+ (defun my--determine-discouraged (msg)
+ (let* ((headers (plist-get msg :headers))
+ (from (or (plist-get headers :From) "")))
+ (cond
+ ((string-match "whatever@example.com" from)
+ (list "text/plain"))
+ (t
+ (list "text/html" "multipart/related")))))
+
+.. _show-large:
+
+Dealing with large messages and threads
+---------------------------------------
+
+If you are finding :ref:`notmuch-show` is annoyingly slow displaying
+large messages, you can customize
+:el:defcustom:`notmuch-show-max-text-part-size`. If you want to speed up the
+display of large threads (with or without large messages), there are
+several options. First, you can display the same query in one of the
+other modes. :ref:`notmuch-unthreaded` is the most robust for
+extremely large queries, but :ref:`notmuch-tree` is also be faster
+than :ref:`notmuch-show` in general, since it only renders a single
+message a time. If you prefer to stay with the rendered thread
+("conversation") view of :ref:`notmuch-show`, you can customize the
+variables :el:defcustom:`notmuch-show-depth-limit`,
+:el:defcustom:`notmuch-show-height-limit` and
+:el:defcustom:`notmuch-show-max-text-part-size` to limit the amount of
+rendering done initially. Note that these limits are implicitly
+*OR*-ed together, and combinations might have surprising effects.
+
+.. el:defcustom:: notmuch-show-depth-limit
+
+ |docstring::notmuch-show-depth-limit|
+
+.. el:defcustom:: notmuch-show-height-limit
+
+ |docstring::notmuch-show-height-limit|
+
+.. el:defcustom:: notmuch-show-max-text-part-size
+
+ |docstring::notmuch-show-max-text-part-size|
+
.. _show-copy:
Copy to kill-ring
:ref:`notmuch-show`, and :ref:`notmuch-tree`. A subset are available
in :ref:`notmuch-search`.
-``c F`` ``notmuch-show-stash-filename``
+.. el:define-key:: c F
+ M-x notmuch-show-stash-filename
+
|docstring::notmuch-show-stash-filename|
-``c G`` ``notmuch-show-stash-git-send-email``
+.. el:define-key:: c G
+ M-x notmuch-show-stash-git-send-email
+
|docstring::notmuch-show-stash-git-send-email|
-``c I`` ``notmuch-show-stash-message-id-stripped``
+.. el:define-key:: c I
+ M-x notmuch-show-stash-message-id-stripped
+
|docstring::notmuch-show-stash-message-id-stripped|
-``c L`` ``notmuch-show-stash-mlarchive-link-and-go``
+.. el:define-key:: c L
+ M-x notmuch-show-stash-mlarchive-link-and-go
+
|docstring::notmuch-show-stash-mlarchive-link-and-go|
-``c T`` ``notmuch-show-stash-tags``
+.. el:define-key:: c T
+ M-x notmuch-show-stash-tags
+
|docstring::notmuch-show-stash-tags|
-``c c`` ``notmuch-show-stash-cc``
+.. el:define-key:: c c
+ M-x notmuch-show-stash-cc
+
|docstring::notmuch-show-stash-cc|
-``c d`` ``notmuch-show-stash-date``
+.. el:define-key:: c d
+ M-x notmuch-show-stash-date
+
|docstring::notmuch-show-stash-date|
-``c f`` ``notmuch-show-stash-from``
+.. el:define-key:: c f
+ M-x notmuch-show-stash-from
+
|docstring::notmuch-show-stash-from|
-``c i`` ``notmuch-show-stash-message-id``
+.. el:define-key:: c i
+ M-x notmuch-show-stash-message-id
+
|docstring::notmuch-show-stash-message-id|
-``c l`` ``notmuch-show-stash-mlarchive-link``
+.. el:define-key:: c l
+ M-x notmuch-show-stash-mlarchive-link
+
|docstring::notmuch-show-stash-mlarchive-link|
-``c s`` ``notmuch-show-stash-subject``
+.. el:define-key:: c s
+ M-x notmuch-show-stash-subject
+
|docstring::notmuch-show-stash-subject|
-``c t`` ``notmuch-show-stash-to``
+.. el:define-key:: c t
+ M-x notmuch-show-stash-to
+
|docstring::notmuch-show-stash-to|
-``c ?``
- Show all available copying commands
+.. el:define-key:: c ?
+ M-x notmuch-subkeymap-help
+
+ Show all available copying commands
+
+.. _emacs-show-duplicates:
+
+Dealing with duplicates
+-----------------------
+
+If there are are multiple files with the same :mailheader:`Message-ID`
+(see :any:`duplicate-files`), then :any:`notmuch-show` displays the
+number of duplicates and identifies the current duplicate. In the
+following example duplicate 3 of 5 is displayed.
+
+.. code-block::
+ :emphasize-lines: 1
+
+ M. Mustermann <max@example.com> (Sat, 30 Jul 2022 10:33:10 -0300) (inbox signed) 3/5
+ Subject: Re: Multiple files per message in emacs
+ To: notmuch@notmuchmail.org
+
+.. el:define-key:: %
+ M-x notmuch-show-choose-duplicate
+
+ |docstring::notmuch-show-choose-duplicate|
.. _notmuch-tree:
message giving the relative date, the author, subject, and any
tags.
-``c``
+.. el:define-key:: c
+
:ref:`show-copy`
-``<return>``
+.. el:define-key:: <return>
+
Displays that message.
-``N``
+.. el:define-key:: N
+
Move to next message
-``P``
+.. el:define-key:: P
+
Move to previous message
-``n``
+.. el:define-key:: n
+
Move to next matching message
-``p``
+.. el:define-key:: p
+
Move to previous matching message
-``o`` ``notmuch-tree-toggle-order``
+.. el:define-key:: o
+ M-x notmuch-tree-toggle-order
+
|docstring::notmuch-tree-toggle-order|
-``l`` ``notmuch-tree-filter``
+.. el:define-key:: l
+ M-x notmuch-tree-filter
+
Filter or LIMIT the current search results based on an additional query string
-``t`` ``notmuch-tree-filter-by-tag``
+.. el:define-key:: t
+ M-x notmuch-tree-filter-by-tag
+
Filter the current search results based on an additional tag
-``g`` ``=``
+.. el:define-key:: g
+ =
+
Refresh the buffer
-``?``
+.. el:define-key:: ?
+
Display full set of key bindings
As is the case with :ref:`notmuch-search`, the presentation of results
can be controlled by the variable ``notmuch-search-oldest-first``.
+.. el:defcustom:: notmuch-tree-result-format
+
+ |docstring::notmuch-tree-result-format|
+
+ The following example shows how to optionally display recipients instead
+ of authors for sent mail (assuming the user is named Mustermann).
+
+ .. code:: lisp
+
+ (defun -notmuch-authors-or-to (format-string result)
+ (let* ((headers (plist-get result :headers))
+ (to (plist-get headers :To))
+ (author (plist-get headers :From))
+ (face (if (plist-get result :match)
+ 'notmuch-tree-match-author-face
+ 'notmuch-tree-no-match-author-face)))
+ (propertize
+ (format format-string
+ (if (string-match "Mustermann" author)
+ (concat "To:" (notmuch-tree-clean-address to))
+ author))
+ 'face face)))
+
+ (setq notmuch-tree-result-format
+ '(("date" . "%12s ")
+ (-notmuch-authors-or-to . "%-20.20s")
+ ((("tree" . "%s")
+ ("subject" . "%s"))
+ . " %-54s ")
+ ("tags" . "(%s)")))
+
+ See also :el:defcustom:`notmuch-search-result-format` and
+ :el:defcustom:`notmuch-unthreaded-result-format`.
+
+
.. _notmuch-unthreaded:
notmuch-unthreaded
Keybindings are the same as :any:`notmuch-tree`.
+.. el:defcustom:: notmuch-unthreaded-result-format
+
+ |docstring::notmuch-unthreaded-result-format|
+
+ See also :el:defcustom:`notmuch-search-result-format` and
+ :el:defcustom:`notmuch-tree-result-format`.
+
Global key bindings
===================
Several features are accessible from most places in notmuch through the
following key bindings:
-``j``
+.. el:define-key:: j
+
Jump to saved searches using :ref:`notmuch-jump`.
-``k``
+.. el:define-key:: k
+
Tagging operations using :ref:`notmuch-tag-jump`
-``C-_`` ``C-/`` ``C-x u``: Undo previous tagging operation using :ref:`notmuch-tag-undo`
+.. el:define-key:: C-_
+ C-/
+ C-x u
+
+ Undo previous tagging operation using :any:`notmuch-tag-undo`
.. _notmuch-jump:
operations specified in ``notmuch-tagging-keys``; i.e. each
``+tag`` is replaced by ``-tag`` and vice versa.
-:index:`notmuch-tagging-keys`
+.. el:defcustom:: notmuch-tagging-keys
|docstring::notmuch-tagging-keys|
-.. _notmuch-tag-undo:
notmuch-tag-undo
----------------
Each notmuch buffer supporting tagging operations (i.e buffers in
:any:`notmuch-show`, :any:`notmuch-search`, :any:`notmuch-tree`, and
:any:`notmuch-unthreaded` mode) keeps a local stack of tagging
-operations. These can be undone via ``notmuch-tag-undo``. By default
+operations. These can be undone via :any:`notmuch-tag-undo`. By default
this is bound to the usual Emacs keys for undo.
-:index:`notmuch-tag-undo`
+.. el:define-key:: C-_
+ C-/
+ C-x u
+ M-x notmuch-tag-undo
|docstring::notmuch-tag-undo|
Buffer navigation
=================
-:index:`notmuch-cycle-notmuch-buffers`
+.. el:define-key:: M-x notmuch-cycle-notmuch-buffers
+
|docstring::notmuch-cycle-notmuch-buffers|
Configuration
Importing Mail
--------------
-:index:`notmuch-poll`
+.. el:define-key:: M-x notmuch-poll
+
|docstring::notmuch-poll|
-:index:`notmuch-poll-script`
+.. el:defcustom:: notmuch-poll-script
+
|docstring::notmuch-poll-script|
Sending Mail
------------
-:index:`mail-user-agent`
+.. el:defcustom:: mail-user-agent
Emacs consults the variable :code:`mail-user-agent` to choose a mail
sending package for commands like :code:`report-emacs-bug` and
:code:`compose-mail`. To use ``notmuch`` for this, customize this
variable to the symbol :code:`notmuch-user-agent`.
+.. el:defcustom:: message-dont-reply-to-names
+
+ When composing mail replies, Emacs's message mode uses the
+ variable :code:`message-dont-reply-to-names` to exclude
+ recipients matching a given collection of regular expressions
+ or satisfying an arbitrary predicate. Notmuch's MUA inherits
+ this standard mechanism and will honour your customization of
+ this variable.
+
Init File
---------
--- /dev/null
+Notmuch Queries
+===============
+
+.. toctree::
+ :titlesonly:
+
+ man7/notmuch-search-terms
+ man7/notmuch-sexp-queries
+ man7/notmuch-properties
(string= (downcase t1)
(downcase t2))))))
-(defvar notmuch-multipart/alternative-discouraged
+(defcustom notmuch-multipart/alternative-discouraged
'(;; Avoid HTML parts.
"text/html"
;; multipart/related usually contain a text/html part and some
;; associated graphics.
- "multipart/related"))
+ "multipart/related")
+ "Which mime types to hide by default for multipart messages.
+
+Can either be a list of mime types (as strings) or a function
+mapping a plist representing the current message to such a list.
+See Info node `(notmuch-emacs) notmuch-show' for a sample function."
+ :group 'notmuch-show
+ :type '(radio (repeat :tag "MIME Types" string)
+ (function :tag "Function")))
(defun notmuch-multipart/alternative-determine-discouraged (msg)
"Return the discouraged alternatives for the specified message."
(defvar-local notmuch-show-process-crypto nil)
+(defun notmuch--run-show (search-terms &optional duplicate)
+ "Return a list of threads of messages matching SEARCH-TERMS.
+
+A thread is a forest or list of trees. A tree is a two element
+list where the first element is a message, and the second element
+is a possibly empty forest of replies."
+ (let ((args '("show" "--format=sexp" "--format-version=5")))
+ (when notmuch-show-process-crypto
+ (setq args (append args '("--decrypt=true"))))
+ (when duplicate
+ (setq args (append args (list (format "--duplicate=%d" duplicate)))))
+ (setq args (append args search-terms))
+ (apply #'notmuch-call-notmuch-sexp args)))
+
;;; Generic Utilities
(defun notmuch-interactive-region ()
'notmuch-interactive-region
"notmuch 0.29")
+(defun notmuch--inline-override-types ()
+ "Override mm-inline-override-types to stop application/*
+parts from being displayed unless the user has customized
+it themselves."
+ (if (equal mm-inline-override-types
+ (eval (car (get 'mm-inline-override-types 'standard-value))))
+ (cons "application/.*" mm-inline-override-types)
+ mm-inline-override-types))
;;; _
(provide 'notmuch-lib)
;;; Code:
+(eval-when-compile (require 'subr-x))
+
(require 'message)
+(require 'gmm-utils)
(require 'mm-view)
(require 'format-spec)
;;; Mua reply
-(defun notmuch-mua-reply (query-string &optional sender reply-all)
- (let ((args '("reply" "--format=sexp" "--format-version=5"))
- (process-crypto notmuch-show-process-crypto)
- reply
- original)
+(defun notmuch-mua-reply (query-string &optional sender reply-all duplicate)
+ (let* ((duparg (and duplicate (list (format "--duplicate=%d" duplicate))))
+ (args `("reply" "--format=sexp" "--format-version=5" ,@duparg))
+ (process-crypto notmuch-show-process-crypto)
+ reply
+ original)
(when process-crypto
(setq args (append args '("--decrypt=true"))))
(if reply-all
;; text.
(notmuch-show-process-crypto process-crypto)
;; Don't indent multipart sub-parts.
- (notmuch-show-indent-multipart nil))
+ (notmuch-show-indent-multipart nil)
+ ;; Stop certain mime types from being inlined
+ (mm-inline-override-types (notmuch--inline-override-types)))
;; We don't want sigstatus buttons (an information leak and usually wrong anyway).
(cl-letf (((symbol-function 'notmuch-crypto-insert-sigstatus-button) #'ignore)
((symbol-function 'notmuch-crypto-insert-encstatus-button) #'ignore))
(erase-buffer)
(notmuch-message-mode)))
+(defun notmuch-mua--remove-dont-reply-to-names ()
+ (when-let* ((nr (if (functionp message-dont-reply-to-names)
+ message-dont-reply-to-names
+ (gmm-regexp-concat message-dont-reply-to-names)))
+ (nr-filter
+ (if (functionp nr)
+ (lambda (mail) (and (not (funcall nr mail)) mail))
+ (lambda (mail) (and (not (string-match-p nr mail)) mail)))))
+ (dolist (header '("To" "Cc"))
+ (when-let ((v (message-fetch-field header)))
+ (let* ((tokens (mapcar #'string-trim (message-tokenize-header v)))
+ (good-tokens (delq nil (mapcar nr-filter tokens)))
+ (addr (and good-tokens (mapconcat #'identity good-tokens ", "))))
+ (message-replace-header header addr))))))
+
(defun notmuch-mua-mail (&optional to subject other-headers _continue
switch-function yank-action send-actions
return-action &rest ignored)
- "Invoke the notmuch mail composition window."
+ "Invoke the notmuch mail composition window.
+
+The position of point when the function returns differs depending
+on the values of TO and SUBJECT. If both are non-nil, point is
+moved to the message's body. If SUBJECT is nil but TO isn't,
+point is moved to the \"Subject:\" header. Otherwise, point is
+moved to the \"To:\" header."
(interactive)
(when notmuch-mua-user-agent-function
(let ((user-agent (funcall notmuch-mua-user-agent-function)))
(message-this-is-mail t))
(message-setup-1 headers yank-action send-actions return-action))
(notmuch-fcc-header-setup)
+ (notmuch-mua--remove-dont-reply-to-names)
(message-sort-headers)
(message-hide-headers)
(set-buffer-modified-p nil)
(notmuch-mua-maybe-set-window-dedicated)
- (message-goto-to))
+ (cond
+ ((and to subject) (message-goto-body))
+ (to (message-goto-subject))
+ (t (message-goto-to))))
(defvar notmuch-mua-sender-history nil)
(message-hide-headers)
(set-buffer-modified-p nil))))
-(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all)
+(defun notmuch-mua-new-reply (query-string &optional prompt-for-sender reply-all duplicate)
"Compose a reply to the message identified by QUERY-STRING.
If PROMPT-FOR-SENDER is non-nil, the user will be prompted for
the From: address first. If REPLY-ALL is non-nil, the message
-will be addressed to all recipients of the source message."
+will be addressed to all recipients of the source message. If
+DUPLICATE is non-nil, based the reply on that duplicate file"
;; `select-active-regions' is t by default. The reply insertion code
;; sets the region to the quoted message to make it easy to delete
;; (kill-region or C-w). These two things combine to put the quoted
(let ((sender (and prompt-for-sender
(notmuch-mua-prompt-for-sender)))
(select-active-regions nil))
- (notmuch-mua-reply query-string sender reply-all)
+ (notmuch-mua-reply query-string sender reply-all duplicate)
(deactivate-mark)))
;;; Checks
;;; Basic query function
-(defun notmuch-query-get-threads (search-terms)
- "Return a list of threads of messages matching SEARCH-TERMS.
-
-A thread is a forest or list of trees. A tree is a two element
-list where the first element is a message, and the second element
-is a possibly empty forest of replies."
- (let ((args '("show" "--format=sexp" "--format-version=5")))
- (when notmuch-show-process-crypto
- (setq args (append args '("--decrypt=true"))))
- (setq args (append args search-terms))
- (apply #'notmuch-call-notmuch-sexp args)))
+(define-obsolete-function-alias
+ 'notmuch-query-get-threads
+ #'notmuch--run-show
+ "notmuch 0.37")
;;; Mapping functions across collections of messages
(defun notmuch-query-map-tree (fn tree)
"Apply function FN to every message in TREE.
Flatten results to a list. See the function
-`notmuch-query-get-threads' for more information."
+`notmuch--run-show' for more information."
(cons (funcall fn (car tree))
(notmuch-query-map-forest fn (cadr tree))))
"Return a list of message-ids of messages that match SEARCH-TERMS."
(notmuch-query-map-threads
(lambda (msg) (plist-get msg :id))
- (notmuch-query-get-threads search-terms)))
+ (notmuch--run-show search-terms)))
+
+;;; Everything in this library is obsolete
+(dolist (fun '(map-aux map-threads map-forest map-tree get-message-ids))
+ (make-obsolete (intern (format "notmuch-query-%s" fun)) nil "notmuch 0.37"))
(provide 'notmuch-query)
(require 'notmuch-lib)
(require 'notmuch-tag)
-(require 'notmuch-query)
(require 'notmuch-wash)
(require 'notmuch-mua)
(require 'notmuch-crypto)
:group 'notmuch-show)
(defcustom notmuch-show-header-line t
- "Show a header line with the current message's subject."
- :type 'boolean
+ "Show a header line in notmuch show buffers.
+
+If t (the default), the header line will contain the current
+message's subject.
+
+If a string, this value is interpreted as a format string to be
+passed to `format-spec` with `%s` as the substitution variable
+for the message's subject. E.g., to display the subject trimmed
+to a maximum of 80 columns, you could use \"%>-80s\" as format.
+
+If you assign to this variable a function, it will be called with
+the subject as argument, and the return value will be used as the
+header line format. Since the function is called with the
+message buffer as the current buffer, it is also possible to
+access any other properties of the message, using for instance
+notmuch-show functions such as
+`notmuch-show-get-message-properties'.
+
+Finally, if this variable is set to nil, no header is
+displayed."
+ :type '(choice (const :tag "No header" ni)
+ (const :tag "Subject" t)
+ (string :tag "Format")
+ (function :tag "Function"))
+ :group 'notmuch-show)
+
+(defcustom notmuch-show-depth-limit nil
+ "Depth beyond which message bodies are displayed lazily.
+
+If bound to an integer, any message with tree depth greater than
+this will have its body display lazily, initially
+inserting only a button.
+
+If this variable is set to nil (the default) no such lazy
+insertion is done."
+ :type '(choice (const :tag "No limit" nil)
+ (number :tag "Limit" 10))
+ :group 'notmuch-show)
+
+(defcustom notmuch-show-height-limit nil
+ "Height (from leaves) beyond which message bodies are displayed lazily.
+
+If bound to an integer, any message with height in the message
+tree greater than this will have its body displayed lazily,
+initially only a button.
+
+If this variable is set to nil (the default) no such lazy
+display is done."
+ :type '(choice (const :tag "No limit" nil)
+ (number :tag "Limit" 10))
:group 'notmuch-show)
(defcustom notmuch-show-relative-dates t
;; Otherwise format the name and address together.
(concat p-name " <" p-address ">"))))
-(defun notmuch-show-insert-headerline (headers date tags depth)
+(defun notmuch-show--mark-height (tree)
+ "Calculate and cache height (distance from deepest descendent)"
+ (let* ((msg (car tree))
+ (children (cadr tree))
+ (cached-height (plist-get msg :height)))
+ (or cached-height
+ (let ((height
+ (if (null children) 0
+ (1+ (apply #'max (mapcar #'notmuch-show--mark-height children))))))
+ (plist-put msg :height height)
+ height))))
+
+(defun notmuch-show-insert-headerline (headers date tags depth duplicate file-count)
"Insert a notmuch style headerline based on HEADERS for a
message at DEPTH in the current thread."
(let ((start (point))
date
") ("
(notmuch-tag-format-tags tags tags)
- ")\n")
+ ")")
+ (insert
+ (if (> file-count 1)
+ (let ((txt (format "%d/%d\n" duplicate file-count)))
+ (concat
+ (notmuch-show-spaces-n (max 0 (- (window-width) (+ (current-column) (length txt)))))
+ txt))
+ "\n"))
(overlay-put (make-overlay start (point))
'face 'notmuch-message-summary-face)))
(let* ((content-type (plist-get part :content-type))
(mime-type (notmuch-show-mime-type part))
(nth (plist-get part :id))
+ (height (plist-get msg :height))
(long (and (notmuch-match-content-type mime-type "text/*")
(> notmuch-show-max-text-part-size 0)
(> (length (plist-get part :content))
notmuch-show-max-text-part-size)))
+ (deep (and notmuch-show-depth-limit
+ (> depth notmuch-show-depth-limit)))
+ (high (and notmuch-show-height-limit
+ (> height notmuch-show-height-limit)))
(beg (point))
;; This default header-p function omits the part button for
;; the first (or only) part if this is text/plain.
- (button (and (funcall notmuch-show-insert-header-p-function part hide)
+ (button (and (or deep long high
+ (funcall notmuch-show-insert-header-p-function part hide))
(notmuch-show-insert-part-header
nth mime-type
(and content-type (downcase content-type))
(plist-get part :filename))))
- ;; Hide the part initially if HIDE is t, or if it is too long
+ ;; Hide the part initially if HIDE is t, or if it is too long/deep
;; and we have a button to allow toggling.
(show-part (not (or (equal hide t)
+ (and deep button)
+ (and high button)
(and long button))))
(content-beg (point)))
;; Store the computed mime-type for later use (e.g. by attachment handlers).
(defvar notmuch-show-previous-subject "")
(make-variable-buffer-local 'notmuch-show-previous-subject)
+(defun notmuch-show-choose-duplicate (duplicate)
+ "Display message file with index DUPLICATE in place of the current one.
+
+Message file indices are based on the order the files are
+discovered by `notmuch new' (and hence are somewhat arbitrary),
+and correspond to those passed to the \"\\-\\-duplicate\" arguments
+to the CLI.
+
+When called interactively, the function will prompt for the index
+of the file to display. An error will be signaled if the index
+is out of range."
+ (interactive "Nduplicate: ")
+ (let ((count (length (notmuch-show-get-prop :filename))))
+ (when (or (> duplicate count)
+ (< duplicate 1))
+ (error "Duplicate %d out of range [1,%d]" duplicate count)))
+ (notmuch-show-move-to-message-top)
+ (save-excursion
+ (let* ((extent (notmuch-show-message-extent))
+ (id (notmuch-show-get-message-id))
+ (depth (notmuch-show-get-depth))
+ (inhibit-read-only t)
+ (new-msg (notmuch--run-show (list id) duplicate)))
+ ;; clean up existing overlays to avoid extending them.
+ (dolist (o (overlays-in (car extent) (cdr extent)))
+ (delete-overlay o))
+ ;; pretend insertion is happening at end of buffer
+ (narrow-to-region (point-min) (car extent))
+ ;; Insert first, then delete, to avoid marker for start of next
+ ;; message being in same place as the start of this one.
+ (notmuch-show-insert-msg new-msg depth)
+ (widen)
+ (delete-region (point) (cdr extent)))))
+
(defun notmuch-show-insert-msg (msg depth)
"Insert the message MSG at depth DEPTH in the current thread."
(let* ((headers (plist-get msg :headers))
+ (duplicate (or (plist-get msg :duplicate) 0))
+ (files (length (plist-get msg :filename)))
;; Indentation causes the buffer offset of the start/end
;; points to move, so we must use markers.
message-start message-end
(or (and notmuch-show-relative-dates
(plist-get msg :date_relative))
(plist-get headers :Date))
- (plist-get msg :tags) depth)
+ (plist-get msg :tags) depth duplicate files)
(setq content-start (point-marker))
;; Set `headers-start' to point after the 'Subject:' header to be
;; compatible with the existing implementation. This just sets it
(replies (cadr tree)))
;; We test whether there is a message or just some replies.
(when msg
+ (notmuch-show--mark-height tree)
(notmuch-show-insert-msg msg depth))
(notmuch-show-insert-thread replies (1+ depth))))
(let ((buffer-name (generate-new-buffer-name
(or buffer-name
(concat "*notmuch-" thread-id "*"))))
- ;; We override mm-inline-override-types to stop application/*
- ;; parts from being displayed unless the user has customized
- ;; it themselves.
- (mm-inline-override-types
- (if (equal mm-inline-override-types
- (eval (car (get 'mm-inline-override-types 'standard-value))))
- (cons "application/*" mm-inline-override-types)
- mm-inline-override-types)))
+ (mm-inline-override-types (notmuch--inline-override-types)))
+
(pop-to-buffer-same-window (get-buffer-create buffer-name))
;; No need to track undo information for this buffer.
(setq buffer-undo-list t)
(push (list thread "and (" context ")") queries))
queries))
+(defun notmuch-show--header-line-format ()
+ "Compute the header line format of a notmuch-show buffer."
+ (when notmuch-show-header-line
+ (let* ((s (notmuch-sanitize
+ (notmuch-show-strip-re (notmuch-show-get-subject))))
+ (subject (replace-regexp-in-string "%" "%%" s)))
+ (cond ((stringp notmuch-show-header-line)
+ (format-spec notmuch-show-header-line `((?s . ,subject))))
+ ((functionp notmuch-show-header-line)
+ (funcall notmuch-show-header-line subject))
+ (notmuch-show-header-line subject)))))
+
(defun notmuch-show--build-buffer (&optional state)
"Display messages matching the current buffer context.
(notmuch-show-previous-subject ""))
;; Use results from the first query that returns some.
(while (and (not forest) queries)
- (setq forest (notmuch-query-get-threads
+ (setq forest (notmuch--run-show
(append cli-args (list "'") (car queries) (list "'"))))
(when (and forest notmuch-show-single-message)
(setq forest (list (list (list forest)))))
;; display changes.
(notmuch-show-mapc
(lambda () (notmuch-show-set-prop :orig-tags (notmuch-show-get-tags))))
- ;; Set the header line to the subject of the first message.
- (when notmuch-show-header-line
- (setq header-line-format
- (replace-regexp-in-string "%" "%%"
- (notmuch-sanitize
- (notmuch-show-strip-re
- (notmuch-show-get-subject))))))
+ (setq header-line-format (notmuch-show--header-line-format))
(run-hooks 'notmuch-show-hook)
(if state
(notmuch-show-apply-state state)
reset based on the original query."
(interactive "P")
(let ((inhibit-read-only t)
+ (mm-inline-override-types (notmuch--inline-override-types))
(state (unless reset-state
(notmuch-show-capture-state))))
;; `erase-buffer' does not seem to remove overlays, which can lead
(define-key map "#" 'notmuch-show-print-message)
(define-key map "!" 'notmuch-show-toggle-elide-non-matching)
(define-key map "$" 'notmuch-show-toggle-process-crypto)
+ (define-key map "%" 'notmuch-show-choose-duplicate)
(define-key map "<" 'notmuch-show-toggle-thread-indentation)
(define-key map "t" 'toggle-truncate-lines)
(define-key map "." 'notmuch-show-part-map)
;; dme: Would it make sense to use a macro for many of these?
-;; XXX TODO figure out what to do about multiple filenames
(defun notmuch-show-get-filename ()
"Return the filename of the current message."
- (car (notmuch-show-get-prop :filename)))
+ (let ((duplicate (notmuch-show-get-duplicate)))
+ (nth (1- duplicate) (notmuch-show-get-prop :filename))))
(defun notmuch-show-get-header (header &optional props)
"Return the named header of the current message, if any."
(defun notmuch-show-get-date ()
(notmuch-show-get-header :Date))
+(defun notmuch-show-get-duplicate ()
+ ;; if no duplicate property exists, assume first file
+ (or (notmuch-show-get-prop :duplicate) 1))
+
(defun notmuch-show-get-timestamp ()
(notmuch-show-get-prop :timestamp))
(defun notmuch-show-reply (&optional prompt-for-sender)
"Reply to the sender and all recipients of the current message."
(interactive "P")
- (notmuch-mua-new-reply (notmuch-show-get-message-id) prompt-for-sender t))
+ (notmuch-mua-new-reply (notmuch-show-get-message-id) prompt-for-sender t
+ (notmuch-show-get-prop :duplicate)))
(put 'notmuch-show-reply-sender 'notmuch-prefix-doc "... and prompt for sender")
(defun notmuch-show-reply-sender (&optional prompt-for-sender)
"Reply to the sender of the current message."
(interactive "P")
- (notmuch-mua-new-reply (notmuch-show-get-message-id) prompt-for-sender nil))
+ (notmuch-mua-new-reply (notmuch-show-get-message-id) prompt-for-sender nil
+ (notmuch-show-get-prop :duplicate)))
(put 'notmuch-show-forward-message 'notmuch-prefix-doc
"... and prompt for sender")
"View the original source of the current message."
(interactive)
(let* ((id (notmuch-show-get-message-id))
- (buf (get-buffer-create (concat "*notmuch-raw-" id "*")))
+ (duplicate (notmuch-show-get-duplicate))
+ (args (if (> duplicate 1)
+ (list (format "--duplicate=%d" duplicate) id)
+ (list id)))
+ (buf (get-buffer-create (format "*notmuch-raw-%s-%d*" id duplicate)))
(inhibit-read-only t))
(pop-to-buffer-same-window buf)
(erase-buffer)
(let ((coding-system-for-read 'no-conversion))
- (notmuch--call-process notmuch-command nil t nil "show" "--format=raw" id))
+ (apply #'notmuch--call-process notmuch-command nil t nil "show" "--format=raw" args))
(goto-char (point-min))
(set-buffer-modified-p nil)
(setq buffer-read-only t)
If SHOW is non-nil, open the next item in a show
buffer. Otherwise just highlight the next item in the search
buffer. If PREVIOUS is non-nil, move to the previous item in the
-search results instead."
+search results instead.
+
+Return non-nil on success."
(interactive "P")
(let ((parent-buffer notmuch-show-parent-buffer))
(notmuch-bury-or-kill-this-buffer)
(require 'mail-parse)
(require 'notmuch-lib)
-(require 'notmuch-query)
(require 'notmuch-show)
(require 'notmuch-tag)
(require 'notmuch-parser)
:type '(alist :key-type symbol :value-type string)
:group 'notmuch-tree)
+(defconst notmuch-tree--field-names
+ '(choice :tag "Field"
+ (const :tag "Date" "date")
+ (const :tag "Authors" "authors")
+ (const :tag "Subject" "subject")
+ (const :tag "Tree" "tree")
+ (const :tag "Tags" "tags")
+ (function)))
+
(defcustom notmuch-tree-result-format
`(("date" . "%12s ")
("authors" . "%-20s")
("tags" . "(%s)"))
"Result formatting for tree view.
-Supported fields are: date, authors, subject, tree, tags.
+List of pairs of (field . format-string). Supported field
+strings are: \"date\", \"authors\", \"subject\", \"tree\",
+\"tags\". It is also supported to pass a function in place of a
+field-name. In this case the function is passed the thread
+object (plist) and format string.
Tree means the thread tree box graphics. The field may
also be a list in which case the formatting rules are
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)
+\(put it in the neighbouring fields instead)."
+
+ :type `(alist :key-type (choice ,notmuch-tree--field-names
+ (alist :key-type ,notmuch-tree--field-names
+ :value-type (string :tag "Format")))
+ :value-type (string :tag "Format"))
:group 'notmuch-tree)
(defcustom notmuch-unthreaded-result-format
("tags" . "(%s)"))
"Result formatting for unthreaded tree view.
-Supported fields are: date, authors, subject, tree, tags.
+List of pairs of (field . format-string). Supported field
+strings are: \"date\", \"authors\", \"subject\", \"tree\",
+\"tags\". It is also supported to pass a function in place of a
+field-name. In this case the function is passed the thread
+object (plist) and format string.
Tree means the thread tree box graphics. The field may
also be a list in which case the formatting rules are
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)
+\(put it in the neighbouring fields instead)."
+
+ :type `(alist :key-type (choice ,notmuch-tree--field-names
+ (alist :key-type ,notmuch-tree--field-names
+ :value-type (string :tag "Format")))
+ :value-type (string :tag "Format"))
:group 'notmuch-tree)
(defun notmuch-tree-result-format ()
("tags" . "(%s)"))
"Search result formatting.
-Supported fields are: date, count, authors, subject, tags.
-For example:
- (setq notmuch-search-result-format
- \\='((\"authors\" . \"%-40s\")
- (\"subject\" . \"%s\")))
+List of pairs of (field . format-string). Supported field
+strings are: \"date\", \"count\", \"authors\", \"subject\",
+\"tags\". It is also supported to pass a function in place of a
+field name. In this case the function is passed the thread
+object (plist) and format string.
Line breaks are permitted in format strings (though this is
currently experimental). Note that a line break at the end of an
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
+ (choice
+ (const :tag "Date" "date")
+ (const :tag "Count" "count")
+ (const :tag "Authors" "authors")
+ (const :tag "Subject" "subject")
+ (const :tag "Tags" "tags")
+ function)
+ :value-type (string :tag "Format"))
:group 'notmuch-search)
;; The name of this variable `notmuch-init-file' is consistent with the
With a prefix argument, invert the default value of
`notmuch-show-only-matching-messages' when displaying the
-thread."
+thread.
+
+Return non-nil on success."
(interactive "P")
(let ((thread-id (notmuch-search-find-thread-id)))
(if thread-id
(format "*%s*" (truncate-string-to-width
(notmuch-search-find-subject)
30 nil nil t)))
- (message "End of search results."))))
+ (message "End of search results.")
+ nil)))
(defun notmuch-tree-from-search-current-query ()
"Tree view of current query."
notmuch_run_hook (notmuch_database_t *notmuch, const char *hook)
{
char *hook_path;
+ const char *config_path;
int status = 0;
pid_t pid;
return 1;
}
+ config_path = notmuch_config_path (notmuch);
+ if (setenv ("NOTMUCH_CONFIG", config_path, 1)) {
+ perror ("setenv");
+ return 1;
+ }
+
/* Check access before fork() for speed and simplicity of error handling. */
if (access (hook_path, X_OK) == -1) {
/* Ignore ENOENT. It's okay not to have a hook, hook dir, or even
return NOTMUCH_STATUS_READ_ONLY_DATABASE;
}
+ if (! notmuch->open) {
+ _notmuch_database_log (notmuch, "Cannot write to a closed database.\n");
+ return NOTMUCH_STATUS_CLOSED_DATABASE;
+ }
+
return NOTMUCH_STATUS_SUCCESS;
}
notmuch_query_t *query = NULL;
unsigned int count = 0, total = 0;
- status = _notmuch_database_ensure_writable (notmuch);
- if (status)
- return status;
+ if (_notmuch_database_mode (notmuch) != NOTMUCH_DATABASE_MODE_READ_WRITE)
+ return NOTMUCH_STATUS_READ_ONLY_DATABASE;
db = notmuch->writable_xapian_db;
message->doc = doc;
message->termpos = 0;
+ message->modified = false;
return message;
}
return value;
}
-/*
- * For special applications where we only want the thread id, reading
- * in all metadata is a heavy I/O penalty.
- */
-const char *
-_notmuch_message_get_thread_id_only (notmuch_message_t *message)
-{
-
- Xapian::TermIterator i = message->doc.termlist_begin ();
- Xapian::TermIterator end = message->doc.termlist_end ();
-
- message->thread_id = _notmuch_message_get_term (message, i, end,
- _find_prefix ("thread"));
- return message->thread_id;
-}
-
-
static void
_notmuch_message_ensure_metadata (notmuch_message_t *message, void *field)
{
}
/* Add "folder:" term for directory. */
-static notmuch_status_t
+NODISCARD static notmuch_status_t
_notmuch_message_add_folder_terms (notmuch_message_t *message,
const char *directory)
{
*folder = '\0';
}
- _notmuch_message_add_term (message, "folder", folder);
+ if (notmuch_status_t status = COERCE_STATUS (_notmuch_message_add_term (message, "folder",
+ folder),
+ "adding folder term"))
+ return status;
talloc_free (folder);
#define RECURSIVE_SUFFIX "/**"
/* Add "path:" terms for directory. */
-static notmuch_status_t
+NODISCARD static notmuch_status_t
_notmuch_message_add_path_terms (notmuch_message_t *message,
const char *directory)
{
+ notmuch_status_t status;
+
/* Add exact "path:" term. */
- _notmuch_message_add_term (message, "path", directory);
+ status = COERCE_STATUS (_notmuch_message_add_term (message, "path", directory),
+ "adding path term");
+ if (status)
+ return status;
if (strlen (directory)) {
char *path, *p;
for (p = path + strlen (path) - 1; p > path; p--) {
if (*p == '/') {
strcpy (p, RECURSIVE_SUFFIX);
- _notmuch_message_add_term (message, "path", path);
+ status = COERCE_STATUS (_notmuch_message_add_term (message, "path", path),
+ "adding path term");
+ if (status)
+ return status;
}
}
}
/* Recursive all-matching path:** for consistency. */
- _notmuch_message_add_term (message, "path", "**");
+ status = COERCE_STATUS (_notmuch_message_add_term (message, "path", "**"),
+ "adding path term");
+ if (status)
+ return status;
return NOTMUCH_STATUS_SUCCESS;
}
const char *direntry, *directory;
char *colon;
const std::string &term = *i;
+ notmuch_status_t term_status;
/* Terminate loop at first term without desired prefix. */
if (strncmp (term.c_str (), direntry_prefix, direntry_prefix_len))
message->notmuch,
directory_id);
- _notmuch_message_add_folder_terms (message, directory);
- _notmuch_message_add_path_terms (message, directory);
+ term_status = _notmuch_message_add_folder_terms (message, directory);
+ if (term_status)
+ return term_status;
+
+ term_status = _notmuch_message_add_path_terms (message, directory);
+ if (term_status)
+ return term_status;
}
return status;
/* New file-direntry allows navigating to this message with
* notmuch_directory_get_child_files() . */
- _notmuch_message_add_term (message, "file-direntry", direntry);
+ status = COERCE_STATUS (_notmuch_message_add_term (message, "file-direntry", direntry),
+ "adding file-direntry term");
+ if (status)
+ return status;
- _notmuch_message_add_folder_terms (message, directory);
- _notmuch_message_add_path_terms (message, directory);
+ status = _notmuch_message_add_folder_terms (message, directory);
+ if (status)
+ return status;
+
+ status = _notmuch_message_add_path_terms (message, directory);
+ if (status)
+ return status;
talloc_free (local);
*
* This change will not be reflected in the database until the next
* call to _notmuch_message_sync. */
-notmuch_private_status_t
+NODISCARD notmuch_private_status_t
_notmuch_message_add_term (notmuch_message_t *message,
const char *prefix_name,
const char *value)
{
char *term;
+ notmuch_private_status_t status = NOTMUCH_PRIVATE_STATUS_SUCCESS;
if (value == NULL)
return NOTMUCH_PRIVATE_STATUS_NULL_POINTER;
term = talloc_asprintf (message, "%s%s",
_find_prefix (prefix_name), value);
+ if (strlen (term) > NOTMUCH_TERM_MAX) {
+ status = NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG;
+ goto DONE;
+ }
- if (strlen (term) > NOTMUCH_TERM_MAX)
- return NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG;
-
- message->doc.add_term (term, 0);
- message->modified = true;
+ try {
+ message->doc.add_term (term, 0);
+ message->modified = true;
+ _notmuch_message_invalidate_metadata (message, prefix_name);
+ } catch (Xapian::Error &error) {
+ LOG_XAPIAN_EXCEPTION (message, error);
+ status = NOTMUCH_PRIVATE_STATUS_XAPIAN_EXCEPTION;
+ }
+ DONE:
talloc_free (term);
-
- _notmuch_message_invalidate_metadata (message, prefix_name);
-
- return NOTMUCH_PRIVATE_STATUS_SUCCESS;
+ return status;
}
/* Parse 'text' and add a term to 'message' for each parsed word. Each
*
* This change will not be reflected in the database until the next
* call to _notmuch_message_sync. */
-notmuch_private_status_t
+NODISCARD notmuch_private_status_t
_notmuch_message_remove_term (notmuch_message_t *message,
const char *prefix_name,
const char *value)
try {
message->doc.remove_term (term);
message->modified = true;
- } catch (const Xapian::InvalidArgumentError) {
+ } catch (const Xapian::InvalidArgumentError &error) {
/* We'll let the philosophers try to wrestle with the
* question of whether failing to remove that which was not
* there in the first place is failure. For us, we'll silently
* consider it all good. */
+ LOG_XAPIAN_EXCEPTION (message, error);
}
talloc_free (term);
char *to_set, *to_clear;
notmuch_status_t status = NOTMUCH_STATUS_SUCCESS;
+ status = _notmuch_database_ensure_writable (message->notmuch);
+ if (status)
+ return status;
+
_get_maildir_flag_actions (message, &to_set, &to_clear);
for (filenames = notmuch_message_get_filenames (message);
if (thread_id == NULL)
thread_id = orig_thread_id;
- _notmuch_message_add_term (message, "thread", thread_id);
+ ret = COERCE_STATUS (_notmuch_message_add_term (message, "thread", thread_id),
+ "adding thread term");
+ if (ret)
+ goto DONE;
+
/* Take header values only from first filename */
if (found == 0)
_notmuch_message_set_header_values (message, date, from, subject);
}
if (found == 0) {
/* put back thread id to help cleanup */
- _notmuch_message_add_term (message, "thread", orig_thread_id);
+ ret = COERCE_STATUS (_notmuch_message_add_term (message, "thread", orig_thread_id),
+ "adding thread term");
+ if (ret)
+ goto DONE;
+
ret = _notmuch_message_delete (message);
} else {
_notmuch_message_sync (message);
NOTMUCH_PRIVATE_STATUS_NO_CONFIG = NOTMUCH_STATUS_NO_CONFIG,
NOTMUCH_PRIVATE_STATUS_NO_DATABASE = NOTMUCH_STATUS_NO_DATABASE,
NOTMUCH_PRIVATE_STATUS_DATABASE_EXISTS = NOTMUCH_STATUS_DATABASE_EXISTS,
+ NOTMUCH_PRIVATE_STATUS_NO_MAIL_ROOT = NOTMUCH_STATUS_NO_MAIL_ROOT,
+ NOTMUCH_PRIVATE_STATUS_BAD_QUERY_SYNTAX = NOTMUCH_STATUS_BAD_QUERY_SYNTAX,
+ NOTMUCH_PRIVATE_STATUS_CLOSED_DATABASE = NOTMUCH_STATUS_CLOSED_DATABASE,
/* Then add our own private values. */
NOTMUCH_PRIVATE_STATUS_TERM_TOO_LONG = NOTMUCH_STATUS_LAST_STATUS,
void
_notmuch_message_remove_unprefixed_terms (notmuch_message_t *message);
-const char *
-_notmuch_message_get_thread_id_only (notmuch_message_t *message);
-
size_t _notmuch_message_get_thread_depth (notmuch_message_t *message);
void
#undef talloc_steal
#define talloc_steal _notmuch_talloc_steal
#endif
+
+#if __cplusplus >= 201703L || __cppcheck__
+#define NODISCARD [[nodiscard]]
+#else
+#define NODISCARD /**/
+#endif
#endif
#endif
* No mail root could be deduced from parameters and environment
*/
NOTMUCH_STATUS_NO_MAIL_ROOT,
+ /**
+ * Database is not fully opened, or has been closed
+ */
+ NOTMUCH_STATUS_CLOSED_DATABASE,
/**
* Not an actual status value. Just a way to find out how many
* valid status values there are.
* valid string. Whereas when this function returns FALSE,
* notmuch_tags_get will return NULL.
*
+ * It is acceptable to pass NULL for 'tags', in which case this
+ * function will always return FALSE.
+
* See the documentation of notmuch_message_get_tags for example code
* showing how to iterate over a notmuch_tags_t object.
*/
return NOTMUCH_STATUS_SUCCESS;
}
+static notmuch_status_t
+resolve_binding (notmuch_database_t *notmuch, const _sexp_binding_t *env, const char *name,
+ const _sexp_binding_t **out)
+{
+ for (; env; env = env->next) {
+ if (strcmp (name, env->name) == 0) {
+ *out = env;
+ return NOTMUCH_STATUS_SUCCESS;
+ }
+ }
+
+ _notmuch_database_log (notmuch, "undefined parameter '%s'\n", name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+}
+
+static notmuch_status_t
+_sexp_expand_term (notmuch_database_t *notmuch,
+ const _sexp_prefix_t *prefix,
+ const _sexp_binding_t *env,
+ const sexp_t *sx,
+ const char **out)
+{
+ notmuch_status_t status;
+
+ if (! out)
+ return NOTMUCH_STATUS_NULL_POINTER;
+
+ while (sx->ty == SEXP_VALUE && sx->aty == SEXP_BASIC && sx->val[0] == ',') {
+ const char *name = sx->val + 1;
+ const _sexp_binding_t *binding;
+
+ status = resolve_binding (notmuch, env, name, &binding);
+ if (status)
+ return status;
+
+ sx = binding->sx;
+ env = binding->context;
+ }
+
+ if (sx->ty != SEXP_VALUE) {
+ _notmuch_database_log (notmuch, "'%s' expects single atom as argument\n",
+ prefix->name);
+ return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
+ }
+
+ *out = sx->val;
+ return NOTMUCH_STATUS_SUCCESS;
+}
+
static notmuch_status_t
_sexp_parse_wildcard (notmuch_database_t *notmuch,
const _sexp_prefix_t *parent,
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)
+ const _sexp_binding_t *env,
+ const sexp_t *term, Xapian::Query &output)
{
if (! parent) {
_notmuch_database_log (notmuch, "illegal '%s' outside field\n",
}
std::string msg; /* ignored */
+ const char *str;
+ notmuch_status_t status;
+
+ status = _sexp_expand_term (notmuch, prefix, env, term, &str);
+ if (status)
+ return status;
return _notmuch_regexp_to_query (notmuch, Xapian::BAD_VALUENO, parent->name,
- val, output, msg);
+ str, output, msg);
}
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;
+ notmuch_status_t status;
+
+ const _sexp_binding_t *binding;
+
+ status = resolve_binding (notmuch, env, name, &binding);
+ if (status)
+ return status;
+
+ return _sexp_to_xapian_query (notmuch, parent, binding->context, binding->sx,
+ output);
}
static notmuch_status_t
}
from = sx->val;
+ if (strcmp (from, "*") == 0)
+ from = "";
+
to = from;
if (sx->next) {
}
to = sx->next->val;
+ if (strcmp (to, "*") == 0)
+ to = "";
}
if (strcmp (prefix->name, "date") == 0) {
long from_idx, to_idx;
try {
- from_idx = std::stol (from);
+ if (EMPTY_STRING (from))
+ from_idx = 0L;
+ else
+ from_idx = std::stol (from);
} catch (std::logic_error &e) {
_notmuch_database_log (notmuch, "bad 'from' revision: '%s'\n", from);
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
}
try {
- to_idx = std::stol (to);
+ if (EMPTY_STRING (to))
+ to_idx = LONG_MAX;
+ else
+ to_idx = std::stol (to);
} catch (std::logic_error &e) {
_notmuch_database_log (notmuch, "bad 'to' revision: '%s'\n", to);
return NOTMUCH_STATUS_BAD_QUERY_SYNTAX;
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->xapian_op == Xapian::Query::OP_WILDCARD) {
+ const char *str;
+ status = _sexp_expand_term (notmuch, prefix, env, sx->list->next, &str);
+ if (status)
+ return status;
+
+ return _sexp_parse_wildcard (notmuch, parent, env, str, output);
+ }
if (prefix->flags & SEXP_FLAG_DO_REGEX) {
- return _sexp_parse_regex (notmuch, prefix, parent, env, sx->list->next->val, output);
+ return _sexp_parse_regex (notmuch, prefix, parent, env, sx->list->next, output);
}
if (prefix->flags & SEXP_FLAG_DO_EXPAND) {
notmuch_bool_t
notmuch_tags_valid (notmuch_tags_t *tags)
{
- return tags->iterator != NULL;
+ return tags && (tags->iterator != NULL);
}
const char *
notmuch_status_t
mime_node_open (const void *ctx, notmuch_message_t *message,
+ int duplicate,
_notmuch_crypto_t *crypto, mime_node_t **root_out)
{
const char *filename = notmuch_message_get_filename (message);
mime_node_context_t *mctx;
mime_node_t *root;
notmuch_status_t status;
- int fd;
+ int fd = -1;
root = talloc_zero (ctx, mime_node_t);
if (root == NULL) {
talloc_set_destructor (mctx, _mime_node_context_free);
/* Fast path */
- fd = open (filename, O_RDONLY);
+ if (duplicate <= 0)
+ fd = open (filename, O_RDONLY);
if (fd == -1) {
- /* Slow path - for some reason the first file in the list is
- * not available anymore. This is clearly a problem in the
+ /* Slow path - Either we are trying to open a specific file, or
+ * for some reason the first file in the list is
+ * not available anymore. The latter is clearly a problem in the
* database, but we are not going to let this problem be a
* show stopper */
notmuch_filenames_t *filenames;
+ int i = 1;
+
for (filenames = notmuch_message_get_filenames (message);
notmuch_filenames_valid (filenames);
- notmuch_filenames_move_to_next (filenames)) {
- filename = notmuch_filenames_get (filenames);
- fd = open (filename, O_RDONLY);
- if (fd != -1)
- break;
+ notmuch_filenames_move_to_next (filenames), i++) {
+ if (i >= duplicate) {
+ filename = notmuch_filenames_get (filenames);
+ fd = open (filename, O_RDONLY);
+ if (fd != -1) {
+ break;
+ } else {
+ if (duplicate > 0) {
+ fprintf (stderr, "Error opening %s: %s\n", filename, strerror (errno));
+ status = NOTMUCH_STATUS_FILE_ERROR;
+ goto DONE;
+ }
+ }
+ }
}
talloc_free (filenames);
bool entire_thread;
bool omit_excluded;
bool output_body;
+ int duplicate;
int part;
_notmuch_crypto_t crypto;
bool include_html;
void
format_part_sprinter (const void *ctx, struct sprinter *sp, mime_node_t *node,
+ int duplicate,
bool output_body,
bool include_html);
};
/* Construct a new MIME node pointing to the root message part of
- * message. If crypto->verify is true, signed child parts will be
+ * message. Use the duplicate-th filename if that parameter is
+ * positive. If crypto->verify is true, signed child parts will be
* verified. If crypto->decrypt is NOTMUCH_DECRYPT_TRUE, encrypted
* child parts will be decrypted using either stored session keys or
* asymmetric crypto. If crypto->decrypt is NOTMUCH_DECRYPT_AUTO,
*/
notmuch_status_t
mime_node_open (const void *ctx, notmuch_message_t *message,
+ int duplicate,
_notmuch_crypto_t *crypto, mime_node_t **node_out);
/* Return a new MIME node for the requested child part of parent.
--- /dev/null
+#!/usr/bin/env python3
+#
+# Copyright (c) 2011-2014 David Bremner <david@tethera.net>
+# W. Trevor King <wking@tremily.us>
+#
+# This program is free software: you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation, either version 3 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful,
+# but WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+# GNU General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program. If not, see https://www.gnu.org/licenses/ .
+
+"""
+Manage notmuch tags with Git
+"""
+
+from __future__ import print_function
+from __future__ import unicode_literals
+
+import codecs as _codecs
+import collections as _collections
+import functools as _functools
+import inspect as _inspect
+import locale as _locale
+import logging as _logging
+import os as _os
+import re as _re
+import shutil as _shutil
+import subprocess as _subprocess
+import sys as _sys
+import tempfile as _tempfile
+import textwrap as _textwrap
+from urllib.parse import quote as _quote
+from urllib.parse import unquote as _unquote
+import json as _json
+
+_LOG = _logging.getLogger('notmuch-git')
+_LOG.setLevel(_logging.WARNING)
+_LOG.addHandler(_logging.StreamHandler())
+
+NOTMUCH_GIT_DIR = None
+TAG_PREFIX = None
+FORMAT_VERSION = 1
+
+_HEX_ESCAPE_REGEX = _re.compile('%[0-9A-F]{2}')
+_TAG_DIRECTORY = 'tags/'
+_TAG_FILE_REGEX = ( _re.compile(_TAG_DIRECTORY + '(?P<id>[^/]*)/(?P<tag>[^/]*)'),
+ _re.compile(_TAG_DIRECTORY + '([0-9a-f]{2}/){2}(?P<id>[^/]*)/(?P<tag>[^/]*)'))
+
+# magic hash for Git (git hash-object -t blob /dev/null)
+_EMPTYBLOB = 'e69de29bb2d1d6434b8b29ae775ad8c2e48c5391'
+
+def _hex_quote(string, safe='+@=:,'):
+ """
+ quote('abc def') -> 'abc%20def'.
+
+ Wrap urllib.parse.quote with additional safe characters (in
+ addition to letters, digits, and '_.-') and lowercase hex digits
+ (e.g. '%3a' instead of '%3A').
+ """
+ uppercase_escapes = _quote(string, safe)
+ return _HEX_ESCAPE_REGEX.sub(
+ lambda match: match.group(0).lower(),
+ uppercase_escapes)
+
+def _xapian_quote(string):
+ """
+ Quote a string for Xapian's QueryParser.
+
+ Xapian uses double-quotes for quoting strings. You can escape
+ internal quotes by repeating them [1,2,3].
+
+ [1]: https://trac.xapian.org/ticket/128#comment:2
+ [2]: https://trac.xapian.org/ticket/128#comment:17
+ [3]: https://trac.xapian.org/changeset/13823/svn
+ """
+ return '"{0}"'.format(string.replace('"', '""'))
+
+
+def _xapian_unquote(string):
+ """
+ Unquote a Xapian-quoted string.
+ """
+ if string.startswith('"') and string.endswith('"'):
+ return string[1:-1].replace('""', '"')
+ return string
+
+
+def timed(fn):
+ """Timer decorator"""
+ from time import perf_counter
+
+ def inner(*args, **kwargs):
+ start_time = perf_counter()
+ rval = fn(*args, **kwargs)
+ end_time = perf_counter()
+ _LOG.info('{0}: {1:.8f}s elapsed'.format(fn.__name__, end_time - start_time))
+ return rval
+
+ return inner
+
+
+class SubprocessError(RuntimeError):
+ "A subprocess exited with a nonzero status"
+ def __init__(self, args, status, stdout=None, stderr=None):
+ self.status = status
+ self.stdout = stdout
+ self.stderr = stderr
+ msg = '{args} exited with {status}'.format(args=args, status=status)
+ if stderr:
+ msg = '{msg}: {stderr}'.format(msg=msg, stderr=stderr)
+ super(SubprocessError, self).__init__(msg)
+
+
+class _SubprocessContextManager(object):
+ """
+ PEP 343 context manager for subprocesses.
+
+ 'expect' holds a tuple of acceptable exit codes, otherwise we'll
+ raise a SubprocessError in __exit__.
+ """
+ def __init__(self, process, args, expect=(0,)):
+ self._process = process
+ self._args = args
+ self._expect = expect
+
+ def __enter__(self):
+ return self._process
+
+ def __exit__(self, type, value, traceback):
+ for name in ['stdin', 'stdout', 'stderr']:
+ stream = getattr(self._process, name)
+ if stream:
+ stream.close()
+ setattr(self._process, name, None)
+ status = self._process.wait()
+ _LOG.debug(
+ 'collect {args} with status {status} (expected {expect})'.format(
+ args=self._args, status=status, expect=self._expect))
+ if status not in self._expect:
+ raise SubprocessError(args=self._args, status=status)
+
+ def wait(self):
+ return self._process.wait()
+
+
+def _spawn(args, input=None, additional_env=None, wait=False, stdin=None,
+ stdout=None, stderr=None, encoding=_locale.getpreferredencoding(),
+ expect=(0,), **kwargs):
+ """Spawn a subprocess, and optionally wait for it to finish.
+
+ This wrapper around subprocess.Popen has two modes, depending on
+ the truthiness of 'wait'. If 'wait' is true, we use p.communicate
+ internally to write 'input' to the subprocess's stdin and read
+ from it's stdout/stderr. If 'wait' is False, we return a
+ _SubprocessContextManager instance for fancier handling
+ (e.g. piping between processes).
+
+ For 'wait' calls when you want to write to the subprocess's stdin,
+ you only need to set 'input' to your content. When 'input' is not
+ None but 'stdin' is, we'll automatically set 'stdin' to PIPE
+ before calling Popen. This avoids having the subprocess
+ accidentally inherit the launching process's stdin.
+ """
+ _LOG.debug('spawn {args} (additional env. var.: {env})'.format(
+ args=args, env=additional_env))
+ if not stdin and input is not None:
+ stdin = _subprocess.PIPE
+ if additional_env:
+ if not kwargs.get('env'):
+ kwargs['env'] = dict(_os.environ)
+ kwargs['env'].update(additional_env)
+ p = _subprocess.Popen(
+ args, stdin=stdin, stdout=stdout, stderr=stderr, **kwargs)
+ if wait:
+ if hasattr(input, 'encode'):
+ input = input.encode(encoding)
+ (stdout, stderr) = p.communicate(input=input)
+ status = p.wait()
+ _LOG.debug(
+ 'collect {args} with status {status} (expected {expect})'.format(
+ args=args, status=status, expect=expect))
+ if stdout is not None:
+ stdout = stdout.decode(encoding)
+ if stderr is not None:
+ stderr = stderr.decode(encoding)
+ if status not in expect:
+ raise SubprocessError(
+ args=args, status=status, stdout=stdout, stderr=stderr)
+ return (status, stdout, stderr)
+ if p.stdin and not stdin:
+ p.stdin.close()
+ p.stdin = None
+ if p.stdin:
+ p.stdin = _codecs.getwriter(encoding=encoding)(stream=p.stdin)
+ stream_reader = _codecs.getreader(encoding=encoding)
+ if p.stdout:
+ p.stdout = stream_reader(stream=p.stdout)
+ if p.stderr:
+ p.stderr = stream_reader(stream=p.stderr)
+ return _SubprocessContextManager(args=args, process=p, expect=expect)
+
+
+def _git(args, **kwargs):
+ args = ['git', '--git-dir', NOTMUCH_GIT_DIR] + list(args)
+ return _spawn(args=args, **kwargs)
+
+
+def _get_current_branch():
+ """Get the name of the current branch.
+
+ Return 'None' if we're not on a branch.
+ """
+ try:
+ (status, branch, stderr) = _git(
+ args=['symbolic-ref', '--short', 'HEAD'],
+ stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, wait=True)
+ except SubprocessError as e:
+ if 'not a symbolic ref' in e:
+ return None
+ raise
+ return branch.strip()
+
+
+def _get_remote():
+ "Get the default remote for the current branch."
+ local_branch = _get_current_branch()
+ (status, remote, stderr) = _git(
+ args=['config', 'branch.{0}.remote'.format(local_branch)],
+ stdout=_subprocess.PIPE, wait=True)
+ return remote.strip()
+
+def _tag_query(prefix=None):
+ if prefix is None:
+ prefix = TAG_PREFIX
+ return '(tag (starts-with "{:s}"))'.format(prefix.replace('"','\\\"'))
+
+def count_messages(prefix=None):
+ "count messages with a given prefix."
+ (status, stdout, stderr) = _spawn(
+ args=['notmuch', 'count', '--query=sexp', _tag_query(prefix)],
+ stdout=_subprocess.PIPE, wait=True)
+ if status != 0:
+ _LOG.error("failed to run notmuch config")
+ sys.exit(1)
+ return int(stdout.rstrip())
+
+def get_tags(prefix=None):
+ "Get a list of tags with a given prefix."
+ (status, stdout, stderr) = _spawn(
+ args=['notmuch', 'search', '--query=sexp', '--output=tags', _tag_query(prefix)],
+ stdout=_subprocess.PIPE, wait=True)
+ return [tag for tag in stdout.splitlines()]
+
+def archive(treeish='HEAD', args=()):
+ """
+ Dump a tar archive of the current notmuch-git tag set.
+
+ Using 'git archive'.
+
+ Each tag $tag for message with Message-Id $id is written to
+ an empty file
+
+ tags/hash1(id)/hash2(id)/encode($id)/encode($tag)
+
+ The encoding preserves alphanumerics, and the characters
+ "+-_@=.:," (not the quotes). All other octets are replaced with
+ '%' followed by a two digit hex number.
+ """
+ _git(args=['archive', treeish] + list(args), wait=True)
+
+
+def clone(repository):
+ """
+ Create a local notmuch-git repository from a remote source.
+
+ This wraps 'git clone', adding some options to avoid creating a
+ working tree while preserving remote-tracking branches and
+ upstreams.
+ """
+ with _tempfile.TemporaryDirectory(prefix='notmuch-git-clone.') as workdir:
+ _spawn(
+ args=[
+ 'git', 'clone', '--no-checkout', '--separate-git-dir', NOTMUCH_GIT_DIR,
+ repository, workdir],
+ wait=True)
+ _git(args=['config', '--unset', 'core.worktree'], wait=True, expect=(0, 5))
+ _git(args=['config', 'core.bare', 'true'], wait=True)
+ (status, stdout, stderr) = _git(args=['show-ref', '--verify',
+ '--quiet',
+ 'refs/remotes/origin/config'],
+ expect=(0,1),
+ wait=True)
+ if status == 0:
+ _git(args=['branch', 'config', 'origin/config'], wait=True)
+ existing_tags = get_tags()
+ if existing_tags:
+ _LOG.warning(
+ 'Not checking out to avoid clobbering existing tags: {}'.format(
+ ', '.join(existing_tags)))
+ else:
+ checkout()
+
+
+def _is_committed(status):
+ return len(status['added']) + len(status['deleted']) == 0
+
+
+class CachedIndex:
+ def __init__(self, repo, treeish):
+ self.cache_path = _os.path.join(repo, 'notmuch', 'index_cache.json')
+ self.index_path = _os.path.join(repo, 'index')
+ self.current_treeish = treeish
+ # cached values
+ self.treeish = None
+ self.hash = None
+ self.index_checksum = None
+
+ self._load_cache_file()
+
+ def _load_cache_file(self):
+ try:
+ with open(self.cache_path) as f:
+ data = _json.load(f)
+ self.treeish = data['treeish']
+ self.hash = data['hash']
+ self.index_checksum = data['index_checksum']
+ except FileNotFoundError:
+ pass
+ except _json.JSONDecodeError:
+ _LOG.error("Error decoding cache")
+ _sys.exit(1)
+
+ def __enter__(self):
+ self.read_tree()
+ return self
+
+ def __exit__(self, type, value, traceback):
+ checksum = _read_index_checksum(self.index_path)
+ (_, hash, _) = _git(
+ args=['rev-parse', self.current_treeish],
+ stdout=_subprocess.PIPE,
+ wait=True)
+
+ with open(self.cache_path, "w") as f:
+ _json.dump({'treeish': self.current_treeish,
+ 'hash': hash.rstrip(), 'index_checksum': checksum }, f)
+
+ @timed
+ def read_tree(self):
+ current_checksum = _read_index_checksum(self.index_path)
+ (_, hash, _) = _git(
+ args=['rev-parse', self.current_treeish],
+ stdout=_subprocess.PIPE,
+ wait=True)
+ current_hash = hash.rstrip()
+
+ if self.current_treeish == self.treeish and \
+ self.index_checksum and self.index_checksum == current_checksum and \
+ self.hash and self.hash == current_hash:
+ return
+
+ _git(args=['read-tree', self.current_treeish], wait=True)
+
+
+def check_safe_fraction(status):
+ safe = 0.1
+ conf = _notmuch_config_get ('git.safe_fraction')
+ if conf and conf != '':
+ safe=float(conf)
+
+ total = count_messages (TAG_PREFIX)
+ if total == 0:
+ _LOG.error('No existing tags with given prefix, stopping.'.format(safe))
+ _LOG.error('Use --force to override.')
+ exit(1)
+ change = len(status['added'])+len(status['deleted'])
+ fraction = change/total
+ _LOG.debug('total messages {:d}, change: {:d}, fraction: {:f}'.format(total,change,fraction))
+ if fraction > safe:
+ _LOG.error('safe fraction {:f} exceeded, stopping.'.format(safe))
+ _LOG.error('Use --force to override or reconfigure git.safe_fraction.')
+ exit(1)
+
+def commit(treeish='HEAD', message=None, force=False):
+ """
+ Commit prefix-matching tags from the notmuch database to Git.
+ """
+
+ status = get_status()
+
+ if _is_committed(status=status):
+ _LOG.warning('Nothing to commit')
+ return
+
+ if not force:
+ check_safe_fraction (status)
+
+ with CachedIndex(NOTMUCH_GIT_DIR, treeish) as index:
+ try:
+ _update_index(status=status)
+ (_, tree, _) = _git(
+ args=['write-tree'],
+ stdout=_subprocess.PIPE,
+ wait=True)
+ (_, parent, _) = _git(
+ args=['rev-parse', treeish],
+ stdout=_subprocess.PIPE,
+ wait=True)
+ (_, commit, _) = _git(
+ args=['commit-tree', tree.strip(), '-p', parent.strip()],
+ input=message,
+ stdout=_subprocess.PIPE,
+ wait=True)
+ _git(
+ args=['update-ref', treeish, commit.strip()],
+ stdout=_subprocess.PIPE,
+ wait=True)
+ except Exception as e:
+ _git(args=['read-tree', '--empty'], wait=True)
+ _git(args=['read-tree', treeish], wait=True)
+ raise
+
+@timed
+def _update_index(status):
+ with _git(
+ args=['update-index', '--index-info'],
+ stdin=_subprocess.PIPE) as p:
+ for id, tags in status['deleted'].items():
+ for line in _index_tags_for_message(id=id, status='D', tags=tags):
+ p.stdin.write(line)
+ for id, tags in status['added'].items():
+ for line in _index_tags_for_message(id=id, status='A', tags=tags):
+ p.stdin.write(line)
+
+
+def fetch(remote=None):
+ """
+ Fetch changes from the remote repository.
+
+ See 'merge' to bring those changes into notmuch.
+ """
+ args = ['fetch']
+ if remote:
+ args.append(remote)
+ _git(args=args, wait=True)
+
+
+def init(remote=None,format_version=None):
+ """
+ Create an empty notmuch-git repository.
+
+ This wraps 'git init' with a few extra steps to support subsequent
+ status and commit commands.
+ """
+ from pathlib import Path
+ parent = Path(NOTMUCH_GIT_DIR).parent
+ try:
+ _os.makedirs(parent)
+ except FileExistsError:
+ pass
+
+ if not format_version:
+ format_version = 1
+
+ format_version=int(format_version)
+
+ if format_version > 1 or format_version < 0:
+ _LOG.error("Illegal format version {:d}".format(format_version))
+ _sys.exit(1)
+
+ _spawn(args=['git', '--git-dir', NOTMUCH_GIT_DIR, 'init',
+ '--initial-branch=master', '--quiet', '--bare'], wait=True)
+ _git(args=['config', 'core.logallrefupdates', 'true'], wait=True)
+ # create an empty blob (e69de29bb2d1d6434b8b29ae775ad8c2e48c5391)
+ _git(args=['hash-object', '-w', '--stdin'], input='', wait=True)
+ allow_empty=('--allow-empty',)
+ if format_version >= 1:
+ allow_empty=()
+ # create a blob for the FORMAT file
+ (status, stdout, _) = _git(args=['hash-object', '-w', '--stdin'], stdout=_subprocess.PIPE,
+ input='{:d}\n'.format(format_version), wait=True)
+ verhash=stdout.rstrip()
+ _LOG.debug('hash of FORMAT blob = {:s}'.format(verhash))
+ # Add FORMAT to the index
+ _git(args=['update-index', '--add', '--cacheinfo', '100644,{:s},FORMAT'.format(verhash)], wait=True)
+
+ _git(
+ args=[
+ 'commit', *allow_empty, '-m', 'Start a new notmuch-git repository'
+ ],
+ additional_env={'GIT_WORK_TREE': NOTMUCH_GIT_DIR},
+ wait=True)
+
+
+def checkout(force=None):
+ """
+ Update the notmuch database from Git.
+
+ This is mainly useful to discard your changes in notmuch relative
+ to Git.
+ """
+ status = get_status()
+
+ if not force:
+ check_safe_fraction(status)
+
+ with _spawn(
+ args=['notmuch', 'tag', '--batch'], stdin=_subprocess.PIPE) as p:
+ for id, tags in status['added'].items():
+ p.stdin.write(_batch_line(action='-', id=id, tags=tags))
+ for id, tags in status['deleted'].items():
+ p.stdin.write(_batch_line(action='+', id=id, tags=tags))
+
+
+def _batch_line(action, id, tags):
+ """
+ 'notmuch tag --batch' line for adding/removing tags.
+
+ Set 'action' to '-' to remove a tag or '+' to add the tags to a
+ given message id.
+ """
+ tag_string = ' '.join(
+ '{action}{prefix}{tag}'.format(
+ action=action, prefix=_ENCODED_TAG_PREFIX, tag=_hex_quote(tag))
+ for tag in tags)
+ line = '{tags} -- id:{id}\n'.format(
+ tags=tag_string, id=_xapian_quote(string=id))
+ return line
+
+
+def _insist_committed():
+ "Die if the the notmuch tags don't match the current HEAD."
+ status = get_status()
+ if not _is_committed(status=status):
+ _LOG.error('\n'.join([
+ 'Uncommitted changes to {prefix}* tags in notmuch',
+ '',
+ "For a summary of changes, run 'notmuch-git status'",
+ "To save your changes, run 'notmuch-git commit' before merging/pull",
+ "To discard your changes, run 'notmuch-git checkout'",
+ ]).format(prefix=TAG_PREFIX))
+ _sys.exit(1)
+
+
+def pull(repository=None, refspecs=None):
+ """
+ Pull (merge) remote repository changes to notmuch.
+
+ 'pull' is equivalent to 'fetch' followed by 'merge'. We use the
+ Git-configured repository for your current branch
+ (branch.<name>.repository, likely 'origin', and
+ branch.<name>.merge, likely 'master').
+ """
+ _insist_committed()
+ if refspecs and not repository:
+ repository = _get_remote()
+ args = ['pull']
+ if repository:
+ args.append(repository)
+ if refspecs:
+ args.extend(refspecs)
+ with _tempfile.TemporaryDirectory(prefix='notmuch-git-pull.') as workdir:
+ for command in [
+ ['reset', '--hard'],
+ args]:
+ _git(
+ args=command,
+ additional_env={'GIT_WORK_TREE': workdir},
+ wait=True)
+ checkout()
+
+
+def merge(reference='@{upstream}'):
+ """
+ Merge changes from 'reference' into HEAD and load the result into notmuch.
+
+ The default reference is '@{upstream}'.
+ """
+ _insist_committed()
+ with _tempfile.TemporaryDirectory(prefix='notmuch-git-merge.') as workdir:
+ for command in [
+ ['reset', '--hard'],
+ ['merge', reference]]:
+ _git(
+ args=command,
+ additional_env={'GIT_WORK_TREE': workdir},
+ wait=True)
+ checkout()
+
+
+def log(args=()):
+ """
+ A simple wrapper for 'git log'.
+
+ After running 'notmuch-git fetch', you can inspect the changes with
+ 'notmuch-git log HEAD..@{upstream}'.
+ """
+ # we don't want output trapping here, because we want the pager.
+ args = ['log', '--name-status', '--no-renames'] + list(args)
+ with _git(args=args, expect=(0, 1, -13)) as p:
+ p.wait()
+
+
+def push(repository=None, refspecs=None):
+ "Push the local notmuch-git Git state to a remote repository."
+ if refspecs and not repository:
+ repository = _get_remote()
+ args = ['push']
+ if repository:
+ args.append(repository)
+ if refspecs:
+ args.extend(refspecs)
+ _git(args=args, wait=True)
+
+
+def status():
+ """
+ Show pending updates in notmuch or git repo.
+
+ Prints lines of the form
+
+ ng Message-Id tag
+
+ where n is a single character representing notmuch database status
+
+ * A
+
+ Tag is present in notmuch database, but not committed to notmuch-git
+ (equivalently, tag has been deleted in notmuch-git repo, e.g. by a
+ pull, but not restored to notmuch database).
+
+ * D
+
+ Tag is present in notmuch-git repo, but not restored to notmuch
+ database (equivalently, tag has been deleted in notmuch).
+
+ * U
+
+ Message is unknown (missing from local notmuch database).
+
+ The second character (if present) represents a difference between
+ local and upstream branches. Typically 'notmuch-git fetch' needs to be
+ run to update this.
+
+ * a
+
+ Tag is present in upstream, but not in the local Git branch.
+
+ * d
+
+ Tag is present in local Git branch, but not upstream.
+ """
+ status = get_status()
+ # 'output' is a nested defaultdict for message status:
+ # * The outer dict is keyed by message id.
+ # * The inner dict is keyed by tag name.
+ # * The inner dict values are status strings (' a', 'Dd', ...).
+ output = _collections.defaultdict(
+ lambda : _collections.defaultdict(lambda : ' '))
+ for id, tags in status['added'].items():
+ for tag in tags:
+ output[id][tag] = 'A'
+ for id, tags in status['deleted'].items():
+ for tag in tags:
+ output[id][tag] = 'D'
+ for id, tags in status['missing'].items():
+ for tag in tags:
+ output[id][tag] = 'U'
+ if _is_unmerged():
+ for id, tag in _diff_refs(filter='A'):
+ output[id][tag] += 'a'
+ for id, tag in _diff_refs(filter='D'):
+ output[id][tag] += 'd'
+ for id, tag_status in sorted(output.items()):
+ for tag, status in sorted(tag_status.items()):
+ print('{status}\t{id}\t{tag}'.format(
+ status=status, id=id, tag=tag))
+
+
+def _is_unmerged(ref='@{upstream}'):
+ try:
+ (status, fetch_head, stderr) = _git(
+ args=['rev-parse', ref],
+ stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, wait=True)
+ except SubprocessError as e:
+ if 'No upstream configured' in e.stderr:
+ return
+ raise
+ (status, base, stderr) = _git(
+ args=['merge-base', 'HEAD', ref],
+ stdout=_subprocess.PIPE, wait=True)
+ return base != fetch_head
+
+class DatabaseCache:
+ def __init__(self):
+ try:
+ from notmuch2 import Database
+ self._notmuch = Database()
+ except ImportError:
+ self._notmuch = None
+ self._known = {}
+
+ def known(self,id):
+ if id in self._known:
+ return self._known[id];
+
+ if self._notmuch:
+ try:
+ _ = self._notmuch.find(id)
+ self._known[id] = True
+ except LookupError:
+ self._known[id] = False
+ else:
+ (_, stdout, stderr) = _spawn(
+ args=['notmuch', 'search', '--output=files', 'id:{0}'.format(id)],
+ stdout=_subprocess.PIPE,
+ wait=True)
+ self._known[id] = stdout != None
+ return self._known[id]
+
+@timed
+def get_status():
+ status = {
+ 'deleted': {},
+ 'missing': {},
+ }
+ db = DatabaseCache()
+ with PrivateIndex(repo=NOTMUCH_GIT_DIR, prefix=TAG_PREFIX) as index:
+ maybe_deleted = index.diff(filter='D')
+ for id, tags in maybe_deleted.items():
+ if db.known(id):
+ status['deleted'][id] = tags
+ else:
+ status['missing'][id] = tags
+ status['added'] = index.diff(filter='A')
+
+ return status
+
+class PrivateIndex:
+ def __init__(self, repo, prefix):
+ try:
+ _os.makedirs(_os.path.join(repo, 'notmuch'))
+ except FileExistsError:
+ pass
+
+ file_name = 'notmuch/index'
+ self.index_path = _os.path.join(repo, file_name)
+ self.cache_path = _os.path.join(repo, 'notmuch', '{:s}.json'.format(_hex_quote(file_name)))
+
+ self.current_prefix = prefix
+
+ self.prefix = None
+ self.uuid = None
+ self.lastmod = None
+ self.checksum = None
+ self._load_cache_file()
+ self.file_tree = None
+ self._index_tags()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, type, value, traceback):
+ checksum = _read_index_checksum(self.index_path)
+ (count, uuid, lastmod) = _read_database_lastmod()
+ with open(self.cache_path, "w") as f:
+ _json.dump({'prefix': self.current_prefix, 'uuid': uuid, 'lastmod': lastmod, 'checksum': checksum }, f)
+
+ def _load_cache_file(self):
+ try:
+ with open(self.cache_path) as f:
+ data = _json.load(f)
+ self.prefix = data['prefix']
+ self.uuid = data['uuid']
+ self.lastmod = data['lastmod']
+ self.checksum = data['checksum']
+ except FileNotFoundError:
+ return None
+ except _json.JSONDecodeError:
+ _LOG.error("Error decoding cache")
+ _sys.exit(1)
+
+ @timed
+ def _read_file_tree(self):
+ self.file_tree = {}
+
+ with _git(
+ args=['ls-files', 'tags'],
+ additional_env={'GIT_INDEX_FILE': self.index_path},
+ stdout=_subprocess.PIPE) as git:
+ for file in git.stdout:
+ dir=_os.path.dirname(file)
+ tag=_os.path.basename(file).rstrip()
+ if dir not in self.file_tree:
+ self.file_tree[dir]=[tag]
+ else:
+ self.file_tree[dir].append(tag)
+
+
+ def _clear_tags_for_message(self, id):
+ """
+ Clear any existing index entries for message 'id'
+
+ Neither 'id' nor the tags in 'tags' should be encoded/escaped.
+ """
+
+ if self.file_tree == None:
+ self._read_file_tree()
+
+ dir = _id_path(id)
+
+ if dir not in self.file_tree:
+ return
+
+ for file in self.file_tree[dir]:
+ line = '0 0000000000000000000000000000000000000000\t{:s}/{:s}\n'.format(dir,file)
+ yield line
+
+
+ @timed
+ def _index_tags(self):
+ "Write notmuch tags to private git index."
+ prefix = '+{0}'.format(_ENCODED_TAG_PREFIX)
+ current_checksum = _read_index_checksum(self.index_path)
+ if (self.prefix == None or self.prefix != self.current_prefix
+ or self.checksum == None or self.checksum != current_checksum):
+ _git(
+ args=['read-tree', '--empty'],
+ additional_env={'GIT_INDEX_FILE': self.index_path}, wait=True)
+
+ query = _tag_query()
+ clear_tags = False
+ (count,uuid,lastmod) = _read_database_lastmod()
+ if self.prefix == self.current_prefix and self.uuid \
+ and self.uuid == uuid and self.checksum == current_checksum:
+ query = '(and (infix "lastmod:{:d}..")) {:s})'.format(self.lastmod+1, query)
+ clear_tags = True
+ with _spawn(
+ args=['notmuch', 'dump', '--format=batch-tag', '--query=sexp', '--', query],
+ stdout=_subprocess.PIPE) as notmuch:
+ with _git(
+ args=['update-index', '--index-info'],
+ stdin=_subprocess.PIPE,
+ additional_env={'GIT_INDEX_FILE': self.index_path}) as git:
+ for line in notmuch.stdout:
+ if line.strip().startswith('#'):
+ continue
+ (tags_string, id) = [_.strip() for _ in line.split(' -- id:')]
+ tags = [
+ _unquote(tag[len(prefix):])
+ for tag in tags_string.split()
+ if tag.startswith(prefix)]
+ id = _xapian_unquote(string=id)
+ if clear_tags:
+ for line in self._clear_tags_for_message(id=id):
+ git.stdin.write(line)
+ for line in _index_tags_for_message(
+ id=id, status='A', tags=tags):
+ git.stdin.write(line)
+
+ @timed
+ def diff(self, filter):
+ """
+ Get an {id: {tag, ...}} dict for a given filter.
+
+ For example, use 'A' to find added tags, and 'D' to find deleted tags.
+ """
+ s = _collections.defaultdict(set)
+ with _git(
+ args=[
+ 'diff-index', '--cached', '--diff-filter', filter,
+ '--name-only', 'HEAD'],
+ additional_env={'GIT_INDEX_FILE': self.index_path},
+ stdout=_subprocess.PIPE) as p:
+ # Once we drop Python < 3.3, we can use 'yield from' here
+ for id, tag in _unpack_diff_lines(stream=p.stdout):
+ s[id].add(tag)
+ return s
+
+def _read_index_checksum (index_path):
+ """Read the index checksum, as defined by index-format.txt in the git source
+ WARNING: assumes SHA1 repo"""
+ import binascii
+ try:
+ with open(index_path, 'rb') as f:
+ size=_os.path.getsize(index_path)
+ f.seek(size-20);
+ return binascii.hexlify(f.read(20)).decode('ascii')
+ except FileNotFoundError:
+ return None
+
+def _read_database_lastmod():
+ with _spawn(
+ args=['notmuch', 'count', '--lastmod', '*'],
+ stdout=_subprocess.PIPE) as notmuch:
+ (count,uuid,lastmod_str) = notmuch.stdout.readline().split()
+ return (count,uuid,int(lastmod_str))
+
+def _id_path(id):
+ hid=_hex_quote(string=id)
+ from hashlib import blake2b
+
+ if FORMAT_VERSION==0:
+ return 'tags/{hid}'.format(hid=hid)
+ elif FORMAT_VERSION==1:
+ idhash = blake2b(hid.encode('utf8'), digest_size=2).hexdigest()
+ return 'tags/{dir1}/{dir2}/{hid}'.format(
+ hid=hid,
+ dir1=idhash[0:2],dir2=idhash[2:])
+ else:
+ _LOG.error("Unknown format version",FORMAT_VERSION)
+ _sys.exit(1)
+
+def _index_tags_for_message(id, status, tags):
+ """
+ Update the Git index to either create or delete an empty file.
+
+ Neither 'id' nor the tags in 'tags' should be encoded/escaped.
+ """
+ mode = '100644'
+ hash = _EMPTYBLOB
+
+ if status == 'D':
+ mode = '0'
+ hash = '0000000000000000000000000000000000000000'
+
+ for tag in tags:
+ path = '{ipath}/{tag}'.format(ipath=_id_path(id),tag=_hex_quote(string=tag))
+ yield '{mode} {hash}\t{path}\n'.format(mode=mode, hash=hash, path=path)
+
+
+def _diff_refs(filter, a='HEAD', b='@{upstream}'):
+ with _git(
+ args=['diff', '--diff-filter', filter, '--name-only', a, b],
+ stdout=_subprocess.PIPE) as p:
+ # Once we drop Python < 3.3, we can use 'yield from' here
+ for id, tag in _unpack_diff_lines(stream=p.stdout):
+ yield id, tag
+
+
+def _unpack_diff_lines(stream):
+ "Iterate through (id, tag) tuples in a diff stream."
+ for line in stream:
+ match = _TAG_FILE_REGEX[FORMAT_VERSION].match(line.strip())
+ if not match:
+ message = 'non-tag line in diff: {!r}'.format(line.strip())
+ if line.startswith(_TAG_DIRECTORY):
+ raise ValueError(message)
+ _LOG.info(message)
+ continue
+ id = _unquote(match.group('id'))
+ tag = _unquote(match.group('tag'))
+ yield (id, tag)
+
+
+def _help(parser, command=None):
+ """
+ Show help for an notmuch-git command.
+
+ Because some folks prefer:
+
+ $ notmuch-git help COMMAND
+
+ to
+
+ $ notmuch-git COMMAND --help
+ """
+ if command:
+ parser.parse_args([command, '--help'])
+ else:
+ parser.parse_args(['--help'])
+
+def _notmuch_config_get(key):
+ (status, stdout, stderr) = _spawn(
+ args=['notmuch', 'config', 'get', key],
+ stdout=_subprocess.PIPE, wait=True)
+ if status != 0:
+ _LOG.error("failed to run notmuch config")
+ _sys.exit(1)
+ return stdout.rstrip()
+
+def read_format_version():
+ try:
+ (status, stdout, stderr) = _git(
+ args=['cat-file', 'blob', 'master:FORMAT'],
+ stdout=_subprocess.PIPE, stderr=_subprocess.PIPE, wait=True)
+ except SubprocessError as e:
+ _LOG.debug("failed to read FORMAT file from git, assuming format version 0")
+ return 0
+
+ return int(stdout)
+
+# based on BaseDirectory.save_data_path from pyxdg (LGPL2+)
+def xdg_data_path(profile):
+ resource = _os.path.join('notmuch',profile,'git')
+ assert not resource.startswith('/')
+ _home = _os.path.expanduser('~')
+ xdg_data_home = _os.environ.get('XDG_DATA_HOME') or \
+ _os.path.join(_home, '.local', 'share')
+ path = _os.path.join(xdg_data_home, resource)
+ return path
+
+if __name__ == '__main__':
+ import argparse
+
+ parser = argparse.ArgumentParser(
+ description=__doc__.strip(),
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ parser.add_argument(
+ '-C', '--git-dir', metavar='REPO',
+ help='Git repository to operate on.')
+ parser.add_argument(
+ '-p', '--tag-prefix', metavar='PREFIX',
+ default = None,
+ help='Prefix of tags to operate on.')
+ parser.add_argument(
+ '-N', '--nmbug', action='store_true',
+ help='Set defaults for --tag-prefix and --git-dir for the notmuch bug tracker')
+ parser.add_argument(
+ '-l', '--log-level',
+ choices=['critical', 'error', 'warning', 'info', 'debug'],
+ help='Log verbosity. Defaults to {!r}.'.format(
+ _logging.getLevelName(_LOG.level).lower()))
+
+ help = _functools.partial(_help, parser=parser)
+ help.__doc__ = _help.__doc__
+ subparsers = parser.add_subparsers(
+ title='commands',
+ description=(
+ 'For help on a particular command, run: '
+ "'%(prog)s ... <command> --help'."))
+ for command in [
+ 'archive',
+ 'checkout',
+ 'clone',
+ 'commit',
+ 'fetch',
+ 'help',
+ 'init',
+ 'log',
+ 'merge',
+ 'pull',
+ 'push',
+ 'status',
+ ]:
+ func = locals()[command]
+ doc = _textwrap.dedent(func.__doc__).strip().replace('%', '%%')
+ subparser = subparsers.add_parser(
+ command,
+ help=doc.splitlines()[0],
+ description=doc,
+ formatter_class=argparse.RawDescriptionHelpFormatter)
+ subparser.set_defaults(func=func)
+ if command == 'archive':
+ subparser.add_argument(
+ 'treeish', metavar='TREE-ISH', nargs='?', default='HEAD',
+ help=(
+ 'The tree or commit to produce an archive for. Defaults '
+ "to 'HEAD'."))
+ subparser.add_argument(
+ 'args', metavar='ARG', nargs='*',
+ help=(
+ "Argument passed through to 'git archive'. Set anything "
+ 'before <tree-ish>, see git-archive(1) for details.'))
+ elif command == 'checkout':
+ subparser.add_argument(
+ '-f', '--force', action='store_true',
+ help='checkout a large fraction of tags.')
+ elif command == 'clone':
+ subparser.add_argument(
+ 'repository',
+ help=(
+ 'The (possibly remote) repository to clone from. See the '
+ 'URLS section of git-clone(1) for more information on '
+ 'specifying repositories.'))
+ elif command == 'commit':
+ subparser.add_argument(
+ '-f', '--force', action='store_true',
+ help='commit a large fraction of tags.')
+ subparser.add_argument(
+ 'message', metavar='MESSAGE', default='', nargs='?',
+ help='Text for the commit message.')
+ elif command == 'fetch':
+ subparser.add_argument(
+ 'remote', metavar='REMOTE', nargs='?',
+ help=(
+ 'Override the default configured in branch.<name>.remote '
+ 'to fetch from a particular remote repository (e.g. '
+ "'origin')."))
+ elif command == 'help':
+ subparser.add_argument(
+ 'command', metavar='COMMAND', nargs='?',
+ help='The command to show help for.')
+ elif command == 'init':
+ subparser.add_argument(
+ '--format-version', metavar='VERSION',
+ default = None,
+ help='create format VERSION repository.')
+ elif command == 'log':
+ subparser.add_argument(
+ 'args', metavar='ARG', nargs='*',
+ help="Additional argument passed through to 'git log'.")
+ elif command == 'merge':
+ subparser.add_argument(
+ 'reference', metavar='REFERENCE', default='@{upstream}',
+ nargs='?',
+ help=(
+ 'Reference, usually other branch heads, to merge into '
+ "our branch. Defaults to '@{upstream}'."))
+ elif command == 'pull':
+ subparser.add_argument(
+ 'repository', metavar='REPOSITORY', default=None, nargs='?',
+ help=(
+ 'The "remote" repository that is the source of the pull. '
+ 'This parameter can be either a URL (see the section GIT '
+ 'URLS in git-pull(1)) or the name of a remote (see the '
+ 'section REMOTES in git-pull(1)).'))
+ subparser.add_argument(
+ 'refspecs', metavar='REFSPEC', default=None, nargs='*',
+ help=(
+ 'Refspec (usually a branch name) to fetch and merge. See '
+ 'the <refspec> entry in the OPTIONS section of '
+ 'git-pull(1) for other possibilities.'))
+ elif command == 'push':
+ subparser.add_argument(
+ 'repository', metavar='REPOSITORY', default=None, nargs='?',
+ help=(
+ 'The "remote" repository that is the destination of the '
+ 'push. This parameter can be either a URL (see the '
+ 'section GIT URLS in git-push(1)) or the name of a remote '
+ '(see the section REMOTES in git-push(1)).'))
+ subparser.add_argument(
+ 'refspecs', metavar='REFSPEC', default=None, nargs='*',
+ help=(
+ 'Refspec (usually a branch name) to push. See '
+ 'the <refspec> entry in the OPTIONS section of '
+ 'git-push(1) for other possibilities.'))
+
+ args = parser.parse_args()
+
+ nmbug_mode = False
+ notmuch_profile = _os.getenv('NOTMUCH_PROFILE','default')
+
+ if args.nmbug or _os.path.basename(__file__) == 'nmbug':
+ nmbug_mode = True
+
+ if args.git_dir:
+ NOTMUCH_GIT_DIR = args.git_dir
+ else:
+ if nmbug_mode:
+ default = _os.path.join('~', '.nmbug')
+ else:
+ default = _notmuch_config_get ('git.path')
+ if default == '':
+ default = xdg_data_path(notmuch_profile)
+
+ NOTMUCH_GIT_DIR = _os.path.expanduser(_os.getenv('NOTMUCH_GIT_DIR', default))
+
+ _NOTMUCH_GIT_DIR = _os.path.join(NOTMUCH_GIT_DIR, '.git')
+ if _os.path.isdir(_NOTMUCH_GIT_DIR):
+ NOTMUCH_GIT_DIR = _NOTMUCH_GIT_DIR
+
+ if args.tag_prefix:
+ TAG_PREFIX = args.tag_prefix
+ else:
+ if nmbug_mode:
+ prefix = 'notmuch::'
+ else:
+ prefix = _notmuch_config_get ('git.tag_prefix')
+
+ TAG_PREFIX = _os.getenv('NOTMUCH_GIT_PREFIX', prefix)
+
+ _ENCODED_TAG_PREFIX = _hex_quote(TAG_PREFIX, safe='+@=,') # quote ':'
+
+ if args.log_level:
+ level = getattr(_logging, args.log_level.upper())
+ _LOG.setLevel(level)
+
+ # for test suite
+ for var in ['NOTMUCH_GIT_DIR', 'NOTMUCH_GIT_PREFIX', 'NOTMUCH_PROFILE', 'NOTMUCH_CONFIG' ]:
+ _LOG.debug('env {:s} = {:s}'.format(var, _os.getenv(var,'%None%')))
+
+ if _notmuch_config_get('built_with.sexp_queries') != 'true':
+ _LOG.error("notmuch git needs sexp query support")
+ _sys.exit(1)
+
+ if not getattr(args, 'func', None):
+ parser.print_usage()
+ _sys.exit(1)
+
+ # The following two lines are used by the test suite.
+ _LOG.debug('prefix = {:s}'.format(TAG_PREFIX))
+ _LOG.debug('repository = {:s}'.format(NOTMUCH_GIT_DIR))
+
+ if args.func != init:
+ FORMAT_VERSION = read_format_version()
+
+ _LOG.debug('FORMAT_VERSION={:d}'.format(FORMAT_VERSION))
+
+ if args.func == help:
+ arg_names = ['command']
+ else:
+ (arg_names, varargs, varkw) = _inspect.getargs(args.func.__code__)
+ kwargs = {key: getattr(args, key) for key in arg_names if key in args}
+ try:
+ args.func(**kwargs)
+ except SubprocessError as e:
+ if _LOG.level == _logging.DEBUG:
+ raise # don't mask the traceback
+ _LOG.error(str(e))
+ _sys.exit(1)
notmuch_messages_move_to_next (messages)) {
message = notmuch_messages_get (messages);
- if (mime_node_open (notmuch, message, ¶ms->crypto, &node))
+ if (mime_node_open (notmuch, message, params->duplicate, ¶ms->crypto, &node))
return 1;
reply = create_reply_message (notmuch, message,
/* Start the original */
sp->map_key (sp, "original");
- format_part_sprinter (notmuch, sp, node, true, false);
+ format_part_sprinter (notmuch, sp, node, params->duplicate, true, false);
/* End */
sp->end (sp);
int opt_index;
notmuch_show_params_t params = {
.part = -1,
+ .duplicate = 0,
.crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
};
int format = FORMAT_DEFAULT;
{ "auto", NOTMUCH_DECRYPT_AUTO },
{ "true", NOTMUCH_DECRYPT_NOSTASH },
{ 0, 0 } } },
+ { .opt_int = ¶ms.duplicate, .name = "duplicate" },
{ .opt_inherit = notmuch_shared_options },
{ }
};
#include "sprinter.h"
#include "zlib-extra.h"
+static const char *
+_get_filename (notmuch_message_t *message, int index)
+{
+ notmuch_filenames_t *filenames = notmuch_message_get_filenames (message);
+ int i = 1;
+
+ for (;
+ notmuch_filenames_valid (filenames);
+ notmuch_filenames_move_to_next (filenames), i++) {
+ if (i >= index)
+ return notmuch_filenames_get (filenames);
+ }
+ return NULL;
+}
+
static const char *
_get_tags_as_string (const void *ctx, notmuch_message_t *message)
{
void
format_part_sprinter (const void *ctx, sprinter_t *sp, mime_node_t *node,
+ int duplicate,
bool output_body,
bool include_html)
{
sp->begin_map (sp);
format_message_sprinter (sp, node->envelope_file);
+ sp->map_key (sp, "duplicate");
+ sp->integer (sp, duplicate > 0 ? duplicate : 1);
+
if (output_body) {
sp->map_key (sp, "body");
sp->begin_list (sp);
- format_part_sprinter (ctx, sp, mime_node_child (node, 0), true, include_html);
+ format_part_sprinter (ctx, sp, mime_node_child (node, 0), -1, true, include_html);
sp->end (sp);
}
}
for (i = 0; i < node->nchildren; i++)
- format_part_sprinter (ctx, sp, mime_node_child (node, i), true, include_html);
+ format_part_sprinter (ctx, sp, mime_node_child (node, i), -1, true, include_html);
/* Close content structures */
for (i = 0; i < nclose; i++)
mime_node_t *node, unused (int indent),
const notmuch_show_params_t *params)
{
- format_part_sprinter (ctx, sp, node, params->output_body, params->include_html);
+ format_part_sprinter (ctx, sp, node, params->duplicate, params->output_body,
+ params->include_html);
return NOTMUCH_STATUS_SUCCESS;
}
char buf[4096];
notmuch_status_t ret = NOTMUCH_STATUS_FILE_ERROR;
- filename = notmuch_message_get_filename (node->envelope_file);
+ filename = _get_filename (node->envelope_file, params->duplicate);
if (filename == NULL) {
fprintf (stderr, "Error: Cannot get message filename.\n");
goto DONE;
session_key_count_error = notmuch_message_count_properties (message, "session-key",
&session_keys);
- status = mime_node_open (local, message, &(params->crypto), &root);
+ status = mime_node_open (local, message, params->duplicate, &(params->crypto), &root);
if (status)
goto DONE;
part = mime_node_seek_dfs (root, (params->part < 0 ? 0 : params->part));
sprinter_t *sprinter;
notmuch_show_params_t params = {
.part = -1,
+ .duplicate = 0,
.omit_excluded = true,
.output_body = true,
.crypto = { .decrypt = NOTMUCH_DECRYPT_AUTO },
{ .opt_bool = ¶ms.crypto.verify, .name = "verify" },
{ .opt_bool = ¶ms.output_body, .name = "body" },
{ .opt_bool = ¶ms.include_html, .name = "include-html" },
+ { .opt_int = ¶ms.duplicate, .name = "duplicate" },
{ .opt_inherit = notmuch_shared_options },
{ }
};
/* specifying a part implies single message display */
single_message = params.part >= 0;
+ /* specifying a duplicate also implies single message display */
+ single_message = single_message || (params.duplicate > 0);
+
if (format == NOTMUCH_FORMAT_NOT_SPECIFIED) {
/* if part was requested and format was not specified, use format=raw */
if (params.part >= 0)
} help_topic_t;
static const help_topic_t help_topics[] = {
- { "search-terms",
- "Common search term syntax." },
{ "hooks",
"Hooks that will be run before or after certain commands." },
{ "properties",
"Message property conventions and documentation." },
+ { "search-terms",
+ "Common infix search term syntax." },
+ { "sexp-queries",
+ "Common s-expression search term syntax." },
};
static const command_t *
static int
_help_for (const char *topic_name)
{
- const command_t *command;
- const help_topic_t *topic;
- unsigned int i;
+ char *page;
if (! topic_name) {
printf ("The notmuch mail system.\n\n");
return EXIT_SUCCESS;
}
- command = find_command (topic_name);
- if (command) {
- char *page = talloc_asprintf (NULL, "notmuch-%s", command->name);
- exec_man (page);
- }
+ page = talloc_asprintf (NULL, "notmuch-%s", topic_name);
+ exec_man (page);
- for (i = 0; i < ARRAY_SIZE (help_topics); i++) {
- topic = &help_topics[i];
- if (strcmp (topic_name, topic->name) == 0) {
- char *page = talloc_asprintf (NULL, "notmuch-%s", topic->name);
- exec_man (page);
- }
- }
-
- fprintf (stderr,
- "\nSorry, %s is not a known command. There's not much I can do to help.\n\n",
- topic_name);
return EXIT_FAILURE;
}
* false on errors.
*/
static bool
-try_external_command (char *argv[])
+try_external_command (const char *config_file_name, char *argv[])
{
char *old_argv0 = argv[0];
bool ret = true;
+ if (config_file_name) {
+ if (setenv ("NOTMUCH_CONFIG", config_file_name, 1)) {
+ perror ("setenv");
+ exit (1);
+ }
+ }
+
argv[0] = talloc_asprintf (NULL, "notmuch-%s", old_argv0);
/*
/* if command->function is NULL, try external command */
if (! command || ! command->function) {
/* This won't return if the external command is found. */
- if (try_external_command (argv + opt_index))
+ if (try_external_command (config_file_name, argv + opt_index))
fprintf (stderr, "Error: Unknown command '%s' (see \"notmuch help\")\n",
command_name);
ret = EXIT_FAILURE;
}
if (status == NOTMUCH_STATUS_NO_CONFIG)
- fputs ("Try running 'notmuch setup' to create a configuration.", stderr);
+ fputs ("Try running 'notmuch setup' to create a configuration.\n", stderr);
return EXIT_FAILURE;
}
NULL,
¬much,
&status_string);
-
- if (status == NOTMUCH_STATUS_NO_CONFIG && ! (command->mode & NOTMUCH_COMMAND_CONFIG_CREATE)) {
- fputs ("Try running 'notmuch setup' to create a configuration.", stderr);
- goto DONE;
- }
switch (status) {
case NOTMUCH_STATUS_NO_CONFIG:
if (! (command->mode & NOTMUCH_COMMAND_CONFIG_CREATE)) {
- fputs ("Try running 'notmuch setup' to create a configuration.", stderr);
+ fputs ("Try running 'notmuch setup' to create a configuration.\n", stderr);
goto DONE;
}
break;
(notmuch-tag msg (list \"+test\"))
(notmuch-tag msg (list \"-test\"))))"
+time_emacs "show warmup" \
+ '(notmuch-show "thread:{id:tip-4f8219875a0dad2cfad9e93a3fafcd9626db98d2@git.kernel.org}")'
+
+time_emacs "show thread #1" \
+ '(notmuch-show "thread:{id:tip-4f8219875a0dad2cfad9e93a3fafcd9626db98d2@git.kernel.org}")'
+
+time_emacs "depth bound #1" \
+ '(let ((notmuch-show-depth-limit 0))
+ (notmuch-show "thread:{id:tip-4f8219875a0dad2cfad9e93a3fafcd9626db98d2@git.kernel.org}"))'
+
+time_emacs "height bound #1" \
+ '(let ((notmuch-show-height-limit -1))
+ (notmuch-show "thread:{id:tip-4f8219875a0dad2cfad9e93a3fafcd9626db98d2@git.kernel.org}"))'
+
+time_emacs "size bound #1" \
+ '(let ((notmuch-show-max-text-part-size 1))
+ (notmuch-show "thread:{id:tip-4f8219875a0dad2cfad9e93a3fafcd9626db98d2@git.kernel.org}"))'
+
+time_emacs "show thread #2" \
+ '(notmuch-show "thread:{id:20101208005731.943729010@clark.site}")'
+
+time_emacs "depth bound #2" \
+ '(let ((notmuch-show-depth-limit 0))
+ (notmuch-show "thread:{id:20101208005731.943729010@clark.site}"))'
+
+time_emacs "height bound #2" \
+ '(let ((notmuch-show-height-limit -1))
+ (notmuch-show "thread:{id:20101208005731.943729010@clark.site}"))'
+
+time_emacs "size bound #2" \
+ '(let ((notmuch-show-max-text-part-size 1))
+ (notmuch-show "thread:{id:20101208005731.943729010@clark.site}"))'
+
+time_emacs "show thread #3" \
+ '(notmuch-show "thread:{id:20120109014938.GE20796@mit.edu}")'
+
+time_emacs "depth bound #3" \
+ '(let ((notmuch-show-depth-limit 0))
+ (notmuch-show "thread:{id:20120109014938.GE20796@mit.edu}"))'
+
+time_emacs "height bound #3" \
+ '(let ((notmuch-show-height-limit -1))
+ (notmuch-show "thread:{id:20120109014938.GE20796@mit.edu}"))'
+
+time_emacs "size bound #3" \
+ '(let ((notmuch-show-max-text-part-size 1))
+ (notmuch-show "thread:{id:20120109014938.GE20796@mit.edu}"))'
+
+time_emacs "show thread #4" \
+ '(notmuch-show "thread:{id:1280704593.25620.48.camel@mulgrave.site}")'
+
+time_emacs "depth bound #4" \
+ '(let ((notmuch-show-depth-limit 0))
+ (notmuch-show "thread:{id:1280704593.25620.48.camel@mulgrave.site}"))'
+
+time_emacs "height bound #4" \
+ '(let ((notmuch-show-height-limit -1))
+ (notmuch-show "thread:{id:1280704593.25620.48.camel@mulgrave.site}"))'
+
+time_emacs "size bound #4" \
+ '(let ((notmuch-show-max-text-part-size 1))
+ (notmuch-show "thread:{id:1280704593.25620.48.camel@mulgrave.site}"))'
+
time_done
--- /dev/null
+#!/usr/bin/env bash
+
+test_description='notmuch-git'
+
+. $(dirname "$0")/perf-test-lib.sh || exit 1
+
+time_start
+
+time_run 'init' "notmuch git init"
+
+time_run 'commit --force' "notmuch git commit --force"
+time_run 'commit' "notmuch git -l error commit"
+time_run 'commit' "notmuch git -l error commit"
+
+time_run 'checkout' "notmuch git checkout"
+
+time_run 'tag -inbox' "notmuch tag -inbox '*'"
+
+time_run 'checkout --force' "notmuch git checkout --force"
+
+
+
+time_done
m=db.find('20091117232137.GA7669@griffis1.net')
to=m.header('To')
print(to)
+EOF
+ test_expect_equal_file EXPECTED OUTPUT
+
+ test_begin_subtest ".notmuch not ignored in split config ($config)"
+ test_subtest_known_broken
+ generate_message '[dir]=.notmuch/cur' '[subject]="Do not ignore, very important"'
+ NOTMUCH_NEW > OUTPUT
+ notmuch search subject:Do-not-ignore | notmuch_search_sanitize >> OUTPUT
+ cat <<EOF > EXPECTED
+Added 1 new message to the database.
+thread:XXX 2001-01-05 [1/1] Notmuch Test Suite; Do not ignore, very important (inbox unread)
EOF
test_expect_equal_file EXPECTED OUTPUT
;;
;;
esac
- case $config in
- split|XDG*)
- esac
restore_config
rm -rf home/.local
rm -rf home/.config
test_expect_equal "600" "$(stat -c %a "$cur_msg_filename")"
test_begin_subtest "Insert message adds default tags"
-output=$(notmuch show --format=json "subject:insert-subject")
+output=$(notmuch show --format=json "subject:insert-subject" | notmuch_json_show_sanitize)
expected='[[[{
- "id": "'"${gen_msg_id}"'",
+ "id": "XXXXX",
"crypto": {},
"match": true,
"excluded": false,
- "filename": ["'"${cur_msg_filename}"'"],
+ "filename": ["YYYYY"],
"timestamp": 946728000,
"date_relative": "2000-01-01",
"tags": ["inbox","unread"],
notmuch search --query=sexp '(and (date 2009-11-17 2009-11-18) (from keithp))' | notmuch_search_sanitize > OUTPUT
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "date query, lower bound only"
+notmuch search date:2009-11-18.. and from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp '(and (date 2009-11-18 "") (from keithp))' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "date query, upper bound only"
+notmuch search date:..2009-11-17 and from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp '(and (date "" 2009-11-17) (from keithp))' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "date query, lower bound only, using *"
+notmuch search date:2009-11-18.. and from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp '(and (date 2009-11-18 *) (from keithp))' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "date query, upper bound only, using *"
+notmuch search date:..2009-11-17 and from:keithp | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp '(and (date * 2009-11-17) (from keithp))' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
test_begin_subtest "date query, illegal nesting 1"
notmuch search --query=sexp '(to (date))' > OUTPUT 2>&1
cat <<EOF > EXPECTED
notmuch search --query=sexp "(and (lastmod $revision $revision2))" | notmuch_search_sanitize > OUTPUT
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "lastmod query, lower bound only"
+notmuch search lastmod:$revision.. | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp "(lastmod $revision \"\")" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, upper bound only"
+notmuch search lastmod:..$revision2 | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp "(lastmod \"\" $revision2)" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, lower bound only, using *"
+notmuch search lastmod:$revision.. | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp "(lastmod $revision *)" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "lastmod query, upper bound only, using *"
+notmuch search lastmod:..$revision2 | notmuch_search_sanitize > EXPECTED
+notmuch search --query=sexp "(lastmod * $revision2)" | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
test_begin_subtest "lastmod query, illegal nesting 1"
notmuch search --query=sexp '(to (lastmod))' > OUTPUT 2>&1
cat <<EOF > EXPECTED
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "Saved Search: bad parameter syntax 5"
+notmuch config set squery.Bad5 '(macro (thing) (tag (rx ,thing)))'
+notmuch search --query=sexp '(Bad5 (1 2))' >OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+'rx' expects single atom as argument
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "Saved Search: bad parameter syntax 6"
+notmuch config set squery.Bad6 '(macro (thing) (tag (starts-with ,thing)))'
+notmuch search --query=sexp '(Bad6 (1 2))' >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 "Saved Search: bad parameter syntax 7"
+notmuch search --query=sexp '(subject (rx ,unknown))' >OUTPUT 2>&1
+cat <<EOF > EXPECTED
+notmuch search: Syntax error in query
+undefined parameter 'unknown'
+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
notmuch search --query=sexp '(Bad6 foo)' >OUTPUT 2>&1
cat <<EOF > EXPECTED
notmuch search: Syntax error in query
-undefined parameter b
+undefined parameter 'b'
EOF
test_expect_equal_file EXPECTED OUTPUT
notmuch search --query=sexp '(TagSubject2 inbox maildir)' | notmuch_search_sanitize > OUTPUT
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "macro in regex"
+notmuch search tag:inbox and date:2009-11-17 | notmuch_search_sanitize > EXPECTED
+notmuch config set squery.D '(macro (tagname) (and (date 2009-11-17) (tag (rx ,tagname))))'
+notmuch search --query=sexp '(D inbo)' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "macro in wildcard"
+notmuch search tag:inbox and date:2009-11-17 | notmuch_search_sanitize > EXPECTED
+notmuch config set squery.W '(macro (tagname) (and (date 2009-11-17) (tag (starts-with ,tagname))))'
+notmuch search --query=sexp '(W inbo)' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty 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 search --query=sexp '(Outer2 inbox maildir)' > OUTPUT 2>&1
cat <<EOF > EXPECTED
notmuch search: Syntax error in query
-undefined parameter y
+undefined parameter 'y'
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "nested macros (shadowing, regex)"
+notmuch search tag:/inbo/ and subject:/Maildi/ | notmuch_search_sanitize > EXPECTED
+notmuch config set squery.Inner3 '(macro (x) (subject (rx ,x)))'
+notmuch config set squery.Outer3 '(macro (x y) (and (tag (rx ,x)) (Inner3 ,y)))'
+notmuch search --query=sexp '(Outer3 inbo Maildi)' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "nested macros (shadowing, wildcard)"
+notmuch search tag:inbox and subject:maildir | notmuch_search_sanitize > EXPECTED
+notmuch config set squery.Inner4 '(macro (x) (subject (starts-with ,x)))'
+notmuch config set squery.Outer4 '(macro (x y) (and (tag (starts-with ,x)) (Inner4 ,y)))'
+notmuch search --query=sexp '(Outer4 inbo maildi)' | notmuch_search_sanitize > OUTPUT
+test_expect_equal_file_nonempty 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
test_begin_subtest "Show message: json"
add_message "[subject]=\"json-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[bcc]=\"test_suite+bcc@notmuchmail.org\"" "[reply-to]=\"test_suite+replyto@notmuchmail.org\"" "[body]=\"json-show-message\""
-output=$(notmuch show --format=json "json-show-message")
-test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"${gen_msg_filename}\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
+output=$(notmuch show --format=json "json-show-message" | notmuch_json_show_sanitize)
+test_expect_equal_json "$output" "[[[{\"id\": \"XXXXX\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"YYYYY\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
# This should be the same output as above.
test_begin_subtest "Show message: json --body=true"
-output=$(notmuch show --format=json --body=true "json-show-message")
-test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"${gen_msg_filename}\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
+output=$(notmuch show --format=json --body=true "json-show-message" | notmuch_json_show_sanitize)
+test_expect_equal_json "$output" "[[[{\"id\": \"XXXXX\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"YYYYY\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"json-show-message\n\"}]}, []]]]"
test_begin_subtest "Show message: json --body=false"
-output=$(notmuch show --format=json --body=false "json-show-message")
-test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"${gen_msg_filename}\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}}, []]]]"
+output=$(notmuch show --format=json --body=false "json-show-message" | notmuch_json_show_sanitize)
+test_expect_equal_json "$output" "[[[{\"id\": \"XXXXX\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"YYYYY\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Bcc\": \"test_suite+bcc@notmuchmail.org\", \"Reply-To\": \"test_suite+replyto@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}}, []]]]"
test_begin_subtest "Search message: json"
add_message "[subject]=\"json-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"json-search-message\""
test_begin_subtest "Show message: json, utf-8"
add_message "[subject]=\"json-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""
-output=$(notmuch show --format=json "jsön-show-méssage")
-test_expect_equal_json "$output" "[[[{\"id\": \"${gen_msg_id}\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"${gen_msg_filename}\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"
+output=$(notmuch show --format=json "jsön-show-méssage" | notmuch_json_show_sanitize)
+test_expect_equal_json "$output" "[[[{\"id\": \"XXXXX\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"YYYYY\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\",\"unread\"], \"headers\": {\"Subject\": \"json-show-utf8-body-sübjéct\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"text/plain\", \"content\": \"jsön-show-méssage\n\"}]}, []]]]"
test_begin_subtest "Show message: json, inline attachment filename"
subject='json-show-inline-attachment-filename'
filename=$(notmuch search --output=files "id:$id")
# Get length of README after base64-encoding, minus additional newline.
attachment_length=$(( $(base64 $NOTMUCH_SRCDIR/test/README | wc -c) - 1 ))
-test_expect_equal_json "$output" "[[[{\"id\": \"$id\", \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"$filename\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"content-length\": $attachment_length, \"content-transfer-encoding\": \"base64\", \"content-disposition\": \"inline\", \"filename\": \"README\"}]}]}, []]]]"
+test_expect_equal_json "$output" "[[[{\"id\": \"$id\", \"duplicate\": 1, \"crypto\": {}, \"match\": true, \"excluded\": false, \"filename\": [\"$filename\"], \"timestamp\": 946728000, \"date_relative\": \"2000-01-01\", \"tags\": [\"inbox\"], \"headers\": {\"Subject\": \"$subject\", \"From\": \"Notmuch Test Suite <test_suite@notmuchmail.org>\", \"To\": \"test_suite@notmuchmail.org\", \"Date\": \"Sat, 01 Jan 2000 12:00:00 +0000\"}, \"body\": [{\"id\": 1, \"content-type\": \"multipart/mixed\", \"content\": [{\"id\": 2, \"content-type\": \"text/plain\", \"content\": \"This is a test message with inline attachment with a filename\"}, {\"id\": 3, \"content-type\": \"application/octet-stream\", \"content-length\": $attachment_length, \"content-transfer-encoding\": \"base64\", \"content-disposition\": \"inline\", \"filename\": \"README\"}]}]}, []]]]"
test_begin_subtest "Search message: json, utf-8"
add_message "[subject]=\"json-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
[
{
"date_relative": "2001-01-05",
+ "duplicate": 1,
"excluded": false,
"filename": [
"${MAIL_DIR}/copy1",
[
{
"date_relative": "2001-01-05",
+ "duplicate": 1,
"excluded": false,
"filename": "${MAIL_DIR}/copy1",
"headers": {
test_begin_subtest "Show message: sexp"
add_message "[subject]=\"sexp-show-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[bcc]=\"test_suite+bcc@notmuchmail.org\"" "[reply-to]=\"test_suite+replyto@notmuchmail.org\"" "[body]=\"sexp-show-message\""
-output=$(notmuch show --format=sexp "sexp-show-message")
-test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename (\"${gen_msg_filename}\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\")) :crypto () :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+output=$(notmuch show --format=sexp "sexp-show-message" | notmuch_sexp_show_sanitize)
+test_expect_equal "$output" "((((:id \"XXXXX\" :match t :excluded nil :filename (\"YYYYY\") :timestamp 42 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\")) :crypto () :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"GENERATED_DATE\")) ())))"
# This should be the same output as above.
test_begin_subtest "Show message: sexp --body=true"
-output=$(notmuch show --format=sexp --body=true "sexp-show-message")
-test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename (\"${gen_msg_filename}\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\")) :crypto () :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+output=$(notmuch show --format=sexp --body=true "sexp-show-message" | notmuch_sexp_show_sanitize)
+test_expect_equal "$output" "((((:id \"XXXXX\" :match t :excluded nil :filename (\"YYYYY\") :timestamp 42 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :body ((:id 1 :content-type \"text/plain\" :content \"sexp-show-message\n\")) :crypto () :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"GENERATED_DATE\")) ())))"
test_begin_subtest "Show message: sexp --body=false"
-output=$(notmuch show --format=sexp --body=false "sexp-show-message")
-test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename (\"${gen_msg_filename}\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :crypto () :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+output=$(notmuch show --format=sexp --body=false "sexp-show-message" | notmuch_sexp_show_sanitize)
+test_expect_equal "$output" "((((:id \"XXXXX\" :match t :excluded nil :filename (\"YYYYY\") :timestamp 42 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :crypto () :headers (:Subject \"sexp-show-subject\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Bcc \"test_suite+bcc@notmuchmail.org\" :Reply-To \"test_suite+replyto@notmuchmail.org\" :Date \"GENERATED_DATE\")) ())))"
test_begin_subtest "Search message: sexp"
add_message "[subject]=\"sexp-search-subject\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"sexp-search-message\""
-output=$(notmuch search --format=sexp "sexp-search-message" | notmuch_search_sanitize)
-test_expect_equal "$output" "((:thread \"0000000000000002\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))"
+output=$(notmuch search --format=sexp "sexp-search-message" | notmuch_sexp_search_sanitize)
+test_expect_equal "$output" "((:thread \"XXX\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-subject\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))"
test_begin_subtest "Show message: sexp, utf-8"
add_message "[subject]=\"sexp-show-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-show-méssage\""
-output=$(notmuch show --format=sexp "jsön-show-méssage")
-test_expect_equal "$output" "((((:id \"${gen_msg_id}\" :match t :excluded nil :filename (\"${gen_msg_filename}\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :body ((:id 1 :content-type \"text/plain\" :content \"jsön-show-méssage\n\")) :crypto () :headers (:Subject \"sexp-show-utf8-body-sübjéct\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+output=$(notmuch show --format=sexp "jsön-show-méssage" | notmuch_sexp_show_sanitize)
+test_expect_equal "$output" "((((:id \"XXXXX\" :match t :excluded nil :filename (\"YYYYY\") :timestamp 42 :date_relative \"2000-01-01\" :tags (\"inbox\" \"unread\") :body ((:id 1 :content-type \"text/plain\" :content \"jsön-show-méssage\n\")) :crypto () :headers (:Subject \"sexp-show-utf8-body-sübjéct\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :Date \"GENERATED_DATE\")) ())))"
test_begin_subtest "Search message: sexp, utf-8"
add_message "[subject]=\"sexp-search-utf8-body-sübjéct\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[body]=\"jsön-search-méssage\""
-output=$(notmuch search --format=sexp "jsön-search-méssage" | notmuch_search_sanitize)
-test_expect_equal "$output" "((:thread \"0000000000000004\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-utf8-body-sübjéct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))"
+output=$(notmuch search --format=sexp "jsön-search-méssage" | notmuch_sexp_search_sanitize)
+test_expect_equal "$output" "((:thread \"XXX\" :timestamp 946728000 :date_relative \"2000-01-01\" :matched 1 :total 1 :authors \"Notmuch Test Suite\" :subject \"sexp-search-utf8-body-sübjéct\" :query (\"id:$gen_msg_id\" nil) :tags (\"inbox\" \"unread\")))"
test_begin_subtest "Show message: sexp, inline attachment filename"
subject='sexp-show-inline-attachment-filename'
filename=$(notmuch search --output=files "id:$id")
# Get length of README after base64-encoding, minus additional newline.
attachment_length=$(( $(base64 $NOTMUCH_SRCDIR/test/README | wc -c) - 1 ))
-test_expect_equal "$output" "((((:id \"$id\" :match t :excluded nil :filename (\"$filename\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\") :body ((:id 1 :content-type \"multipart/mixed\" :content ((:id 2 :content-type \"text/plain\" :content \"This is a test message with inline attachment with a filename\") (:id 3 :content-type \"application/octet-stream\" :content-disposition \"inline\" :filename \"README\" :content-transfer-encoding \"base64\" :content-length $attachment_length)))) :crypto () :headers (:Subject \"sexp-show-inline-attachment-filename\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"test_suite@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
+test_expect_equal "$output" "((((:id \"$id\" :match t :excluded nil :filename (\"$filename\") :timestamp 946728000 :date_relative \"2000-01-01\" :tags (\"inbox\") :duplicate 1 :body ((:id 1 :content-type \"multipart/mixed\" :content ((:id 2 :content-type \"text/plain\" :content \"This is a test message with inline attachment with a filename\") (:id 3 :content-type \"application/octet-stream\" :content-disposition \"inline\" :filename \"README\" :content-transfer-encoding \"base64\" :content-length $attachment_length)))) :crypto () :headers (:Subject \"sexp-show-inline-attachment-filename\" :From \"Notmuch Test Suite <test_suite@notmuchmail.org>\" :To \"test_suite@notmuchmail.org\" :Date \"Sat, 01 Jan 2000 12:00:00 +0000\")) ())))"
test_begin_subtest "show extra headers"
add_message "[subject]=\"extra-headers\"" "[date]=\"Sat, 01 Jan 2000 12:00:00 -0000\"" "[in-reply-to]=\"<parent@notmuch-test-suite>\"" "[body]=\"extra-headers test\""\
for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)\"" \
notmuch config set show.extra_headers "in-reply-to;received"
-notmuch show --format=sexp --body=false id:${gen_msg_id} | \
- notmuch_dir_sanitize | sed 's/msg-[0-9]*/MSG/g'> OUTPUT
+notmuch show --format=sexp --body=false id:${gen_msg_id} | notmuch_sexp_show_sanitize > OUTPUT
cat <<EOF > EXPECTED
-((((:id "MSG@notmuch-test-suite" :match t :excluded nil :filename ("MAIL_DIR/MSG") :timestamp 946728000 :date_relative "2000-01-01" :tags ("inbox" "unread") :crypto () :headers (:Subject "extra-headers" :From "Notmuch Test Suite <test_suite@notmuchmail.org>" :To "Notmuch Test Suite <test_suite@notmuchmail.org>" :Date "Sat, 01 Jan 2000 12:00:00 +0000" :In-Reply-To "<parent@notmuch-test-suite>" :Received "from mail.example.com (mail.example.com [1.1.1.1])\011by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\011for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)")) ())))
+((((:id "XXXXX" :match t :excluded nil :filename ("YYYYY") :timestamp 42 :date_relative "2000-01-01" :tags ("inbox" "unread") :crypto () :headers (:Subject "extra-headers" :From "Notmuch Test Suite <test_suite@notmuchmail.org>" :To "Notmuch Test Suite <test_suite@notmuchmail.org>" :Date "GENERATED_DATE" :In-Reply-To "<parent@notmuch-test-suite>" :Received "from mail.example.com (mail.example.com [1.1.1.1])\011by mail.notmuchmail.org (some MTA) with ESMTP id 12345678\011for <test_suite_other@notmuchmail.org>; Sat, 10 Apr 2010 07:54:51 -0400 (EDT)")) ())))
EOF
test_expect_equal_file EXPECTED OUTPUT
test_expect_success "notmuch show --format=text --part=8 'id:87liy5ap00.fsf@yoom.home.cworth.org'"
test_begin_subtest "--format=json --part=0, full message"
-notmuch show --format=json --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+notmuch show --format=json --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' | notmuch_json_show_sanitize >OUTPUT
cat <<EOF >EXPECTED
-{"id": "87liy5ap00.fsf@yoom.home.cworth.org", "crypto": {}, "match": true, "excluded": false, "filename": ["${MAIL_DIR}/multipart"], "timestamp": 978709437, "date_relative": "2001-01-05", "tags": ["attachment","inbox","signed","unread"], "headers": {"Subject": "Multipart message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:43:57 +0000"}, "body": [
+{"id": "XXXXX", "crypto": {}, "match": true, "excluded": false, "filename": ["YYYYY"], "timestamp": 42, "date_relative": "2001-01-05", "tags": ["attachment","inbox","signed","unread"], "headers": {"Subject": "Multipart message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "GENERATED_DATE"}, "body": [
{"id": 1, "content-type": "multipart/signed", "content": [
{"id": 2, "content-type": "multipart/mixed", "content": [
-{"id": 3, "content-type": "message/rfc822", "content-disposition": "inline", "content": [{"headers": {"Subject": "html message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "Fri, 05 Jan 2001 15:42:57 +0000"}, "body": [
+{"id": 3, "content-type": "message/rfc822", "content-disposition": "inline", "content": [{"headers": {"Subject": "html message", "From": "Carl Worth <cworth@cworth.org>", "To": "cworth@cworth.org", "Date": "GENERATED_DATE"}, "body": [
{"id": 4, "content-type": "multipart/alternative", "content": [
-{"id": 5, "content-type": "text/html", "content-length": 71},
+{"id": 5, "content-type": "text/html", "content-length": "NONZERO"},
{"id": 6, "content-type": "text/plain", "content": "This is an embedded message, with a multipart/alternative part.\n"}]}]}]},
{"id": 7, "content-type": "text/plain", "content-disposition": "attachment", "filename": "attachment", "content": "This is a text attachment.\n"},
{"id": 8, "content-type": "text/plain", "content": "And this message is signed.\n\n-Carl\n"}]},
-{"id": 9, "content-type": "application/pgp-signature", "content-length": 197}]}]}
+{"id": 9, "content-type": "application/pgp-signature", "content-length": "NONZERO"}]}]}
EOF
test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED)"
test_expect_equal_file "${MAIL_DIR}"/multipart OUTPUT
test_begin_subtest "--format=raw --part=0, full message"
-notmuch show --format=raw --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' >OUTPUT
+notmuch show --format=raw --part=0 'id:87liy5ap00.fsf@yoom.home.cworth.org' | notmuch_json_show_sanitize >OUTPUT
test_expect_equal_file "${MAIL_DIR}"/multipart OUTPUT
test_begin_subtest "--format=raw --part=1, message body"
cat_expected_head () {
cat <<EOF
-[[[{"id": "htmlmessage", "match":true, "excluded": false, "date_relative":"2000-01-01",
+[[[{"id": "XXXXX", "match":true, "excluded": false, "date_relative":"2000-01-01",
"crypto": {},
"timestamp": 946684800,
- "filename": ["${MAIL_DIR}/include-html"],
+ "filename": ["YYYYY"],
"tags": ["inbox", "unread"],
"headers": { "Date": "Sat, 01 Jan 2000 00:00:00 +0000", "From": "A <a@example.com>",
"Subject": "html message", "To": "B <b@example.com>"},
cat_expected_head > EXPECTED.nohtml
cat <<EOF >> EXPECTED.nohtml
"content": [
- { "id": 2, "content-charset": "UTF-8", "content-length": 21, "content-type": "text/html"},
- { "id": 3, "content-charset": "ISO-8859-1", "content-length": 20, "content-type": "text/html"},
+ { "id": 2, "content-charset": "UTF-8", "content-length": "NONZERO", "content-type": "text/html"},
+ { "id": 3, "content-charset": "ISO-8859-1", "content-length": "NONZERO", "content-type": "text/html"},
{ "id": 4, "content-type": "text/plain", "content": "0.5 equals \\u00bd\\n"}
]}]},[]]]]
EOF
EOF
test_begin_subtest "html parts excluded by default"
-notmuch show --format=json id:htmlmessage > OUTPUT
+notmuch show --format=json id:htmlmessage | notmuch_json_show_sanitize > OUTPUT
test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED.nohtml)"
test_begin_subtest "html parts included"
-notmuch show --format=json --include-html id:htmlmessage > OUTPUT
+notmuch show --format=json --include-html id:htmlmessage | notmuch_json_show_sanitize > OUTPUT
test_expect_equal_json "$(cat OUTPUT)" "$(cat EXPECTED.withhtml)"
test_begin_subtest "indexes mime-type #1"
test_expect_success "notmuch show --format=raw subject:$size > /dev/null"
done
+add_email_corpus duplicate
+ID=87r2ecrr6x.fsf@zephyr.silentflame.com
+test_begin_subtest "raw content, duplicate files"
+rm -f OUTPUT.raw
+for dup in {1..5}; do
+ notmuch show --format=raw --duplicate=${dup} --format=raw id:${ID} | md5sum | cut -f1 -d' ' >> OUTPUT.raw
+done
+sort OUTPUT.raw > OUTPUT
+notmuch search --output=files id:${ID} | xargs md5sum | cut -f1 -d ' ' | sort > EXPECTED
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
test_done
OK"
test_begin_subtest "Reply with RFC 2047-encoded headers (JSON)"
-output=$(echo '{"answer":' && notmuch reply --format=json id:${gen_msg_id} 2>&1 && echo ', "success": "OK"}')
+output=$(echo '{"answer":' && notmuch reply --format=json id:${gen_msg_id} 2>&1 | notmuch_json_show_sanitize \
+ && echo ', "success": "OK"}')
test_expect_equal_json "$output" '
{ "answer": {
"original": {
"crypto": {},
"date_relative": "2010-01-05",
"excluded": false,
- "filename": ["'${MAIL_DIR}'/msg-015"],
+ "filename": ["YYYYY"],
"headers": {
"Date": "Tue, 05 Jan 2010 15:43:56 +0000",
"From": "\u2603 <snowman@example.com>",
"Subject": "\u00e0\u00df\u00e7",
"To": "Notmuch Test Suite <test_suite@notmuchmail.org>"
},
- "id": "'${gen_msg_id}'",
+ "id": "XXXXX",
"match": false,
"tags": [
"inbox",
> Note the Cc: and cc: headers.
OK"
+add_email_corpus duplicate
+
+ID1=debian/2.6.1.dfsg-4-1-g87ea161@87ea161e851dfb1ea324af00e4ecfccc18875e15
+
+test_begin_subtest "format json, --duplicate=2, duplicate key"
+output=$(notmuch reply --format=json --duplicate=2 id:${ID1})
+test_json_nodes <<<"$output" "dup:['original']['duplicate']=2"
+
+test_begin_subtest "format json, subject, --duplicate=1"
+output=$(notmuch reply --format=json --duplicate=1 id:${ID1})
+file=$(notmuch search --output=files id:${ID1} | head -n 1)
+subject=$(sed -n 's/^Subject: \(.*\)$/\1/p' < $file)
+test_json_nodes <<<"$output" "subject:['reply-headers']['Subject']=\"Re: $subject\""
+
+test_begin_subtest "format json, subject, --duplicate=2"
+output=$(notmuch reply --format=json --duplicate=2 id:${ID1})
+file=$(notmuch search --output=files id:${ID1} | tail -n 1)
+subject=$(sed -n 's/^Subject: \(.*\)$/\1/p' < $file)
+test_json_nodes <<<"$output" "subject:['reply-headers']['Subject']=\"Re: $subject\""
+
+ID2=87r2geywh9.fsf@tethera.net
+for dup in {1..2}; do
+ test_begin_subtest "format json, body, --duplicate=${dup}"
+ output=$(notmuch reply --format=json --duplicate=${dup} id:${ID2} | \
+ $NOTMUCH_PYTHON -B "$NOTMUCH_SRCDIR"/test/json_check_nodes.py "body:['original']['body'][0]['content']" | \
+ grep '^# body')
+ test_expect_equal "$output" "# body ${dup}"
+done
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+for dup in {1..5}; do
+ test_begin_subtest "format json, --duplicate=${dup}, 'duplicate' key"
+ output=$(notmuch reply --format=json --duplicate=${dup} id:${ID3})
+ test_json_nodes <<<"$output" "dup:['original']['duplicate']=${dup}"
+done
+
test_done
echo "${TOKEN}" > ${2}
}
+create_printenv_hook () {
+ mkdir -p ${HOOK_DIR}
+ cat <<EOF >"${HOOK_DIR}/${1}"
+#!/bin/sh
+printenv "${2}" > "${3}"
+EOF
+ chmod +x "${HOOK_DIR}/${1}"
+}
+
create_write_hook () {
local TOKEN="${RANDOM}"
mkdir -p ${HOOK_DIR}
# create maildir structure for notmuch-insert
mkdir -p "$MAIL_DIR"/{cur,new,tmp}
+ORIG_NOTMUCH_CONFIG=${NOTMUCH_CONFIG}
for config in traditional profile explicit relative XDG split; do
unset NOTMUCH_PROFILE
+ export NOTMUCH_CONFIG=${ORIG_NOTMUCH_CONFIG}
+ EXPECTED_CONFIG=${NOTMUCH_CONFIG}
notmuch config set database.hook_dir
notmuch config set database.path ${MAIL_DIR}
case $config in
dir=${HOME}/.config/notmuch/other
mkdir -p ${dir}
HOOK_DIR=${dir}/hooks
- cp ${NOTMUCH_CONFIG} ${dir}/config
+ EXPECTED_CONFIG=${dir}/config
+ cp ${NOTMUCH_CONFIG} ${EXPECTED_CONFIG}
export NOTMUCH_PROFILE=other
+ unset NOTMUCH_CONFIG
;;
explicit)
HOOK_DIR=${HOME}/.notmuch-hooks
EOF
test_expect_equal_file EXPECTED OUTPUT
+ test_begin_subtest "NOTMUCH_CONFIG is set"
+ create_printenv_hook "pre-new" NOTMUCH_CONFIG OUTPUT
+ NOTMUCH_NEW
+ cat <<EOF > EXPECTED
+${EXPECTED_CONFIG}
+EOF
+ test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+ test_begin_subtest "NOTMUCH_CONFIG is set by --config"
+ create_printenv_hook "pre-new" NOTMUCH_CONFIG OUTPUT
+ cp "${EXPECTED_CONFIG}" "${EXPECTED_CONFIG}.alternate"
+ notmuch --config "${EXPECTED_CONFIG}.alternate" new
+ cat <<EOF > EXPECTED
+${EXPECTED_CONFIG}.alternate
+EOF
+ test_expect_equal_file_nonempty EXPECTED OUTPUT
+
rm -rf ${HOOK_DIR}
done
test_done
--- /dev/null
+#!/usr/bin/env bash
+test_description='hooks'
+. $(dirname "$0")/test-lib.sh || exit 1
+
+create_echo_script () {
+ local TOKEN="${RANDOM}"
+ mkdir -p ${BIN_DIR}
+ cat <<EOF >"${BIN_DIR}/${1}"
+#!/bin/sh
+echo "${TOKEN}" > ${3}
+EOF
+ chmod +x "${BIN_DIR}/${1}"
+ echo "${TOKEN}" > ${2}
+}
+
+create_printenv_script () {
+ mkdir -p ${BIN_DIR}
+ cat <<EOF >"${BIN_DIR}/${1}"
+#!/bin/sh
+printenv "${2}" > "${3}"
+EOF
+ chmod +x "${BIN_DIR}/${1}"
+}
+
+# add a message to generate mail dir and database
+add_message
+
+BIN_DIR=`pwd`/bin
+PATH=$BIN_DIR:$PATH
+
+test_begin_subtest "'notmuch foo' runs notmuch-foo"
+rm -rf ${BIN_DIR}
+create_echo_script "notmuch-foo" EXPECTED OUTPUT $HOOK_DIR
+notmuch foo
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+create_printenv_script "notmuch-printenv" NOTMUCH_CONFIG OUTPUT
+
+test_begin_subtest "NOTMUCH_CONFIG is set"
+notmuch printenv
+cat <<EOF > EXPECTED
+${NOTMUCH_CONFIG}
+EOF
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "NOTMUCH_CONFIG is set by --config"
+cp "${NOTMUCH_CONFIG}" "${NOTMUCH_CONFIG}.alternate"
+cat <<EOF > EXPECTED
+${NOTMUCH_CONFIG}.alternate
+EOF
+notmuch --config "${NOTMUCH_CONFIG}.alternate" printenv
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_done
(test-visible-output))'
test_expect_equal_file $EXPECTED/notmuch-show-process-crypto-mime-parts-on OUTPUT
+test_begin_subtest "notmuch-search-show-thread returns non-nil on success"
+test_emacs_expect_t '(notmuch-search "id:20091117203301.GV3165@dottiness.seas.harvard.edu")
+ (notmuch-test-wait)
+ (and (notmuch-search-show-thread)
+ (not (notmuch-show-next-thread)))'
+
+test_begin_subtest "notmuch-search-show-thread returns nil when there are no messages"
+test_emacs_expect_t '(notmuch-search "id:non-existing-id")
+ (notmuch-test-wait)
+ (not (notmuch-search-show-thread))'
+
test_begin_subtest "notmuch-show: don't elide non-matching messages"
test_emacs '(let ((notmuch-show-only-matching-messages nil))
(notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
(test-visible-output))'
test_expect_equal_file $EXPECTED/notmuch-show-elide-non-matching-messages-on OUTPUT
+test_begin_subtest "Hide bodies of messages by depth"
+test_emacs '(let ((notmuch-show-depth-limit -1))
+ (notmuch-search "thread:{id:87ocn0qh6d.fsf@yoom.home.cworth.org}")
+ (notmuch-test-wait)
+ (notmuch-search-show-thread)
+ (notmuch-test-wait)
+ (test-visible-output))'
+test_expect_equal_file $EXPECTED/notmuch-show-depth OUTPUT
+
+
+test_begin_subtest "Hide bodies of messages by height"
+test_emacs '(let ((notmuch-show-height-limit -1))
+ (notmuch-search "thread:{id:87ocn0qh6d.fsf@yoom.home.cworth.org}")
+ (notmuch-test-wait)
+ (notmuch-search-show-thread)
+ (notmuch-test-wait)
+ (test-visible-output))'
+# folding all messages by height or depth should look the same
+test_expect_equal_file $EXPECTED/notmuch-show-depth OUTPUT
+
+test_begin_subtest "Hide bodies of messages; show only leaves."
+test_emacs '(let ((notmuch-show-height-limit 0))
+ (notmuch-search "thread:{id:87ocn0qh6d.fsf@yoom.home.cworth.org}")
+ (notmuch-test-wait)
+ (notmuch-search-show-thread)
+ (notmuch-test-wait)
+ (test-visible-output))'
+test_expect_equal_file $EXPECTED/notmuch-show-height-0 OUTPUT
+
+test_begin_subtest "Hide bodies of messages (depth > 1)"
+test_emacs '(let ((notmuch-show-depth-limit 1))
+ (notmuch-search "thread:{id:87ocn0qh6d.fsf@yoom.home.cworth.org}")
+ (notmuch-test-wait)
+ (notmuch-search-show-thread)
+ (notmuch-test-wait)
+ (test-visible-output))'
+test_expect_equal_file $EXPECTED/notmuch-show-depth-1 OUTPUT
+
+test_begin_subtest "Hide bodies of messages by size"
+test_emacs '(let ((notmuch-show-max-text-part-size 1))
+ (notmuch-search "thread:{id:87ocn0qh6d.fsf@yoom.home.cworth.org}")
+ (notmuch-test-wait)
+ (notmuch-search-show-thread)
+ (notmuch-test-wait)
+ (test-visible-output))'
+test_expect_equal_file $EXPECTED/notmuch-show-size OUTPUT
+
+test_begin_subtest "Hide bodies of messages by size > 450"
+test_emacs '(let ((notmuch-show-max-text-part-size 450))
+ (notmuch-search "thread:{id:87ocn0qh6d.fsf@yoom.home.cworth.org}")
+ (notmuch-test-wait)
+ (notmuch-search-show-thread)
+ (notmuch-test-wait)
+ (test-visible-output))'
+test_expect_equal_file $EXPECTED/notmuch-show-size-450 OUTPUT
+
test_begin_subtest "notmuch-show: elide non-matching messages (w/ notmuch-show-toggle-elide-non-matching)"
test_emacs '(let ((notmuch-show-only-matching-messages nil))
(notmuch-search "from:lars@seas.harvard.edu and subject:\"Maildir storage\"")
output=$(head -1 OUTPUT.raw|cut -f1-4 -d' ')
test_expect_equal "$output" "Notmuch Test Suite <test_suite@notmuchmail.org>"
+test_begin_subtest "multipart/alternative hides html by default"
+test_emacs '(notmuch-show "id:cf0c4d610911171136h1713aa59w9cf9aa31f052ad0a@mail.gmail.com")
+ (test-visible-output)'
+test_expect_equal_file $EXPECTED/notmuch-show-multipart-alternative OUTPUT
# switching to the crypto corpus, using gpg from here on:
add_gnupg_home
(notmuch-show \"$tid\")))"
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
+add_email_corpus attachment
+
+test_begin_subtest "tar not inlined by default"
+test_emacs '(notmuch-show "id:874llc2bkp.fsf@curie.anarc.at")
+ (test-visible-output "OUTPUT")'
+cat <<EOF > EXPECTED
+Antoine Beaupré <anarcat@orangeseeds.org> (2018-03-19) (attachment inbox)
+Subject: Re: bug: "no top level messages" crash on Zen email loops
+To: David Bremner <david@tethera.net>, notmuch@notmuchmail.org
+Date: Mon, 19 Mar 2018 13:56:54 -0400
+
+[ multipart/mixed ]
+[ text/plain ]
+And obviously I forget the frigging attachment.
+[ zendesk-email-loop2.tgz: application/x-gtar-compressed ]
+[ text/plain ]
+
+PS: don't we have a "you forgot to actually attach the damn file" plugin
+when we detect the word "attachment" and there's no attach? :p
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "tar not inlined by default on refresh"
+test_emacs '(notmuch-show "id:874llc2bkp.fsf@curie.anarc.at")
+ (notmuch-show-refresh-view)
+ (test-visible-output "OUTPUT")'
+cat <<EOF > EXPECTED
+Antoine Beaupré <anarcat@orangeseeds.org> (2018-03-19) (attachment inbox)
+Subject: Re: bug: "no top level messages" crash on Zen email loops
+To: David Bremner <david@tethera.net>, notmuch@notmuchmail.org
+Date: Mon, 19 Mar 2018 13:56:54 -0400
+
+[ multipart/mixed ]
+[ text/plain ]
+And obviously I forget the frigging attachment.
+[ zendesk-email-loop2.tgz: application/x-gtar-compressed ]
+[ text/plain ]
+
+PS: don't we have a "you forgot to actually attach the damn file" plugin
+when we detect the word "attachment" and there's no attach? :p
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+add_email_corpus duplicate
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+test_begin_subtest "duplicate=3, subject"
+test_emacs "(notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 3)
+ (test-visible-output \"OUTPUT\")"
+output=$(grep "Subject:" OUTPUT)
+file=$(notmuch search --output=files id:${ID3} | head -n 3 | tail -n 1)
+subject=$(grep '^Subject:' $file)
+test_expect_equal "$output" "$subject"
+
+FILE3=$(notmuch search --output=files --duplicate=3 "id:${ID3}")
+test_begin_subtest "duplicate=3, stash"
+test_emacs_expect_t \
+ "(notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 3)
+ (notmuch-show-stash-filename)
+ (notmuch-test-expect-equal (list (car kill-ring)) (list \"${FILE3}\"))"
+
+test_begin_subtest "duplicate=0"
+test_emacs "(test-log-error
+ (notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 0))"
+cat <<EOF > EXPECTED
+(error Duplicate 0 out of range [1,5])
+EOF
+test_expect_equal_file EXPECTED MESSAGES
+
+test_begin_subtest "duplicate=1000"
+test_emacs "(test-log-error
+ (notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 1000))"
+cat <<EOF > EXPECTED
+(error Duplicate 1000 out of range [1,5])
+EOF
+test_expect_equal_file EXPECTED MESSAGES
+test_begin_subtest "duplicate=4"
+test_emacs "(notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 4)
+ (test-visible-output \"OUTPUT\")"
+test_expect_equal_file_nonempty $EXPECTED/notmuch-show-duplicate-4 OUTPUT
+
+FILE4=$(notmuch search --output=files --duplicate=4 "id:${ID3}")
+test_begin_subtest "duplicate=4, raw"
+test_emacs "(notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 4)
+ (notmuch-show-view-raw-message)
+ (test-visible-output \"OUTPUT\")"
+subject4=$(grep '^Subject:' $FILE4)
+subject=$(grep '^Subject:' OUTPUT)
+test_expect_equal "$subject4" "$subject"
+
test_done
--- /dev/null
+#!/usr/bin/env bash
+
+test_description="emacs reply"
+. $(dirname "$0")/test-lib.sh || exit 1
+. $NOTMUCH_SRCDIR/test/test-lib-emacs.sh || exit 1
+
+EXPECTED=$NOTMUCH_SRCDIR/test/emacs-reply.expected-output
+
+test_require_emacs
+
+add_email_corpus attachment
+
+test_begin_subtest "tar not inlined by default"
+test_emacs '(notmuch-mua-new-reply "id:874llc2bkp.fsf@curie.anarc.at")
+ (test-visible-output "OUTPUT.raw")'
+cat <<EOF > EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Antoine Beaupré <anarcat@orangeseeds.org>
+Subject: Re: bug: "no top level messages" crash on Zen email loops
+In-Reply-To: <874llc2bkp.fsf@curie.anarc.at>
+Fcc: MAIL_DIR/sent
+--text follows this line--
+Antoine Beaupré <anarcat@orangeseeds.org> writes:
+
+> And obviously I forget the frigging attachment.
+>
+>
+> PS: don't we have a "you forgot to actually attach the damn file" plugin
+> when we detect the word "attachment" and there's no attach? :p
+EOF
+notmuch_dir_sanitize < OUTPUT.raw > OUTPUT
+test_expect_equal_file EXPECTED OUTPUT
+
+add_email_corpus duplicate
+
+ID2=87r2geywh9.fsf@tethera.net
+for dup in {1..2}; do
+ test_begin_subtest "body, duplicate=${dup}"
+ test_emacs "(notmuch-show \"id:${ID2}\")
+ (notmuch-test-wait)
+ (notmuch-show-choose-duplicate $dup)
+ (notmuch-test-wait)
+ (notmuch-show-reply)
+ (test-visible-output \"OUTPUT.raw\")"
+ output=$(grep '^> # body' OUTPUT.raw)
+ test_expect_equal "$output" "> # body ${dup}"
+done
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+test_begin_subtest "duplicate=3, subject"
+test_emacs "(notmuch-show \"id:${ID3}\")
+ (notmuch-test-wait)
+ (notmuch-show-choose-duplicate 3)
+ (notmuch-test-wait)
+ (notmuch-show-reply)
+ (test-visible-output \"OUTPUT\")"
+output=$(sed -n 's/^Subject: //p' OUTPUT)
+file=$(notmuch search --output=files id:${ID3} | head -n 3 | tail -n 1)
+subject=$(sed -n 's/^Subject: //p' $file)
+test_expect_equal "$output" "Re: $subject"
+
+test_begin_subtest "duplicate=4"
+test_emacs "(notmuch-show \"id:${ID3}\")
+ (notmuch-show-choose-duplicate 4)
+ (notmuch-test-wait)
+ (notmuch-show-reply)
+ (test-visible-output \"OUTPUT.raw\")"
+notmuch_dir_sanitize < OUTPUT.raw > OUTPUT
+test_expect_equal_file_nonempty $EXPECTED/notmuch-reply-duplicate-4 OUTPUT
+
+test_done
--- /dev/null
+#!/usr/bin/env bash
+
+test_description="message-dont-reply-to-names in emacs replies"
+. $(dirname "$0")/test-lib.sh || exit 1
+. $NOTMUCH_SRCDIR/test/test-lib-emacs.sh || exit 1
+
+EXPECTED=$NOTMUCH_SRCDIR/test/emacs-show.expected-output
+
+test_require_emacs
+
+add_email_corpus default
+
+test_begin_subtest "regular expression"
+test_emacs '(let ((message-dont-reply-to-names "notmuchmail\\|noreply\\|harvard"))
+ (notmuch-mua-new-reply
+ "id:20091117203301.GV3165@dottiness.seas.harvard.edu" nil t)
+ (test-visible-output "OUTPUT-FULL.raw"))'
+
+notmuch_dir_sanitize < OUTPUT-FULL.raw > OUTPUT-FULL
+head -6 OUTPUT-FULL > OUTPUT
+
+cat <<EOF > EXPECTED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Mikhail Gusarov <dottedmag@dottedmag.net>
+Subject: Re: [notmuch] Working with Maildir storage?
+In-Reply-To: <20091117203301.GV3165@dottiness.seas.harvard.edu>
+Fcc: MAIL_DIR/sent
+--text follows this line--
+EOF
+
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "predicate"
+test_emacs '(let ((message-dont-reply-to-names
+ (lambda (m) (string-prefix-p "Mikhail" m))))
+ (notmuch-mua-new-reply
+ "id:20091117203301.GV3165@dottiness.seas.harvard.edu" nil t)
+ (test-visible-output "OUTPUT-FULL-PRED.raw"))'
+
+notmuch_dir_sanitize < OUTPUT-FULL-PRED.raw > OUTPUT-FULL-PRED
+head -7 OUTPUT-FULL-PRED > OUTPUT-PRED
+
+cat <<EOF > EXPECTED-PRED
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Lars Kellogg-Stedman <lars@seas.harvard.edu>
+Cc: notmuch@notmuchmail.org
+Subject: Re: [notmuch] Working with Maildir storage?
+In-Reply-To: <20091117203301.GV3165@dottiness.seas.harvard.edu>
+Fcc: MAIL_DIR/sent
+--text follows this line--
+EOF
+
+test_expect_equal_file EXPECTED-PRED OUTPUT-PRED
+
+test_begin_subtest "nil value"
+test_emacs '(let ((message-dont-reply-to-names nil))
+ (notmuch-mua-new-reply
+ "id:20091117203301.GV3165@dottiness.seas.harvard.edu" nil t)
+ (test-visible-output "OUTPUT-FULL-NIL.raw"))'
+
+notmuch_dir_sanitize < OUTPUT-FULL-NIL.raw > OUTPUT-FULL-NIL
+head -7 OUTPUT-FULL-NIL > OUTPUT-NIL
+
+cat <<EOF > EXPECTED-NIL
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Lars Kellogg-Stedman <lars@seas.harvard.edu>, Mikhail Gusarov <dottedmag@dottedmag.net>
+Cc: notmuch@notmuchmail.org
+Subject: Re: [notmuch] Working with Maildir storage?
+In-Reply-To: <20091117203301.GV3165@dottiness.seas.harvard.edu>
+Fcc: MAIL_DIR/sent
+--text follows this line--
+EOF
+
+test_expect_equal_file EXPECTED-NIL OUTPUT-NIL
+
+test_done
(notmuch-tree "*")))'
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
+add_email_corpus duplicate
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+test_begin_subtest "duplicate=3, subject"
+test_emacs "(notmuch-tree \"id:${ID3}\")
+ (notmuch-test-wait)
+ (notmuch-tree-show-message t)
+ (notmuch-show-choose-duplicate 3)
+ (test-visible-output \"OUTPUT\")"
+output=$(grep "Subject:" OUTPUT)
+file=$(notmuch search --output=files id:${ID3} | head -n 3 | tail -n 1)
+subject=$(grep '^Subject:' $file)
+test_expect_equal "$output" "$subject"
+
+test_begin_subtest "duplicate=4"
+test_emacs "(notmuch-show \"id:${ID3}\")
+ (notmuch-test-wait)
+ (notmuch-tree-show-message t)
+ (notmuch-show-choose-duplicate 4)
+ (test-visible-output \"OUTPUT\")"
+test_expect_equal_file_nonempty $NOTMUCH_SRCDIR/test/emacs-show.expected-output/notmuch-show-duplicate-4 OUTPUT
+
test_done
(notmuch-unthreaded "*")))'
test_expect_equal "$(cat MESSAGES)" "COMPLETE"
+add_email_corpus duplicate
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+test_begin_subtest "duplicate=3, subject"
+test_emacs "(let ((notmuch-tree-show-out t))
+ (notmuch-unthreaded \"id:${ID3}\")
+ (notmuch-test-wait)
+ (notmuch-tree-show-message nil)
+ (notmuch-show-choose-duplicate 3)
+ (test-visible-output \"OUTPUT\"))"
+output=$(grep "Subject:" OUTPUT)
+file=$(notmuch search --output=files id:${ID3} | head -n 3 | tail -n 1)
+subject=$(grep '^Subject:' $file)
+test_expect_equal "$output" "$subject"
+
+test_begin_subtest "duplicate=4"
+test_emacs "(let ((notmuch-tree-show-out t))
+ (notmuch-unthreaded \"id:${ID3}\")
+ (notmuch-test-wait)
+ (notmuch-tree-show-message nil)
+ (notmuch-show-choose-duplicate 4)
+ (test-visible-output \"OUTPUT\"))"
+test_expect_equal_file_nonempty $NOTMUCH_SRCDIR/test/emacs-show.expected-output/notmuch-show-duplicate-4 OUTPUT
+
+
test_done
fi
+add_email_corpus duplicate
+
+ID1=debian/2.6.1.dfsg-4-1-g87ea161@87ea161e851dfb1ea324af00e4ecfccc18875e15
+
+test_begin_subtest "format json, --duplicate=2, duplicate key"
+output=$(notmuch show --format=json --duplicate=2 id:${ID1})
+test_json_nodes <<<"$output" "dup:['duplicate']=2"
+
+test_begin_subtest "format json, subject, --duplicate=1"
+output=$(notmuch show --format=json --duplicate=1 id:${ID1})
+file=$(notmuch search --output=files id:${ID1} | head -n 1)
+subject=$(sed -n 's/^Subject: \(.*\)$/\1/p' < $file)
+test_json_nodes <<<"$output" "subject:['headers']['Subject']=\"$subject\""
+
+test_begin_subtest "format json, subject, --duplicate=2"
+output=$(notmuch show --format=json --duplicate=2 id:${ID1})
+file=$(notmuch search --output=files id:${ID1} | tail -n 1)
+subject=$(sed -n 's/^Subject: \(.*\)$/\1/p' < $file)
+test_json_nodes <<<"$output" "subject:['headers']['Subject']=\"$subject\""
+
+ID2=87r2geywh9.fsf@tethera.net
+for dup in {1..2}; do
+ test_begin_subtest "format json, body, --duplicate=${dup}"
+ output=$(notmuch show --format=json --duplicate=${dup} id:${ID2} | \
+ $NOTMUCH_PYTHON -B "$NOTMUCH_SRCDIR"/test/json_check_nodes.py "body:['body'][0]['content']" | \
+ grep '^# body')
+ test_expect_equal "$output" "# body ${dup}"
+done
+
+ID3=87r2ecrr6x.fsf@zephyr.silentflame.com
+for dup in {1..5}; do
+ test_begin_subtest "format json, --duplicate=${dup}, 'duplicate' key"
+ output=$(notmuch show --format=json --duplicate=${dup} id:${ID3})
+ test_json_nodes <<<"$output" "dup:['duplicate']=${dup}"
+done
+
test_done
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_open (NULL, 0, 0);
+ char* msg = NULL;
+ stat = notmuch_database_open_with_config (NULL,
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
}
EOF
cat <<'EOF' >EXPECTED
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_open ("./nonexistent/foo", 0, 0);
+ char *msg = NULL;
+ stat = notmuch_database_open_with_config ("./nonexistent/foo",
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
}
EOF
cat <<'EOF' >EXPECTED
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_create ("./nonexistent/foo", &db);
+ char *msg = NULL;
+
+ stat = notmuch_database_create_with_config ("./nonexistent/foo", "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
}
EOF
cat <<'EOF' >EXPECTED
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_open (argv[1], 0, 0);
+ char* msg = NULL;
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
}
EOF
cat <<'EOF' >EXPECTED
int main (int argc, char** argv)
{
notmuch_status_t stat;
- stat = notmuch_database_create (NULL, NULL);
+ char *msg;
+
+ stat = notmuch_database_create_with_config (NULL, "", NULL, NULL, &msg);
+ if (msg) fputs (msg, stderr);
}
EOF
cat <<'EOF' >EXPECTED
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_create (argv[1], &db);
+ char *msg;
+
+ stat = notmuch_database_create_with_config (argv[1], "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
}
EOF
cat <<'EOF' >EXPECTED
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, &db);
+ char* msg = NULL;
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d\n", stat);
}
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ "", NULL, &db, NULL);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d\n", stat);
}
char *msg = NULL;
int fd;
- stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
exit (1);
test_expect_equal_file EXPECTED OUTPUT.clean
restore_database
-backup_database
-test_begin_subtest "Xapian exception getting tags"
-cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${POSTLIST_PATH}
- {
- notmuch_tags_t *tags = NULL;
- tags = notmuch_database_get_all_tags (db);
- stat = (tags == NULL);
- }
-EOF
-sed 's/^\(A Xapian exception [^:]*\):.*$/\1/' < OUTPUT > OUTPUT.clean
-cat <<'EOF' >EXPECTED
-== stdout ==
-== stderr ==
-A Xapian exception occurred getting tags
-EOF
-test_expect_equal_file EXPECTED OUTPUT.clean
-restore_database
-
backup_database
test_begin_subtest "Xapian exception creating directory"
cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${POSTLIST_PATH}
notmuch_status_t stat = NOTMUCH_STATUS_SUCCESS;
char *msg = NULL;
- stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
exit (1);
const char *path = talloc_asprintf(db, "%s/01:2,", argv[1]);
EXPECT0(notmuch_database_close (db));
stat = notmuch_database_index_file (db, path, NULL, &msg);
- printf ("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf ("%d\n", stat == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
== stdout ==
1
== stderr ==
-A Xapian exception occurred finding message: Database has been closed.
+Cannot write to a closed database.
EOF
test_expect_equal_file EXPECTED OUTPUT
{
EXPECT0(notmuch_database_close (db));
stat = notmuch_database_set_config (db, "foo", "bar");
- printf("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf("%d\n", stat == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
== stdout ==
1
== stderr ==
-Error: A Xapian exception occurred setting metadata: Database has been closed
+Cannot write to a closed database.
EOF
test_expect_equal_file EXPECTED OUTPUT
notmuch_status_t stat = NOTMUCH_STATUS_SUCCESS;
char *msg = NULL;
- stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
exit (1);
cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
{
stat = notmuch_directory_delete (dir);
- printf ("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf ("%d\n", stat == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
== stdout ==
1
== stderr ==
-A Xapian exception occurred deleting directory entry: Database has been closed.
+Cannot write to a closed database.
EOF
test_expect_equal_file EXPECTED OUTPUT
restore_database
{
time_t stamp = notmuch_directory_get_mtime (dir);
stat = notmuch_directory_set_mtime (dir, stamp);
- printf ("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf ("%d\n", stat == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
== stdout ==
1
== stderr ==
-A Xapian exception occurred setting directory mtime: Database has been closed.
-EOF
-test_expect_equal_file EXPECTED OUTPUT
-restore_database
-
-backup_database
-test_begin_subtest "get/set mtime of directory for a closed db"
-cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
- {
- time_t stamp = notmuch_directory_get_mtime (dir);
- stat = notmuch_directory_set_mtime (dir, stamp);
- printf ("%d\n", stat == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
- }
-EOF
-cat <<EOF > EXPECTED
-== stdout ==
-1
-== stderr ==
-A Xapian exception occurred setting directory mtime: Database has been closed.
+Cannot write to a closed database.
EOF
test_expect_equal_file EXPECTED OUTPUT
restore_database
notmuch_status_t stat;
char *msg = NULL;
- stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
exit (1);
--- /dev/null
+#!/usr/bin/env bash
+test_description="API tests for tags"
+
+. $(dirname "$0")/test-lib.sh || exit 1
+
+add_email_corpus
+
+test_begin_subtest "building database"
+test_expect_success "NOTMUCH_NEW"
+
+cat <<EOF > c_head
+#include <stdio.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+#include <talloc.h>
+#include <notmuch.h>
+
+int main (int argc, char** argv)
+{
+ notmuch_database_t *db;
+ notmuch_status_t stat;
+ char *path;
+ char *msg = NULL;
+ int fd;
+
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
+ if (stat != NOTMUCH_STATUS_SUCCESS) {
+ fprintf (stderr, "error opening database\n%s\n%s\n", notmuch_status_to_string (stat), msg ? msg : "");
+ exit (1);
+ }
+EOF
+cat <<'EOF' > c_tail
+ if (stat) {
+ const char *stat_str = notmuch_database_status_string (db);
+ if (stat_str)
+ fputs (stat_str, stderr);
+ }
+
+}
+EOF
+
+POSTLIST_PATH=(${MAIL_DIR}/.notmuch/xapian/postlist.*)
+
+backup_database
+test_begin_subtest "Xapian exception getting tags"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR} ${POSTLIST_PATH}
+ {
+ notmuch_tags_t *tags = NULL;
+ fd = open(argv[2],O_WRONLY|O_TRUNC);
+ if (fd < 0) {
+ fprintf (stderr, "error opening %s\n", argv[1]);
+ exit (1);
+ }
+ tags = notmuch_database_get_all_tags (db);
+ stat = (tags == NULL);
+ }
+EOF
+sed 's/^\(A Xapian exception [^:]*\):.*$/\1/' < OUTPUT > OUTPUT.clean
+cat <<'EOF' >EXPECTED
+== stdout ==
+== stderr ==
+A Xapian exception occurred getting tags
+EOF
+test_expect_equal_file EXPECTED OUTPUT.clean
+restore_database
+
+test_begin_subtest "NULL tags are not valid"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+ {
+ notmuch_bool_t valid = TRUE;
+ valid = notmuch_tags_valid (NULL);
+ fprintf(stdout, "valid = %d\n", valid);
+ }
+EOF
+cat <<'EOF' >EXPECTED
+== stdout ==
+valid = 0
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_done
notmuch_status_t stat;
char *msg = NULL;
notmuch_message_t *message = NULL;
- const char *id = "1258471718-6781-1-git-send-email-dottedmag@dottedmag.net";
+ const char *id = "87pr7gqidx.fsf@yoom.home.cworth.org";
- stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
exit (1);
EOF
cat <<EOF > EXPECTED
== stdout ==
-1258471718-6781-1-git-send-email-dottedmag@dottedmag.net
+87pr7gqidx.fsf@yoom.home.cworth.org
1
== stderr ==
EOF
EOF
cat <<EOF > EXPECTED
== stdout ==
-MAIL_DIR/01:2,
+MAIL_DIR/cur/40:2,
SUCCESS
== stderr ==
EOF
{
notmuch_status_t status;
status = notmuch_message_add_tag (message, "boom");
- printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
{
notmuch_status_t status;
status = notmuch_message_remove_tag (message, "boom");
- printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
EOF
test_expect_equal_file EXPECTED OUTPUT
+test_begin_subtest "_notmuch_message_add_term catches exceptions"
+cat c_head0 - c_tail <<'EOF' | test_private_C ${MAIL_DIR}
+ {
+ notmuch_private_status_t status;
+ /* This relies on Xapian throwing an exception for adding empty terms */
+ status = _notmuch_message_add_term (message, "body", "");
+ printf("%d\n%d\n", message != NULL, status != NOTMUCH_STATUS_SUCCESS );
+ }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "_notmuch_message_remove_term catches exceptions"
+cat c_head0 - c_tail <<'EOF' | test_private_C ${MAIL_DIR}
+ {
+ notmuch_private_status_t status;
+ /* Xapian throws the same exception for empty and non-existent terms;
+ * error string varies between Xapian versions. */
+ status = _notmuch_message_remove_term (message, "tag", "nonexistent");
+ printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_SUCCESS );
+ }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "_notmuch_message_add_filename on closed db"
+cat c_head - c_tail <<'EOF' | test_private_C ${MAIL_DIR}
+ {
+ notmuch_private_status_t status;
+ status = _notmuch_message_add_filename (message, "some-filename");
+ printf("%d\n%d\n", message != NULL, status != NOTMUCH_STATUS_SUCCESS);
+ }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "_notmuch_message_remove_filename on closed db"
+cat c_head - c_tail <<'EOF' | test_private_C ${MAIL_DIR}
+ {
+ notmuch_private_status_t status;
+ status = _notmuch_message_remove_filename (message, "some-filename");
+ printf("%d\n%d\n", message != NULL, status != NOTMUCH_STATUS_SUCCESS);
+ }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "Handle converting tags to maildir flags with closed db"
+cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
+ {
+ notmuch_status_t status;
+ status = notmuch_message_tags_to_maildir_flags (message);
+ printf("%d\n%d\n", message != NULL, status != NOTMUCH_STATUS_SUCCESS);
+ }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+1
+== stderr ==
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+POSTLIST_PATH=(${MAIL_DIR}/.notmuch/xapian/postlist.*)
+test_begin_subtest "Handle converting tags to maildir flags with corrupted db"
+backup_database
+cat c_head0 - c_tail <<'EOF' | test_C ${MAIL_DIR} ${POSTLIST_PATH}
+ {
+ notmuch_status_t status;
+
+ status = notmuch_message_add_tag (message, "draft");
+ if (status) exit(1);
+
+ int fd = open(argv[2],O_WRONLY|O_TRUNC);
+ if (fd < 0) {
+ fprintf (stderr, "error opening %s\n", argv[1]);
+ exit (1);
+ }
+
+ status = notmuch_message_tags_to_maildir_flags (message);
+ printf("%d\n%d\n", message != NULL, status != NOTMUCH_STATUS_SUCCESS);
+ }
+EOF
+cat <<EOF > EXPECTED
+== stdout ==
+1
+1
+== stderr ==
+EOF
+restore_database
+notmuch new
+notmuch tag -draft id:87pr7gqidx.fsf@yoom.home.cworth.org
+test_expect_equal_file EXPECTED OUTPUT
+
test_begin_subtest "Handle removing all tags with closed db"
cat c_head - c_tail <<'EOF' | test_C ${MAIL_DIR}
{
notmuch_status_t status;
status = notmuch_message_remove_all_tags (message);
- printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_XAPIAN_EXCEPTION);
+ printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
{
notmuch_status_t status;
status = notmuch_message_freeze (message);
- printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_SUCCESS);
+ printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
{
notmuch_status_t status;
status = notmuch_message_thaw (message);
- printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_UNBALANCED_FREEZE_THAW);
+ printf("%d\n%d\n", message != NULL, status == NOTMUCH_STATUS_CLOSED_DATABASE);
}
EOF
cat <<EOF > EXPECTED
notmuch_query_t *query = NULL;
const char *id = "${THREAD}";
- stat = notmuch_database_open_verbose (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db, &msg);
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ NULL, NULL, &db, &msg);
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d %s\n", stat, msg ? msg : "");
exit (1);
unsigned long rev;
- stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, &db);
+ char* msg = NULL;
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
+
if (stat)
fputs ("open failed\n", stderr);
revision = notmuch_database_get_revision (db, &uuid);
notmuch_message_t *message = NULL;
const char *val;
notmuch_status_t stat;
+ char* msg = NULL;
- EXPECT0(notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+ EXPECT0(notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ "", NULL, &db, &msg));
+ if (msg) fputs (msg, stderr);
EXPECT0(notmuch_database_find_message(db, "4EFC743A.3060609@april.org", &message));
if (message == NULL) {
fprintf (stderr, "unable to find message");
if (child == 0) {
notmuch_database_t *db2;
+ char* msg = NULL;
sleep (1);
- EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &db2));
+
+ EXPECT0(notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ "", NULL, &db2, &msg));
+ if (msg) fputs (msg, stderr);
+
taggit (db2, "child");
EXPECT0 (notmuch_database_close (db2));
} else {
notmuch_database_t *db;
+ char* msg = NULL;
- EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &db));
+ EXPECT0(notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ "", NULL, &db, &msg));
+ if (msg) fputs (msg, stderr);
taggit (db, "parent");
sleep (2);
EXPECT0 (notmuch_database_close (db));
notmuch_query_t *query;
notmuch_tags_t *tags;
int i;
+ char* msg = NULL;
- EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_ONLY, &ro_db));
+ EXPECT0(notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &ro_db, &msg));
+ if (msg) fputs (msg, stderr);
assert(ro_db);
EXPECT0 (notmuch_database_find_message (ro_db, "${first_id}", &ro_message));
assert(ro_message);
- EXPECT0 (notmuch_database_open (path, NOTMUCH_DATABASE_MODE_READ_WRITE, &rw_db));
+ EXPECT0(notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_WRITE,
+ "", NULL, &rw_db, &msg));
+ if (msg) fputs (msg, stderr);
+
query = notmuch_query_create(rw_db, "");
EXPECT0 (notmuch_query_search_messages (query, &messages));
{
notmuch_database_t *db;
notmuch_status_t stat;
- stat = notmuch_database_open (argv[1], NOTMUCH_DATABASE_MODE_READ_ONLY, &db);
+ char* msg = NULL;
+
+ stat = notmuch_database_open_with_config (argv[1],
+ NOTMUCH_DATABASE_MODE_READ_ONLY,
+ "", NULL, &db, &msg);
+ if (msg) fputs (msg, stderr);
+
if (stat != NOTMUCH_STATUS_SUCCESS) {
fprintf (stderr, "error opening database: %d\n", stat);
exit (1);
--- /dev/null
+#!/usr/bin/env bash
+test_description='"notmuch git" to save and restore tags'
+. $(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
+
+# be very careful using backup_database / restore_database in this
+# file, as they fool the cache invalidation checks in notmuch-git.
+
+add_email_corpus
+
+git config --global user.email notmuch@example.org
+git config --global user.name "Notmuch Test Suite"
+
+test_begin_subtest "init"
+test_expect_success "notmuch git -p '' -C remote.git init"
+
+test_begin_subtest "init (git.path)"
+notmuch config set git.path configured.git
+notmuch git init
+notmuch config set git.path
+output=$(git -C configured.git rev-parse --is-bare-repository)
+test_expect_equal "$output" "true"
+
+test_begin_subtest "clone"
+test_expect_success "notmuch git -p '' -C tags.git clone remote.git"
+
+test_begin_subtest "initial commit needs force"
+test_expect_code 1 "notmuch git -C tags.git commit"
+
+test_begin_subtest "committing new prefix requires force"
+notmuch git -C force-prefix.git init
+notmuch tag +new-prefix::foo id:20091117190054.GU3165@dottiness.seas.harvard.edu
+test_expect_code 1 "notmuch git -l debug -p 'new-prefix::' -C force-prefix.git commit"
+notmuch tag -new-prefix::foo id:20091117190054.GU3165@dottiness.seas.harvard.edu
+
+test_begin_subtest "committing new prefix works with force"
+notmuch tag +new-prefix::foo id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -l debug -p 'new-prefix::' -C force-prefix.git commit --force
+git -C force-prefix.git ls-tree -r --name-only HEAD | notmuch_git_sanitize | xargs dirname | sort -u > OUTPUT
+notmuch tag -new-prefix::foo id:20091117190054.GU3165@dottiness.seas.harvard.edu
+cat <<EOF>EXPECTED
+20091117190054.GU3165@dottiness.seas.harvard.edu
+EOF
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "checkout new prefix requires force"
+test_expect_code 1 "notmuch git -l debug -p 'new-prefix::' -C force-prefix.git checkout"
+
+test_begin_subtest "checkout new prefix works with force"
+notmuch dump > BEFORE
+notmuch git -l debug -p 'new-prefix::' -C force-prefix.git checkout --force
+notmuch dump --include=tags id:20091117190054.GU3165@dottiness.seas.harvard.edu | grep -v '^#' > OUTPUT
+notmuch restore < BEFORE
+cat <<EOF > EXPECTED
++inbox +new-prefix%3a%3afoo +signed +unread -- id:20091117190054.GU3165@dottiness.seas.harvard.edu
+EOF
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "commit"
+notmuch git -C tags.git commit --force
+git -C tags.git ls-tree -r --name-only HEAD | notmuch_git_sanitize | xargs dirname | sort -u > OUTPUT
+notmuch search --output=messages '*' | sed s/^id:// | sort > EXPECTED
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "commit --force succeeds"
+notmuch git -C force.git init
+test_expect_success "notmuch git -C force.git commit --force"
+
+test_begin_subtest "changing git.safe_fraction succeeds"
+notmuch config set git.safe_fraction 1
+notmuch git -C force2.git init
+test_expect_success "notmuch git -C force2.git commit"
+notmuch config set git.safe_fraction
+
+test_begin_subtest "commit, with quoted tag"
+notmuch git -C clone2.git clone tags.git
+git -C clone2.git ls-tree -r --name-only HEAD | grep /inbox > BEFORE
+notmuch tag '+"quoted tag"' '*'
+notmuch git -C clone2.git commit
+notmuch tag '-"quoted tag"' '*'
+git -C clone2.git ls-tree -r --name-only HEAD | grep /inbox > AFTER
+test_expect_equal_file_nonempty BEFORE AFTER
+
+test_begin_subtest "commit (incremental)"
+notmuch tag +test id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git commit
+git -C tags.git ls-tree -r --name-only HEAD | notmuch_git_sanitize | \
+ grep 20091117190054 | sort > OUTPUT
+echo "--------------------------------------------------" >> OUTPUT
+notmuch tag -test id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git commit
+git -C tags.git ls-tree -r --name-only HEAD | notmuch_git_sanitize | \
+ grep 20091117190054 | sort >> OUTPUT
+cat <<EOF > EXPECTED
+20091117190054.GU3165@dottiness.seas.harvard.edu/inbox
+20091117190054.GU3165@dottiness.seas.harvard.edu/signed
+20091117190054.GU3165@dottiness.seas.harvard.edu/test
+20091117190054.GU3165@dottiness.seas.harvard.edu/unread
+--------------------------------------------------
+20091117190054.GU3165@dottiness.seas.harvard.edu/inbox
+20091117190054.GU3165@dottiness.seas.harvard.edu/signed
+20091117190054.GU3165@dottiness.seas.harvard.edu/unread
+EOF
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+test_begin_subtest "commit (change prefix)"
+notmuch tag +test::one id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git -p 'test::' commit --force
+git -C tags.git ls-tree -r --name-only HEAD |
+ grep 20091117190054 | notmuch_git_sanitize | sort > OUTPUT
+echo "--------------------------------------------------" >> OUTPUT
+notmuch tag -test::one id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git commit --force
+git -C tags.git ls-tree -r --name-only HEAD | notmuch_git_sanitize | \
+ grep 20091117190054 | sort >> OUTPUT
+cat <<EOF > EXPECTED
+20091117190054.GU3165@dottiness.seas.harvard.edu/one
+--------------------------------------------------
+20091117190054.GU3165@dottiness.seas.harvard.edu/inbox
+20091117190054.GU3165@dottiness.seas.harvard.edu/signed
+20091117190054.GU3165@dottiness.seas.harvard.edu/unread
+EOF
+test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+backup_database
+test_begin_subtest "large checkout needs --force"
+notmuch tag -inbox '*'
+test_expect_code 1 "notmuch git -C tags.git checkout"
+restore_database
+
+test_begin_subtest "checkout (git.safe_fraction)"
+notmuch git -C force3.git clone tags.git
+notmuch dump > BEFORE
+notmuch tag -inbox '*'
+notmuch config set git.safe_fraction 1
+notmuch git -C force3.git checkout
+notmuch config set git.safe_fraction
+notmuch dump > AFTER
+test_expect_equal_file_nonempty BEFORE AFTER
+
+test_begin_subtest "checkout"
+notmuch dump > BEFORE
+notmuch tag -inbox '*'
+notmuch git -C tags.git checkout --force
+notmuch dump > AFTER
+test_expect_equal_file_nonempty BEFORE AFTER
+
+test_begin_subtest "archive"
+notmuch git -C tags.git archive | tar tf - | \
+ grep 20091117190054.GU3165@dottiness.seas.harvard.edu | notmuch_git_sanitize | sort > OUTPUT
+cat <<EOF > EXPECTED
+20091117190054.GU3165@dottiness.seas.harvard.edu/
+20091117190054.GU3165@dottiness.seas.harvard.edu/inbox
+20091117190054.GU3165@dottiness.seas.harvard.edu/signed
+20091117190054.GU3165@dottiness.seas.harvard.edu/unread
+EOF
+notmuch git -C tags.git checkout
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "status"
+notmuch tag +test id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git status > OUTPUT
+cat <<EOF > EXPECTED
+A 20091117190054.GU3165@dottiness.seas.harvard.edu test
+EOF
+notmuch git -C tags.git checkout
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "status (global config argument)"
+cp notmuch-config notmuch-config.new
+notmuch --config=notmuch-config.new config set git.path tags.git
+notmuch tag +test id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch --config=./notmuch-config.new git status > OUTPUT
+cat <<EOF > EXPECTED
+A 20091117190054.GU3165@dottiness.seas.harvard.edu test
+EOF
+notmuch --config=notmuch-config.new git checkout
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "fetch"
+notmuch tag +test2 id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C remote.git commit --force
+notmuch tag -test2 id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git fetch
+notmuch git -C tags.git status > OUTPUT
+cat <<EOF > EXPECTED
+ a 20091117190054.GU3165@dottiness.seas.harvard.edu test2
+EOF
+notmuch git -C tags.git checkout
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "merge"
+notmuch git -C tags.git merge
+notmuch dump id:20091117190054.GU3165@dottiness.seas.harvard.edu | grep -v '^#' > OUTPUT
+cat <<EOF > EXPECTED
++inbox +signed +test2 +unread -- id:20091117190054.GU3165@dottiness.seas.harvard.edu
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "push"
+notmuch tag +test3 id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git commit
+notmuch tag -test3 id:20091117190054.GU3165@dottiness.seas.harvard.edu
+notmuch git -C tags.git push
+notmuch git -C remote.git checkout
+notmuch dump id:20091117190054.GU3165@dottiness.seas.harvard.edu | grep -v '^#' > OUTPUT
+cat <<EOF > EXPECTED
++inbox +signed +test2 +test3 +unread -- id:20091117190054.GU3165@dottiness.seas.harvard.edu
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "environment passed through when run as 'notmuch git'"
+env NOTMUCH_GIT_DIR=foo NOTMUCH_GIT_PREFIX=bar NOTMUCH_PROFILE=default notmuch git -C tags.git -p '' -ldebug status |& \
+ grep '^env ' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+env NOTMUCH_GIT_DIR = foo
+env NOTMUCH_GIT_PREFIX = bar
+env NOTMUCH_PROFILE = default
+env NOTMUCH_CONFIG = CWD/notmuch-config
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "--nmbug argument sets defaults"
+notmuch git -ldebug --nmbug status |& grep '^\(prefix\|repository\)' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+prefix = notmuch::
+repository = CWD/home/.nmbug
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "invoke as nmbug sets defaults"
+"$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^\(prefix\|repository\)' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+prefix = notmuch::
+repository = CWD/home/.nmbug
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_DIR works when invoked as nmbug"
+NOTMUCH_GIT_DIR=`pwd`/foo "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+repository = CWD/foo
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_DIR works when invoked as 'notmuch git'"
+NOTMUCH_GIT_DIR=`pwd`/remote.git notmuch git -ldebug status |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+repository = CWD/remote.git
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+
+test_begin_subtest "env variable NOTMUCH_GIT_DIR overrides config when invoked as 'nmbug'"
+notmuch config set git.path `pwd`/bar
+NOTMUCH_GIT_DIR=`pwd`/remote.git "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+notmuch config set git.path
+cat <<EOF > EXPECTED
+repository = CWD/remote.git
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_DIR overrides config when invoked as 'notmuch git'"
+notmuch config set git.path `pwd`/bar
+NOTMUCH_GIT_DIR=`pwd`/remote.git notmuch git -ldebug status |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+notmuch config set git.path
+cat <<EOF > EXPECTED
+repository = CWD/remote.git
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_PREFIX works when invoked as 'nmbug'"
+NOTMUCH_GIT_PREFIX=env:: "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+prefix = env::
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_PREFIX works when invoked as nmbug"
+NOTMUCH_GIT_PREFIX=foo:: "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
+cat <<EOF > EXPECTED
+prefix = foo::
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_PREFIX overrides config when invoked as 'nmbug'"
+notmuch config set git.tag_prefix config::
+NOTMUCH_GIT_PREFIX=env:: "$NOTMUCH_BUILDDIR"/nmbug -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
+notmuch config set git.path
+cat <<EOF > EXPECTED
+prefix = env::
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "env variable NOTMUCH_GIT_PREFIX overrides config when invoked as 'notmuch git'"
+notmuch config set git.tag_prefix config::
+NOTMUCH_GIT_PREFIX=env:: notmuch git -ldebug status |& grep '^prefix' | notmuch_dir_sanitize > OUTPUT
+notmuch config set git.path
+cat <<EOF > EXPECTED
+prefix = env::
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+
+test_begin_subtest "init, xdg default location"
+repo=home/.local/share/notmuch/default/git
+notmuch git -ldebug init |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+git -C $repo rev-parse --absolute-git-dir | notmuch_dir_sanitize >> OUTPUT
+cat <<EOF > EXPECTED
+repository = CWD/$repo
+CWD/$repo
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "init, xdg default location, with profile"
+repo=home/.local/share/notmuch/work/git
+NOTMUCH_PROFILE=work notmuch git -ldebug init |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+git -C $repo rev-parse --absolute-git-dir | notmuch_dir_sanitize >> OUTPUT
+cat <<EOF > EXPECTED
+repository = CWD/$repo
+CWD/$repo
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "init, configured location"
+repo=configured-tags
+notmuch config set git.path `pwd`/$repo
+notmuch git -ldebug init |& grep '^repository' | notmuch_dir_sanitize > OUTPUT
+notmuch config set git.path
+git -C $repo rev-parse --absolute-git-dir | notmuch_dir_sanitize >> OUTPUT
+cat <<EOF > EXPECTED
+repository = CWD/$repo
+CWD/$repo
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "configured tag prefix"
+notmuch config set git.tag_prefix test::
+notmuch git -ldebug status |& grep '^prefix' > OUTPUT
+notmuch config set git.tag_prefix
+cat <<EOF > EXPECTED
+prefix = test::
+EOF
+test_expect_equal_file EXPECTED OUTPUT
+
+test_begin_subtest "default version is 1"
+notmuch git -l debug -C default-version.git init
+output=$(git -C default-version.git cat-file blob HEAD:FORMAT)
+test_expect_equal "${output}" 1
+
+test_begin_subtest "illegal version"
+test_expect_code 1 "notmuch git -l debug -C default-version.git init --format-version=42"
+
+hash=("" "8d/c3/") # for use in synthetic repo contents.
+for ver in {0..1}; do
+ test_begin_subtest "init version=${ver}"
+ notmuch git -C version-${ver}.git -p "test${ver}::" init --format-version=${ver}
+ output=$(git -C version-${ver}.git ls-tree -r --name-only HEAD)
+ expected=("" "FORMAT")
+ test_expect_equal "${output}" "${expected[${ver}]}"
+
+ test_begin_subtest "initial commit version=${ver}"
+ notmuch tag "+test${ver}::a" "+test${ver}::b" id:20091117190054.GU3165@dottiness.seas.harvard.edu
+ notmuch git -C version-${ver}.git -p "test${ver}::" commit --force
+ git -C version-${ver}.git ls-tree -r --name-only HEAD | grep -v FORMAT > OUTPUT
+cat <<EOF > EXPECTED
+tags/${hash[${ver}]}20091117190054.GU3165@dottiness.seas.harvard.edu/a
+tags/${hash[${ver}]}20091117190054.GU3165@dottiness.seas.harvard.edu/b
+EOF
+ test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+ test_begin_subtest "second commit repo=${ver}"
+ notmuch tag "+test${ver}::c" "+test${ver}::d" id:20091117190054.GU3165@dottiness.seas.harvard.edu
+ notmuch git -C version-${ver}.git -p "test${ver}::" commit --force
+ git -C version-${ver}.git ls-tree -r --name-only HEAD | grep -v FORMAT > OUTPUT
+cat <<EOF > EXPECTED
+tags/${hash[$ver]}20091117190054.GU3165@dottiness.seas.harvard.edu/a
+tags/${hash[$ver]}20091117190054.GU3165@dottiness.seas.harvard.edu/b
+tags/${hash[$ver]}20091117190054.GU3165@dottiness.seas.harvard.edu/c
+tags/${hash[$ver]}20091117190054.GU3165@dottiness.seas.harvard.edu/d
+EOF
+ test_expect_equal_file_nonempty EXPECTED OUTPUT
+
+ test_begin_subtest "checkout repo=${ver} "
+ notmuch dump > BEFORE
+ notmuch tag -test::${ver}::a '*'
+ notmuch git -C version-${ver}.git -p "test${ver}::" checkout --force
+ notmuch dump > AFTER
+ test_expect_equal_file_nonempty BEFORE AFTER
+done
+
+test_done
--- /dev/null
+Return-path: <anarcat@orangeseeds.org>
+Envelope-to: david@tethera.net
+Delivery-date: Mon, 19 Mar 2018 13:56:54 -0400
+Received: from marcos.anarc.at ([206.248.172.91])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <anarcat@orangeseeds.org>)
+ id 1exz1i-0002aa-If
+ for david@tethera.net; Mon, 19 Mar 2018 13:56:54 -0400
+Received: from [127.0.0.1] (localhost [127.0.0.1]) (Authenticated sender: anarcat) with ESMTPSA id 718A610E04F
+From: =?utf-8?Q?Antoine_Beaupr=C3=A9?= <anarcat@orangeseeds.org>
+To: David Bremner <david@tethera.net>, notmuch@notmuchmail.org
+Subject: Re: bug: "no top level messages" crash on Zen email loops
+In-Reply-To: <87a7v42bv9.fsf@curie.anarc.at>
+References: <87d10042pu.fsf@curie.anarc.at> <87woy8vx7i.fsf@tesseract.cs.unb.ca> <87a7v42bv9.fsf@curie.anarc.at>
+Date: Mon, 19 Mar 2018 13:56:54 -0400
+Message-ID: <874llc2bkp.fsf@curie.anarc.at>
+MIME-Version: 1.0
+Content-Type: multipart/mixed; boundary="=-=-="
+X-Spam_score: 0.0
+X-Spam_score_int: 0
+X-Spam_bar: /
+
+--=-=-=
+Content-Type: text/plain
+
+And obviously I forget the frigging attachment.
+
+
+--=-=-=
+Content-Type: application/x-gtar-compressed
+Content-Disposition: attachment; filename=zendesk-email-loop2.tgz
+Content-Transfer-Encoding: base64
+
+H4sIAJX1r1oAA+xbaVPbyNbO19Gv6CF1K0mBrM2SLYOY8QoGbAzegKlbVFtq28KyJNSSF269//09
+Lclm35LJTebOiARbvZw+a5+npcPIDh08EMwoED58r0uEKyeK7FPKqfc+V9cHSZFVJZeTlWz2gyjJ
+OUn9gNTvxtGdK6IhDhD6gF0cmDh8dtxr/X/Ra3Rrf0mVpaym5FQlc6orWl7P5hQpmxdzmi4qSj4D
+Y2xSkLdq7XeuwQysPWt/WZIVOba/LGqalJPA/jktJ35A4neR+MH1N7f/KQmjwOVbOBwX0A6NfN8L
+wt8Tr8iY3nSXO+OPA3tku9jhO14BpXrYTIb8Ht9mcMhViGPPSECsF0edEpPAMKuAhoE3RVNo9mhm
+1Y0+O56JnbFHQ/QHbAIZEX6kf3/hfhkskZUssLwzugUDh/biC5rb4RhV241OC9kWkrJFVZHEqqiK
+3C9DL0A7z7Czu406EdlCkoIa4ASyKOXhV0HOFZQc4sWsKKLP1UrnC+ig7eMpXx4Tc0ICvkcCantu
+AbHWIqWYUttFSiabkdBnoKLCXF7Of0Ge+1DEFakjMiNOAa1u2yEOI1pATW8LUdMLiMFLGRUF5Dqy
+QaeGmhFRSGhIjVLxvNq+VMWtymG9cdmu7zWrlS3ul/iuVzyqV7b2O42jy9pxs3N5dNy/LMOX02K7
+kzQ3qu12ca+6dVruVS7rzctKs91nrRXuFxyFnkNw4BpjPEXru0tQoEkM10OzRGojlhMY3wvI0rFp
+WGCmwUtiIUXVECWm51oUgcV8MM8IBoEoiobAYIkunle6hkDbD33EIkMSgA6yGd+ztIyNLSlzY9El
+Ze6JPr/c/YekyxlJkzIS7GqSpKe+NF287EVt5kY1WS7HbpStfbUbaffc6IFkXhRKTwv1bM99eUSR
+yfM5Au8boc5ReyZl5EQK0/bHJEDFarmyzxerbVnV+PZ+EbxT1QT4jwZ2SL+wyU0PmY5N3BCZJAjt
+oQ0CktjxwN2ItYq9F23wnAbL2Ww+q0lFuShKr6qwTyxQYfZWhSKoUCpkRbTJ0gT63O2U36HC9U6S
+ib9Z3hRDjD7aVZ4n8MzmUiqWq2VZlHM5XftamRT9JZluiGsROkm4mHvBRHrOF0AMSc5AalbFrxVH
+L5WknMzScFX9k8SpgAMVXhvL1UDUAtqzwyM8QO0k7zydgE6J7yzjrPKW0Wxc8Z4Ez0vENQhs3iPC
+1yuQ/HQlp5801AtFv1QxzmsDSckOzeHgUsplAQANTT2HZXOg582cpOetS+oHUfj7HWvtcnWXv+X2
+DsH7o55cSoVslWVLkcH9pbRc9qml2tHgipiw+Z57UbDSTKqQVfiiMYaNmBAXGhIX4xr2lNzmLykj
+cmXPDSH8+c7SB7NNIye0fRyEAnZCErg4hGnbHBp4kWvhYGls8LxhXE6BDBu1Zl+XMAjzWFOypGU3
+YL45xgEloRGFQz5/u2aAXQpbC191Tc+CXayAcrAzQXIpQv4BXVLfcynhmcUDsBWY1nG4uA/kn9ph
+yKKG5Sp+RFwSgOdZMLmBbYcEBXSRKAwl99CRgpQlHzOwAO1BGgY+bIvP5lVFy+miLMt5GJlO5Zmb
+8kXTBPFDvg6LgVxDmYgcS7h82x6BhqIAFIdmhrSNsBFQzNMxhj12G5lGAJlxQSwh/dxGlnHHiqCX
+a8NyqRAuwm1EV11AJzQAjYt6HlC4zrQ/NpRNOdob+eOjA9ITx7WS2r7qdxZd3PA79SNrTq8ts7G8
+tptddWTAjLFhsTBkG0ohiF0y9Arwb5p6vG0VbJdf99DUm5hd+TTRF8zUSiHzjPXNymQkNRljzzgy
+G/Ogrzfz+pV442Fts0lmvd5hq7dcqBPVy17NDhzizlsnh61KLWfnG/kDf7h57Cw2ezUXq6Q66YkL
+pxYe1azRebBnXWiT3nH7qnR81VfzWAoWVDmzRq4iir090eotdGtfEMpWcZSjEC65aqPeqmXdUjXn
+BV4w3FzOTFKpn8tndauy31ke2KcTTdZPz9STzRPjK03Hge1GkEQcQDGTyM/cteP/ihkPi8L1/szO
+iaMDoX5Fg9HeZrNfypYOdHvzpNs5L0/y43arNtwTivsDyTpzrIDcXC33RktJGeXV3l4Qnkxvjqwr
+eeDX/H1ltF+JDs91dzZoNoh/le+PvRHBfpdG+PrcOVnsh2fyvjsrDejgeKYuOuc3m1HPOSlNatHs
+tGtP8y0tS2oz/8yiE41cn12U+62uV5meluoGBGm5UazybQaXC0hc3cPm7yypDXvFzIDkiMyZUTlo
+j/dPawOwqoToFH6FgQFnA2y0XMc5HjQkd+HOLYWqejAVvBPDKEhSDjzi0C4vDiriYrNF1F6xfXI4
+PfcOAnMZHXswRGcU6hOzM9mviMOLRrEgiTDp4sKNzsODidf30hZf7FuNKh4W4SrkoaHIF5f62UxJ
+GhiZmyKuRxd86JxUJVO1nIYNk3UYelI9syrHchQq5ym1DolOrhs6vZ3cbk+Pa9VieU1f79jl42sq
+t7JLOgi7XbN6YVbPvSXNFxnfMEKdNaIbrB8oZvekeDNeQG5mlC77l+3LXA8w/Em62jC4yRaj8ogf
+d1kL25p76UHIut1hEwhaduA41EM8Kh9Vi80/xUD9iXM17VnnEzyw5ovh9awYNI+/0UAzudI6mam8
+M5y/0WSMzn2bobebjE2+bzP0fpMxIk/YDL3FZKtse0TcETvmZ3NanuM4nn9/Kn+AFlgOFXwHkPV7
+Uvx15EGe5v3AdkM8cAjHffzIoxYcNylBbJdCS4Zp4l0N4YE3g9axTZFjuwTxHz9y3L79AONtcVxn
+jN0Jm4kYimX7HDZD5pX3UWOGASZkYjelH3oJccK2dnZIxQbnQjNohfVhy0IumSPbBapTQENwpI+n
+QK8NMQD0+mNAF/HCc2yHW/G3KV6ioe0anIXGxPGHkXOPApxEmISWZ0ZTUFLSCGuPw9CnBUGADpq5
+RbUZZIC9GN+eDzDHSjhO1mcHbQpRyCSNKAK5YkluxcQOhSNeQNixLplicBTNbByzMCcDpq/p3dVT
+FHmHAWFsCsTlIyqkyJIKoJQM6hgcKCKezxiCdSHUrZWC/MCzIjNkCvkUkHSPwG6iEtC4SyH9xlyw
+FQ2OIdeQ4GkMW7HjxEQswshSMILJcl4QWwywKCS3kNGKXQVWAj9K9dQnyPG8CWNrjoOYG7YgW5yZ
+BnCk58zWPpYAZTqGxZ3lrxxXYrcBGcFMugVORVb+wzjjuIodL4kmrjf/ba3jCSE+bFnYnCBvGLMO
+H/ECicZpLDaIY4NnRnCSdsEIiMYPfVK9rNTAlMCMArFBgiE2ya+oZ1P7JesAKUaeAqKBaQ8sP/KQ
+ZQP8DxNfX6kTwioK7/lR7AAG95ILrI2vy2pWBs9nDwqgD3zYSsVgJksmA6n7gsRxiVkvCWz4Shbs
+C+APAuHi4lFqoLXOMqgax6TpGRxbArTIpBqAT9jDoW3CSYVJNPQcx5tnwDSe+ymMDZOQWWkTJ+gd
++ZhSOE5bv6HSMgmSJGa2WNCsBs3B9QwODht8EjJWzDWz+CAKkyC3wbwuSdwcnImEqQ+m5CHuwTUz
+6JQpC0INGIh3kHX/nUhL1XsHSgpwmBkLM1lwPFCIsJp0GTBSbLepxeLGerZdGgYQYLB5ULalxDG3
+WiXhLNnVgNERe/oBQkIMASrlvCdVlEHNRHmJkKBptj2z6ZSQp9z61ySLvHRBCK13V/gC9ifBzGbe
+wJ51JLG1hequmQETrq4/kgMyz07I/zb+vFQ1DqfOt2WqnV8rx+XOeauKGC3U6paO6mW0wQtCXykL
+QqVTQWfsSSs7WqOYos3sgx1BqDY30AazPBh+Pje4eWauZLxgJHROhQWjJrHp6dcEoKdzM1Zobexy
+O6yHfRBs7XII7UwhmGNf4tlz4pmhVDbuCr2BUrTPOm7lX4uvVGIFGNwGEmJ6NFw6SQZezzAp3WB9
+CIWJL1joP/EtQgNwM9CWCQ6JfQoqXn3bjgf8H6MoxCSBZyFhemfgWZCvWCNbYm5bDJBIovivX+0p
+c0XswhEKTljg/AAZt8GfrcQU4nbMx45lz27nr3sl0V+g7Rgh8LDLjsZwtJfy/mIbwhe0McRT21kW
+0KejyLQtjPYMDtRrkU9bPRJYsPlsFQMbO1sUdM6Dg9rDdCK1b0AwSWaUQDwvKHzMxtd2qpX7/CRD
+0MeByn5gzNuAjcGtoM2OAPRSyv7uY5izI/i70PFmrGNwr6GdN2Mdg3sN7bwZ6wDCYmvvgPMGZMgU
+9wzw2QC2HdbvAq6IH0EHG7v3xhrc7egdAe9mUKqi96MlSKNP46Wn0dIT3K+zpsG9Ap1eFOxe8jW4
+F8gkEnfeiMHA6V9FYW/EYJDcX0BhqQneBsRA7U9CsZTIPTy2Mwhgq3oAytKBgMwSUt+OzSCgDe49
+2OwlX7BBV29x58eQi1n3PrK7Ded3Y7uHLBrcG2He60yDX71MInHTF9FiqljYGb4ZLaZY0eBeQIsr
+n3kbZIRj3Kug8Y2QkUGv10Djk5DxsYsZ3LvwI6z5rCkBW74TjQK1xKoP8ajBfQ0ifRKPQuC8H5Ey
+y8bII06kO9QHO63y86c0hdfi69PuPZgJWAXG7u4IDKLEiCVBWyzB2yGZUhPSSfxtBZBSLEfNMQgV
+I7nYBdOXShu3sMCyqe/AjrIsuJ5LNnZT2MBowZ7psyE4VtrG21aCgBsJPZvMi8ms3RgvgGdO7tGM
+AmfjhX0pNjZEydNBLyREY4h5l6iLp/eBJeMj1r7BgYexaYnuk99fCd7h3PCjK2P+HtdT9V9y5lRS
+JFXKaWpeVHN6Ts3ns+LXF4C9Vv8lyuK6/ksVFVb/lctK/9R//Teu/8X6L12vFtVvqP9SxIKq//D6
+LzmTf6r+q9g/2kpqwMQfXgP2TKXWMxUoL3c/qNTS3lOplVdK+do3VGo9NvjjMqNnSp+e7XlQqaX8
+lyu13l4FFGuwokga7PG6JGez361S6xkVvr1S6+3lZ3H1WbVUrspSuVSVf85KrfeJU6xVS1oizute
+/rer1Hpv+dQ3VGq9tyhsXam1Ppq6XvyYYx4fJf3fvltRFs4puvWUUvKS9NcqypLyP2VRFhlqN165
+oUuji173Cotdunm1lLs9s3W4SZvjC8+cuYqiTIY31R9QzRPOWsv6jVKea5KrRjK1j+3lqV1tFA9J
+ZVqN5EpjJLWpqmQ3xVZrNuiMgqgf9AcXE+fgZqlqp5V6y86OvcPNrq2NbkTBHmud5cnZnhb6tQa5
+yC7qWX3veulH0ol85kndWaT1W+cVkndzFbl0IOTnLW0zur7YrPhFnHOspi17/n5vNrW0vcNydCQO
+m3mnP7Mq0+yPLcr6uc1Ir498tecfDoOlM+rIzdzNYE4qVLFMs3rWyGbn3qQhBAv3Iqv7ujA/rxw0
+G4PhsJRfnp1dYKl0IB6eDh2hW7v2Wvnzi9Z5kJPxZlXWpe5o8+ZakrsHttroyz133mgdLIr+5pk+
+PiG6WLxpErNUuTruurp6bZcP3Vnvahx2hKMeaU310qA5znfnZbWvXI+F69pVb/KeoqzTxrGVHVxv
+/hxFWWzWdNY8xAvXGbv7IZ87qJzW6XM1WHbZPpbdw8XeeLQq8SkuOmU6j/hnirweFW49KgJiRF4v
+3Ko07bl3SDRvOVxWrvDZk0VAjNSDKiAYNArq067bwycVcrg3lTsdr14syPL3Kel63rw/T0nX39Xg
+jwrCNEl5oSDsJRjxkxSEvVaF8KAmgXvwWjXGvpKyFf/ZjhT/FRLHziDxKjalEfn35wc1G/HT8YSK
+MMcONOGJJ8RjqSCpX7bYw++AIMszOELZawZKyJQ9uWYPsd3lGg1m0DJ5GxC/12GPtZOJsPIUogzu
+oNlz2YN0VlNjw4ERwxnQS56uU9siAxyw1xGA4tNH7n/EbCD2x1mg3ydZ/2JwyGYP2sMImF8iipc0
+eYgfT2Vv/7RbFjlunghjszdlHlhljWVH3m8/uAjkve75TxHIP0Ugf5kikHuUE5UApvThyKP6iw0E
+QBTz64dylQ15Y3cnsVGsVdYkiQYn/gvcgDhOqirWLCYt1MfmbUtizeSGvcgJg+QtEBh8RQ8chVlq
+4yn9A0vMXKlPxGxK0GR57EiIPpoq+9lOXlcBVdjNHrL6NYwyTkB5ibsygryDQcVhYWizI8JquVtx
+Hl9MwBl24PSRSAisPyWgmIoYf2yj1HGz4HZ3Vkkp2tMRKyV24ri7TTcbKPFM1pplvAfmvbdzxITT
+T2YU4BlYNkiyTPzV4ARFHUhkIIqiCacXrCoS0YaKaWr5vDYksiX/xhwVqP1/O2e32yYQROH7fQpk
+KelNaQA7TVW6lnrTF2h7XYEgjpWAkXGUKk/fmeFvzQKmaVRZyvkUyXgJy3J2dnYHxqy8yygrwiS9
+jR7l/FL7xfLrRfCN/qhVZclPzI1giYq3GcUuJW0Env+RPurD3er07ifvQ5FvpGLWfVPpQ3ObNL8e
+bRz09wcg9WnId7u49LPDD0BD8cNuQouXveQISTFVFTruUxrfbw9ubUL7KNnybXGR2812z4M7rDKt
+qEM6s2Kdr47658vVIRk0BdPSxRzZLw4YgumeFp3piOEcn6loD6eqhv1Q7YWa79/J/zg/8y3NP73S
+1lNp9f5HdLfLolFnJcIMeMLahcQ0JPmunhfSOsB0K8fetnF3fuwnfmoZeQ+qimaP/S7frI/XVzwD
+SPH4sfzYvldVp9w/6aaVqdwp3ZaWbsF1q5tWnXL+tSHnhHSxYEmnlbXknJBjXDaaHzhv7SEqSxbq
+OXHrldnCnpCCOEiD25HZUNTTqtXPktl5V0lHW42WzpiKq+K3Vn0dg06wZq6g2ao43U6t7Ja+sJ1a
+2S2d007Os5I0DXJwi7WEBloZwcFAAti8OKFKyVFVUk6VnOQY2Tm04NpvZJW2+BU/RPl9l3o1XD/N
+OL0zcJqOEY3Mj0X4FwqT0cj8WIR8exeNTGtl5CgNysG5LycFkWs+Gd/wwGkuvEoIe8mYEUvU6jXG
+jFgiafU3tjgVmGkllyXLyRm+xJwU6UuzWKLNKOYF1tr4L9kve6i8XbAi6+rss66momVkXb056vyv
+PH06h/e/Lb0b35P3v3krvP/tf1D3/yErzqX//er9f+h/AAAAAAAAAAAAAAAAAAAAAAAAAAAAAABg
+lD9gaWU0AHgAAA==
+--=-=-=
+Content-Type: text/plain
+
+
+PS: don't we have a "you forgot to actually attach the damn file" plugin
+when we detect the word "attachment" and there's no attach? :p
+
+--=-=-=--
--0016e687869333b14e0478963d33--
--0016e687869333b1570478963d35
-Content-Type: application/octet-stream;
+Content-Type: text/x-diff;
name="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
Content-Disposition: attachment;
filename="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
--- /dev/null
+Return-path: <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>
+Envelope-to: david@tethera.net
+Delivery-date: Wed, 28 Nov 2012 18:41:46 -0400
+Received: from [199.188.72.155] (helo=yantan.tethera.net)
+ by tesseract.cs.unb.ca with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
+ (Exim 4.72)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>)
+ id 1TdqKA-00017j-3c
+ for david@tethera.net; Wed, 28 Nov 2012 18:41:46 -0400
+Received: from wagner.debian.org ([217.196.43.132])
+ by yantan.tethera.net with esmtp (Exim 4.72)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>)
+ id 1TdqK9-00072z-AF
+ for david@tethera.net; Wed, 28 Nov 2012 18:41:45 -0400
+Received: from localhost ([::1] helo=wagner.debian.org)
+ by wagner.debian.org with esmtp (Exim 4.72)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>)
+ id 1TdqK8-0007GZ-67
+ for david@tethera.net; Wed, 28 Nov 2012 22:41:44 +0000
+Received: from vasks.debian.org ([217.196.43.140])
+ by wagner.debian.org with esmtp (Exim 4.72)
+ (envelope-from <gladky-anton-guest@alioth.debian.org>)
+ id 1TdqIc-0006jm-OC; Wed, 28 Nov 2012 22:40:11 +0000
+Received: from gladky-anton-guest by vasks.debian.org with local (Exim 4.72)
+ (envelope-from <gladky-anton-guest@vasks.debian.org>)
+ id 1TdqIc-0003j1-DE; Wed, 28 Nov 2012 22:40:10 +0000
+Date: Wed, 28 Nov 2012 22:40:10 +0000
+From: Anton Gladky <gladky.anton@gmail.com>
+To: 691896@bugs.debian.org, control@bugs.debian.org,
+ 691896-submitter@bugs.debian.org
+Message-ID: <debian/2.6.1.dfsg-4-1-g87ea161@87ea161e851dfb1ea324af00e4ecfccc18875e15>
+X-PTS-Approved: Yes
+X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on wagner.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00,FREEMAIL_FROM
+ autolearn=ham version=3.3.1
+Subject: [87ea161] Fix for Bug#691896 committed to git
+X-BeenThere: debian-science-maintainers@lists.alioth.debian.org
+X-Mailman-Version: 2.1.13
+Precedence: list
+List-Id: Mailing list for maintainer discussions and BTS messages
+ <debian-science-maintainers.lists.alioth.debian.org>
+List-Unsubscribe: <http://lists.alioth.debian.org/cgi-bin/mailman/options/debian-science-maintainers>,
+ <mailto:debian-science-maintainers-request@lists.alioth.debian.org?subject=unsubscribe>
+List-Archive: <http://lists.alioth.debian.org/pipermail/debian-science-maintainers>
+List-Post: <mailto:debian-science-maintainers@lists.alioth.debian.org>
+List-Help: <mailto:debian-science-maintainers-request@lists.alioth.debian.org?subject=help>
+List-Subscribe: <http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-science-maintainers>,
+ <mailto:debian-science-maintainers-request@lists.alioth.debian.org?subject=subscribe>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org
+Errors-To: debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org
+X-SA-Exim-Connect-IP: ::1
+X-SA-Exim-Mail-From: debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org
+X-SA-Exim-Scanned: No (on wagner.debian.org); SAEximRunCond expanded to false
+X-Spam-Score: 1.3
+X-Spam_bar: +
+
+
+tags 691896 + pending
+thanks
+
+Hello,
+
+ The following change has been committed for this bug by
+ Anton Gladky <gladky.anton@gmail.com> on Wed, 31 Oct 2012 08:16:42 +0100.
+ The fix will be in the next upload.
+====================================
+Minor fixes in README.Debian. (Closes: #691896)
+
+
+====================================
+
+You can check the diff of the fix at:
+
+ ;a=commitdiff;h=87ea161
+
+
+
+--
+debian-science-maintainers mailing list
+debian-science-maintainers@lists.alioth.debian.org
+http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-science-maintainers
--- /dev/null
+Return-path: <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>
+Envelope-to: david@tethera.net
+Delivery-date: Wed, 28 Nov 2012 18:42:39 -0400
+Received: from [199.188.72.155] (helo=yantan.tethera.net)
+ by tesseract.cs.unb.ca with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
+ (Exim 4.72)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>)
+ id 1TdqL1-00017s-3b
+ for david@tethera.net; Wed, 28 Nov 2012 18:42:39 -0400
+Received: from wagner.debian.org ([217.196.43.132])
+ by yantan.tethera.net with esmtp (Exim 4.72)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>)
+ id 1TdqL0-00073Z-Gw
+ for david@tethera.net; Wed, 28 Nov 2012 18:42:38 -0400
+Received: from localhost ([::1] helo=alioth.debian.org)
+ by wagner.debian.org with esmtp (Exim 4.72)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org>)
+ id 1TdqKz-0007sR-PR
+ for david@tethera.net; Wed, 28 Nov 2012 22:42:37 +0000
+Received: from buxtehude.debian.org ([140.211.166.26])
+ by wagner.debian.org with esmtp (Exim 4.72)
+ (envelope-from <debbugs@buxtehude.debian.org>) id 1TdqKU-0007SW-IG
+ for debian-science-maintainers@lists.alioth.debian.org;
+ Wed, 28 Nov 2012 22:42:07 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.72)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1TdqKR-0001dH-UL; Wed, 28 Nov 2012 22:42:03 +0000
+X-Loop: owner@bugs.debian.org
+Resent-From: Anton Gladky <gladky.anton@gmail.com>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Science Maintainers
+ <debian-science-maintainers@lists.alioth.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Wed, 28 Nov 2012 22:42:02 +0000
+Resent-Message-ID: <handler.691896.B691896.13541424145158@bugs.debian.org>
+X-Debian-PR-Message: followup 691896
+X-Debian-PR-Package: gmsh
+X-Debian-PR-Keywords: pending
+X-Debian-PR-Source: gmsh
+Received: via spool by 691896-submit@bugs.debian.org id=B691896.13541424145158
+ (code B ref 691896); Wed, 28 Nov 2012 22:42:02 +0000
+Received: (at 691896) by bugs.debian.org; 28 Nov 2012 22:40:14 +0000
+Received: from wagner.debian.org ([217.196.43.132])
+ by buxtehude.debian.org with esmtps (TLS1.0:RSA_AES_256_CBC_SHA1:32)
+ (Exim 4.72) (envelope-from <gladky-anton-guest@alioth.debian.org>)
+ id 1TdqIg-0001Kh-Ba; Wed, 28 Nov 2012 22:40:14 +0000
+Received: from vasks.debian.org ([217.196.43.140])
+ by wagner.debian.org with esmtp (Exim 4.72)
+ (envelope-from <gladky-anton-guest@alioth.debian.org>)
+ id 1TdqIc-0006jm-OC; Wed, 28 Nov 2012 22:40:11 +0000
+Received: from gladky-anton-guest by vasks.debian.org with local (Exim 4.72)
+ (envelope-from <gladky-anton-guest@vasks.debian.org>)
+ id 1TdqIc-0003j1-DE; Wed, 28 Nov 2012 22:40:10 +0000
+Date: Wed, 28 Nov 2012 22:40:10 +0000
+From: Anton Gladky <gladky.anton@gmail.com>
+To: 691896@bugs.debian.org, control@bugs.debian.org,
+ 691896-submitter@bugs.debian.org
+Message-ID: <debian/2.6.1.dfsg-4-1-g87ea161@87ea161e851dfb1ea324af00e4ecfccc18875e15>
+X-PTS-Approved: Yes
+Resent-Sender: Debian BTS <debbugs@buxtehude.debian.org>
+X-Spam-Checker-Version: SpamAssassin 3.3.1 (2010-03-16) on wagner.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-4.2 required=5.0 tests=BAYES_00,FREEMAIL_FROM,
+ RCVD_IN_DNSWL_MED autolearn=ham version=3.3.1
+Subject: Bug#691896: [87ea161] Fix for Bug#691896 committed to git
+x-debian-approved: yes
+X-BeenThere: debian-science-maintainers@lists.alioth.debian.org
+X-Mailman-Version: 2.1.13
+Precedence: list
+Reply-To: Anton Gladky <gladky.anton@gmail.com>, 691896@bugs.debian.org
+List-Id: Mailing list for maintainer discussions and BTS messages
+ <debian-science-maintainers.lists.alioth.debian.org>
+List-Unsubscribe: <http://lists.alioth.debian.org/cgi-bin/mailman/options/debian-science-maintainers>,
+ <mailto:debian-science-maintainers-request@lists.alioth.debian.org?subject=unsubscribe>
+List-Archive: <http://lists.alioth.debian.org/pipermail/debian-science-maintainers>
+List-Post: <mailto:debian-science-maintainers@lists.alioth.debian.org>
+List-Help: <mailto:debian-science-maintainers-request@lists.alioth.debian.org?subject=help>
+List-Subscribe: <http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-science-maintainers>,
+ <mailto:debian-science-maintainers-request@lists.alioth.debian.org?subject=subscribe>
+MIME-Version: 1.0
+Content-Type: text/plain; charset="us-ascii"
+Content-Transfer-Encoding: 7bit
+Sender: debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org
+Errors-To: debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org
+X-SA-Exim-Connect-IP: ::1
+X-SA-Exim-Mail-From: debian-science-maintainers-bounces+david=tethera.net@lists.alioth.debian.org
+X-SA-Exim-Scanned: No (on wagner.debian.org); SAEximRunCond expanded to false
+X-Spam-Score: 1.3
+X-Spam_bar: +
+
+
+tags 691896 + pending
+thanks
+
+Hello,
+
+ The following change has been committed for this bug by
+ Anton Gladky <gladky.anton@gmail.com> on Wed, 31 Oct 2012 08:16:42 +0100.
+ The fix will be in the next upload.
+====================================
+Minor fixes in README.Debian. (Closes: #691896)
+
+
+====================================
+
+You can check the diff of the fix at:
+
+ ;a=commitdiff;h=87ea161
+
+--
+debian-science-maintainers mailing list
+debian-science-maintainers@lists.alioth.debian.org
+http://lists.alioth.debian.org/cgi-bin/mailman/listinfo/debian-science-maintainers
--- /dev/null
+From: David Bremner <bremner@debian.org>
+To: Samuel Bronson <naesten@gmail.com>, 695159@bugs.debian.org, Debian Bug Tracking System <submit@bugs.debian.org>
+Subject: Re: Bug#695159: debian-el: Shouldn't put downloaded bugs loose in ~/
+In-Reply-To: <87vcch7hxy.fsf@naesten.dyndns.org>
+References: <87vcch7hxy.fsf@naesten.dyndns.org>
+Date: Thu, 25 Oct 2018 10:41:38 -0300
+Message-ID: <87r2geywh9.fsf@tethera.net>
+MIME-Version: 1.0
+Content-Type: text/plain
+
+
+Control: severity -1 minor
+Control: tag -1 moreinfo
+
+Samuel Bronson <naesten@gmail.com> writes:
+
+> Package: debian-el
+> Version: 35.2+nmu1
+> Severity: normal
+> File: /usr/share/emacs/site-lisp/debian-el/debian-bug.el
+>
+> Dear Maintainer,
+>
+> After being mildly annoyed with this for ages, it finally occurred to me
+> to file a bug about it:
+>
+> It's rather rude of `getdebian-bug-get-bug-as-email' to default to
+> sticking downloaded mbox files loose in ~/, isn't it?
+>
+> (And might it not make sense to try and use the same files as the bts(1)
+> command from the devscripts package?)
+>
+
+Hi Samuel
+
+There is already a variable "debian-bug-download-directory" which can be
+customized. Is there an obviously better default? I guess we could put
+things in /tmp by default, with a minor privacy leak on multiuser
+systems.
+
+d
+
+# body 1
--- /dev/null
+Return-path: <bounces+20181025-bremner=debian.org@tracker.debian.org>
+Envelope-to: david@tethera.net
+Delivery-date: Thu, 25 Oct 2018 09:45:10 -0400
+Received: from muffat.debian.org ([2607:f8f0:614:1::1274:33])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <bounces+20181025-bremner=debian.org@tracker.debian.org>)
+ id 1gFfwj-0004Y9-69
+ for david@tethera.net; Thu, 25 Oct 2018 09:45:10 -0400
+Received: from ticharich.debian.org ([2001:41c8:1000:21::21:23])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=ticharich.debian.org,EMAIL=hostmaster@ticharich.debian.org (verified)
+ by muffat.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <bounces+20181025-bremner=debian.org@tracker.debian.org>)
+ id 1gFfwi-0004A1-J6
+ for david@tethera.net; Thu, 25 Oct 2018 13:45:08 +0000
+Received: from localhost ([::1] helo=ticharich.debian.org)
+ by ticharich.debian.org with esmtp (Exim 4.89)
+ (envelope-from <bounces+20181025-bremner=debian.org@tracker.debian.org>)
+ id 1gFfwh-0002Ex-6w
+ for david@tethera.net; Thu, 25 Oct 2018 13:45:07 +0000
+Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=mailly.debian.org,EMAIL=hostmaster@mailly.debian.org (verified)
+ by ticharich.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1gFfwg-0002Em-NI
+ for dispatch+emacs-goodies-el@tracker.debian.org; Thu, 25 Oct 2018 13:45:06 +0000
+Received: from quantz.debian.org ([2001:41c8:1000:21::21:28])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=quantz.debian.org,EMAIL=hostmaster@quantz.debian.org (verified)
+ by mailly.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1gFfwg-0004h1-AC
+ for dispatch+emacs-goodies-el@tracker.debian.org; Thu, 25 Oct 2018 13:45:06 +0000
+Received: from qa by quantz.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1gFfwf-0007Lg-TQ
+ for dispatch+emacs-goodies-el@tracker.debian.org; Thu, 25 Oct 2018 13:45:05 +0000
+Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=buxtehude.debian.org,EMAIL=hostmaster@buxtehude.debian.org (verified)
+ by quantz.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1gFfwf-0007J0-3r; Thu, 25 Oct 2018 13:45:05 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1gFfwc-0003jj-VU; Thu, 25 Oct 2018 13:45:02 +0000
+X-Loop: owner@bugs.debian.org
+Subject: Bug#695159: debian-el: Shouldn't put downloaded bugs loose in ~/
+Reply-To: David Bremner <bremner@debian.org>, 695159@bugs.debian.org
+Resent-From: David Bremner <bremner@debian.org>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Emacsen team <debian-emacsen@lists.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Thu, 25 Oct 2018 13:45:01 +0000
+Resent-Message-ID: <handler.695159.B.154047490313415@bugs.debian.org>
+X-Debian-PR-Message: followup 695159
+X-Debian-PR-Package: debian-el
+X-Debian-PR-Keywords:
+References: <87vcch7hxy.fsf@naesten.dyndns.org> <87vcch7hxy.fsf@naesten.dyndns.org>
+X-Debian-PR-Source: debian-el, emacs-goodies-el
+Received: via spool by submit@bugs.debian.org id=B.154047490313415
+ (code B); Thu, 25 Oct 2018 13:45:01 +0000
+Received: (at submit) by bugs.debian.org; 25 Oct 2018 13:41:43 +0000
+X-Spam-Checker-Version: SpamAssassin 3.4.1-bugs.debian.org_2005_01_02
+ (2015-04-28) on buxtehude.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-19.5 required=4.0 tests=BAYES_00,FROMDEVELOPER,GMAIL,
+ HAS_BUG_NUMBER,TXREP autolearn=ham autolearn_force=no
+ version=3.4.1-bugs.debian.org_2005_01_02
+X-Spam-Bayes: score:0.0000 Tokens: new, 16; hammy, 110; neutral, 41; spammy,
+ 2. spammytokens:0.971-+--privacy, 0.857-+--customized
+ hammytokens:0.000-+--UD:el, 0.000-+--H*F:U*bremner, 0.000-+--Maintainer,
+ 0.000-+--sitelisp, 0.000-+--site-lisp
+Received: from fethera.tethera.net ([2607:5300:60:c5::1])
+ by buxtehude.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <bremner@debian.org>)
+ id 1gFftO-0003U6-RV; Thu, 25 Oct 2018 13:41:42 +0000
+Received: from remotemail by fethera.tethera.net with local (Exim 4.89)
+ (envelope-from <bremner@debian.org>)
+ id 1gFftL-0004VK-V8; Thu, 25 Oct 2018 09:41:39 -0400
+Received: (nullmailer pid 477 invoked by uid 1000);
+ Thu, 25 Oct 2018 13:41:38 -0000
+From: David Bremner <bremner@debian.org>
+To: Samuel Bronson <naesten@gmail.com>, 695159@bugs.debian.org, Debian Bug Tracking System <submit@bugs.debian.org>
+In-Reply-To: <87vcch7hxy.fsf@naesten.dyndns.org>
+Date: Thu, 25 Oct 2018 10:41:38 -0300
+Message-ID: <87r2geywh9.fsf@tethera.net>
+MIME-Version: 1.0
+Content-Type: text/plain
+Delivered-To: submit@bugs.debian.org
+Delivered-To: emacs-goodies-el@packages.qa.debian.org
+Delivered-To: dispatch+emacs-goodies-el@tracker.debian.org
+X-Loop: dispatch@tracker.debian.org
+X-Distro-Tracker-Keyword: bts
+X-Distro-Tracker-Package: emacs-goodies-el
+List-Id: <emacs-goodies-el.tracker.debian.org>
+X-Debian: tracker.debian.org
+X-Debian-Package: emacs-goodies-el
+X-PTS-Package: emacs-goodies-el
+X-PTS-Keyword: bts
+X-Distro-Tracker-Team: emacsen
+X-Spam_score: -2.3
+X-Spam_score_int: -22
+X-Spam_bar: --
+
+
+Control: severity -1 minor
+Control: tag -1 moreinfo
+
+Samuel Bronson <naesten@gmail.com> writes:
+
+> Package: debian-el
+> Version: 35.2+nmu1
+> Severity: normal
+> File: /usr/share/emacs/site-lisp/debian-el/debian-bug.el
+>
+> Dear Maintainer,
+>
+> After being mildly annoyed with this for ages, it finally occurred to me
+> to file a bug about it:
+>
+> It's rather rude of `getdebian-bug-get-bug-as-email' to default to
+> sticking downloaded mbox files loose in ~/, isn't it?
+>
+> (And might it not make sense to try and use the same files as the bts(1)
+> command from the devscripts package?)
+>
+
+Hi Samuel
+
+There is already a variable "debian-bug-download-directory" which can be
+customized. Is there an obviously better default? I guess we could put
+things in /tmp by default, with a minor privacy leak on multiuser
+systems.
+
+d
+
+# body 2
--- /dev/null
+Return-path: <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>
+Envelope-to: david@tethera.net
+Delivery-date: Thu, 20 Dec 2018 13:27:11 -0500
+Received: from muffat.debian.org ([2607:f8f0:614:1::1274:33])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32M-0005ae-Ko
+ for david@tethera.net; Thu, 20 Dec 2018 13:27:11 -0500
+Received: from alioth-lists-01.debian.net ([2001:ba8:0:2c77:0:4:0:1])
+ by muffat.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32K-0008PT-Nm
+ for david@tethera.net; Thu, 20 Dec 2018 18:27:08 +0000
+Received: from localhost ([::1] helo=alioth-lists-01.debian.net)
+ by alioth-lists-01.debian.net with esmtp (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32I-0003to-Kn
+ for bremner@debian.org; Thu, 20 Dec 2018 18:27:06 +0000
+Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39])
+ by alioth-lists-01.debian.net with esmtps
+ (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>) id 1ga32H-0003tO-29
+ for pkg-emacsen-addons@lists.alioth.debian.org; Thu, 20 Dec 2018 18:27:05 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32F-0005PP-9m; Thu, 20 Dec 2018 18:27:03 +0000
+X-Loop: owner@bugs.debian.org
+Resent-From: Sean Whitton <spwhitton@spwhitton.name>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Emacs addons team
+ <pkg-emacsen-addons@lists.alioth.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Thu, 20 Dec 2018 18:27:02 +0000
+Resent-Message-ID: <handler.916805.B916805.154533033319811@bugs.debian.org>
+X-Debian-PR-Message: followup 916805
+X-Debian-PR-Package: src:assess-el
+X-Debian-PR-Keywords: ftbfs
+References: <87k1k6h43h.fsf@zephyr.silentflame.com>
+X-Debian-PR-Source: assess-el
+Received: via spool by 916805-submit@bugs.debian.org id=B916805.154533033319811
+ (code B ref 916805); Thu, 20 Dec 2018 18:27:02 +0000
+Received: (at 916805) by bugs.debian.org; 20 Dec 2018 18:25:33 +0000
+X-Spam-Checker-Version: SpamAssassin 3.4.2-bugs.debian.org_2005_01_02
+ (2018-09-13) on buxtehude.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-8.8 required=4.0 tests=BAYES_00,DKIM_SIGNED,
+ DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,PGPSIGNATURE,RCVD_IN_DNSWL_LOW,
+ SORTED_RECIPS,SPF_HELO_PASS,SPF_PASS,SUSPICIOUS_RECIPS,TXREP
+ autolearn=ham autolearn_force=no
+ version=3.4.2-bugs.debian.org_2005_01_02
+X-Spam-Bayes: score:0.0000 Tokens: new, 32; hammy, 83; neutral, 22; spammy, 1.
+ spammytokens:0.902-+--emails
+ hammytokens:0.000-+--HX-ME-Sender:xms,
+ 0.000-+--H*RU:10.202.2.43,
+ 0.000-+--Hx-spam-relays-external:10.202.2.43, 0.000-+--H*F:U*spwhitton,
+ 0.000-+--H*F:D*spwhitton.name
+Received: from wout2-smtp.messagingengine.com ([64.147.123.25])
+ by buxtehude.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89) (envelope-from <spwhitton@spwhitton.name>)
+ id 1ga30m-00059C-Kv; Thu, 20 Dec 2018 18:25:32 +0000
+Received: from compute3.internal (compute3.nyi.internal [10.202.2.43])
+ by mailout.west.internal (Postfix) with ESMTP id C50B712F6;
+ Thu, 20 Dec 2018 13:25:30 -0500 (EST)
+Received: from mailfrontend1 ([10.202.2.162])
+ by compute3.internal (MEProxy); Thu, 20 Dec 2018 13:25:31 -0500
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spwhitton.name;
+ h=from:to:subject:date:message-id:mime-version:content-type; s=
+ fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lqnNZlsr/mb10zqE=; b=X9S4kI14
+ zxdg9IKMIyALL2Eao3GTyyojuATJkgCqJvZWd8RWpty6RBStZfyeOqR1L3Gr5m3/
+ EVeiHQyFNnPor2xjMDmblfPS/u09JxVlc0KMpT0XXRfNWsVQn+U40nNRX15kXzZ/
+ D1rYhxpxzKRzU2tByUULCgbGlXAwJQtOXMDw3mpj1BxcoO13H/0H/KQTQ+AcpiOw
+ BV3JFKL/jA+mH8uAPIgNM2mUYZz5REO89eh3lPhLyc7tw745X+4ywZlo/Piqa0+6
+ BCldY9/nDR3csAUKx1+3hkpJPdqFALBWvG3SelGt44BqcoLsOJLB8QH6trCro39o
+ fEnBaUBTAkTAHA==
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-type:date:from:message-id
+ :mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender
+ :x-me-sender:x-sasl-enc; s=fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lq
+ nNZlsr/mb10zqE=; b=QYqLjUBZtfbO0DUlagvXRP2BPnpTgkvIna9/uCewkdoIH
+ /LuPW6DZUNWQa55qiDquqKXcs0tTODTEzYgeIDgqC+DDBTHnFQvXdWyS1X3o4sLL
+ 8dTKk8lv7M1/zKFxyg/ycNvPJGS9m4ZucGbxjwdgAcozhg7W1Qztxt9eVhPVnenS
+ 5sdeJ9mjIE7lYkKX4QVsXPOi86j6QlfMNyi/OnBfX2+95QiA/xPE/wEq4MYlLNm7
+ Av1P/8OrI4ImDKkOEivarktL+isYL7OXyGB4GfUTsydiy9dhP7RKPxrai1kJRu5S
+ b2470KXNatu2WkyMFrsdcwrSqyKIe096k5xPfVI2A==
+X-ME-Sender: <xms:mt4bXHFFQvAGVxMhHQP5DBif075kRubHE1KJQrR0OsDN2ClFFtlRXw>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedtkedrudejfedguddugecutefuodetggdotefrod
+ ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfhuthenuceurghilhhouhhtmecu
+ fedttdenucfuohhrthgvugcurhgvtghiphhsucdlgedtmdenucfjughrpefhvffufffkgg
+ gtsehgtderredttddtnecuhfhrohhmpefuvggrnhcuhghhihhtthhonhcuoehsphifhhhi
+ thhtohhnsehsphifhhhithhtohhnrdhnrghmvgeqnecurfgrrhgrmhepmhgrihhlfhhroh
+ hmpehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgenucevlhhushhtvghr
+ ufhiiigvpedt
+X-ME-Proxy: <xmx:mt4bXGqtKYX8StS4oLrucVpNHp3EoaoH6jbQkd2mTFzupx7FwIJ2dQ>
+ <xmx:mt4bXH457xpOO1PnlqWQoPt1r7kL_P9ta064wZO_JDW1QAycFDjIsg>
+ <xmx:mt4bXPQaOrPjDseNXftuMgX1Y9gyHDbVUCYSYNdX6oXwBQhAGwzqIw>
+ <xmx:mt4bXLbmA2mCpvakWE26qsCvS2IOX4eN0KdeU2tQi-SXYEBMLVpE3A>
+From: Sean Whitton <spwhitton@spwhitton.name>
+To: 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org,
+ 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org,
+ 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org,
+ 916876@bugs.debian.org
+Date: Thu, 20 Dec 2018 18:25:26 +0000
+Message-ID: <87r2ecrr6x.fsf@zephyr.silentflame.com>
+MIME-Version: 1.0
+Received-SPF: pass client-ip=2607:f8f0:614:1::1274:39;
+ envelope-from=debbugs@buxtehude.debian.org; helo=buxtehude.debian.org
+x-debian-approved: yes
+Subject: [Pkg-emacsen-addons] Bug#916805: Increase severity to 'serious'
+X-BeenThere: pkg-emacsen-addons@alioth-lists.debian.net
+X-Mailman-Version: 2.1.23
+Precedence: list
+List-Id: Maintainers list for Emacs addon packages
+ <pkg-emacsen-addons.alioth-lists.debian.net>
+List-Unsubscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/options/pkg-emacsen-addons>,
+ <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=unsubscribe>
+List-Archive: <http://alioth-lists.debian.net/pipermail/pkg-emacsen-addons/>
+List-Post: <mailto:pkg-emacsen-addons@alioth-lists.debian.net>
+List-Help: <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=help>
+List-Subscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-emacsen-addons>,
+ <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=subscribe>
+Reply-To: Sean Whitton <spwhitton@spwhitton.name>, 916805@bugs.debian.org
+Content-Type: multipart/mixed; boundary="===============5317466403067656157=="
+Errors-To: pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net
+Sender: "Pkg-emacsen-addons"
+ <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>
+X-Spam_score: 2.8
+X-Spam_score_int: 28
+X-Spam_bar: ++
+
+--===============5317466403067656157==
+Content-Type: multipart/signed; boundary="=-=-=";
+ micalg=pgp-sha512; protocol="application/pgp-signature"
+
+--=-=-=
+Content-Type: text/plain
+Content-Transfer-Encoding: quoted-printable
+
+control: severity -1 serious
+
+Hello,
+
+Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+all the e-mails).
+
+=2D-=20
+Sean Whitton
+
+--=-=-=
+Content-Type: application/pgp-signature; name="signature.asc"
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEm5FwB64DDjbk/CSLaVt65L8GYkAFAlwb3pYACgkQaVt65L8G
+YkDLrhAAhORxZzZDE5vlXRm89JYA3jd9OyleioZvDDRCrpEd7CQ5AHiMMJizW1lU
+gn6OBIoW4O04TZ5oOuUnDHK/rS0G4zgNCJUyNf06zVECmdvkzspNNpQ3J5aOi4t2
+lhjRIFOKA9ifGsEqYLwP2dork1xFuyHEqHkDH8zpCTvdzkWky1bwAD/Pj5dArd7t
+FeQGsPm7/64H1/rHk8pSP2pQgRsMDX6rIdx3vuQ7r+NssdRq+II4e479l02TiCDi
+FBOX+n3nPXxREPdZ9EKL4SauL/AnRqpeC9GX6fC9OOnQeQ1xVTzNWKa6ixrqkFoH
+TI/vy51p16jFNgdkLkyLtZA8Tq72TIAKWbZC0GFzWJVNASWu7WDIoMn5pgoi454w
+TgsvK9MOnEYeABiDUa1ppaoMiP4+3j5yT0eWttTMSkcKjk1Ap1o+RfUxlIGl0Rog
+ShbG2y6Mv8FERtjzPVQ7VMLDN9zRIbtlSJFm7CboPNSAygzzzaA/RIN/e8MdbZoM
+a8AT9KiAVHEEcw+nWFAatAew5VP9iRZVgrVdWBszuaWOolxnYvpAL45WanqG0eab
+VMe66+rZ8momI0MsM9JcqBwXO+fOf8CrPSO9PL8VFEJXFLZQS7asFStJf2l8msWE
+3IYhvk4B6Nf1R96XzpXLlkOnoGtcnPVAvotrGU/rDfk5i/WF810=
+=mWfF
+-----END PGP SIGNATURE-----
+--=-=-=--
+
+
+--===============5317466403067656157==
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: base64
+Content-Disposition: inline
+
+X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KUGtnLWVtYWNz
+ZW4tYWRkb25zIG1haWxpbmcgbGlzdApQa2ctZW1hY3Nlbi1hZGRvbnNAYWxpb3RoLWxpc3RzLmRl
+Ymlhbi5uZXQKaHR0cHM6Ly9hbGlvdGgtbGlzdHMuZGViaWFuLm5ldC9jZ2ktYmluL21haWxtYW4v
+bGlzdGluZm8vcGtnLWVtYWNzZW4tYWRkb25zCg==
+
+--===============5317466403067656157==--
+
--- /dev/null
+Return-path: <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>
+Envelope-to: david@tethera.net
+Delivery-date: Thu, 20 Dec 2018 13:27:12 -0500
+Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32O-0005ap-Hn
+ for david@tethera.net; Thu, 20 Dec 2018 13:27:12 -0500
+Received: from alioth-lists-01.debian.net ([2001:ba8:0:2c77:0:4:0:1])
+ by mailly.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32O-0004V7-4x
+ for david@tethera.net; Thu, 20 Dec 2018 18:27:12 +0000
+Received: from localhost ([::1] helo=alioth-lists-01.debian.net)
+ by alioth-lists-01.debian.net with esmtp (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32N-0003wA-Sc
+ for bremner@debian.org; Thu, 20 Dec 2018 18:27:11 +0000
+Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39])
+ by alioth-lists-01.debian.net with esmtps
+ (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>) id 1ga32M-0003vQ-Q9
+ for pkg-emacsen-addons@lists.alioth.debian.org; Thu, 20 Dec 2018 18:27:10 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32K-0005Sn-7B; Thu, 20 Dec 2018 18:27:08 +0000
+X-Loop: owner@bugs.debian.org
+Resent-From: Sean Whitton <spwhitton@spwhitton.name>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Emacs addons team
+ <pkg-emacsen-addons@lists.alioth.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Thu, 20 Dec 2018 18:27:06 +0000
+Resent-Message-ID: <handler.916808.B916808.154533033319830@bugs.debian.org>
+X-Debian-PR-Message: followup 916808
+X-Debian-PR-Package: src:hydra-el
+X-Debian-PR-Keywords: ftbfs
+References: <87bm5ih3w5.fsf@zephyr.silentflame.com>
+X-Debian-PR-Source: hydra-el
+Received: via spool by 916808-submit@bugs.debian.org id=B916808.154533033319830
+ (code B ref 916808); Thu, 20 Dec 2018 18:27:06 +0000
+Received: (at 916808) by bugs.debian.org; 20 Dec 2018 18:25:33 +0000
+X-Spam-Checker-Version: SpamAssassin 3.4.2-bugs.debian.org_2005_01_02
+ (2018-09-13) on buxtehude.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-11.3 required=4.0 tests=BAYES_00,DKIM_SIGNED,
+ DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,PGPSIGNATURE,RCVD_IN_DNSWL_LOW,
+ SORTED_RECIPS,SPF_HELO_PASS,SPF_PASS,SUSPICIOUS_RECIPS,TXREP
+ autolearn=unavailable autolearn_force=no
+ version=3.4.2-bugs.debian.org_2005_01_02
+X-Spam-Bayes: score:0.0000 Tokens: new, 0; hammy, 115; neutral, 22; spammy, 1.
+ spammytokens:0.902-+--emails
+ hammytokens:0.000-+--HX-ME-Sender:xms,
+ 0.000-+--H*RU:10.202.2.43,
+ 0.000-+--Hx-spam-relays-external:10.202.2.43, 0.000-+--H*F:U*spwhitton,
+ 0.000-+--H*F:D*spwhitton.name
+Received: from wout2-smtp.messagingengine.com ([64.147.123.25])
+ by buxtehude.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89) (envelope-from <spwhitton@spwhitton.name>)
+ id 1ga30m-00059C-Kv; Thu, 20 Dec 2018 18:25:32 +0000
+Received: from compute3.internal (compute3.nyi.internal [10.202.2.43])
+ by mailout.west.internal (Postfix) with ESMTP id C50B712F6;
+ Thu, 20 Dec 2018 13:25:30 -0500 (EST)
+Received: from mailfrontend1 ([10.202.2.162])
+ by compute3.internal (MEProxy); Thu, 20 Dec 2018 13:25:31 -0500
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spwhitton.name;
+ h=from:to:subject:date:message-id:mime-version:content-type; s=
+ fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lqnNZlsr/mb10zqE=; b=X9S4kI14
+ zxdg9IKMIyALL2Eao3GTyyojuATJkgCqJvZWd8RWpty6RBStZfyeOqR1L3Gr5m3/
+ EVeiHQyFNnPor2xjMDmblfPS/u09JxVlc0KMpT0XXRfNWsVQn+U40nNRX15kXzZ/
+ D1rYhxpxzKRzU2tByUULCgbGlXAwJQtOXMDw3mpj1BxcoO13H/0H/KQTQ+AcpiOw
+ BV3JFKL/jA+mH8uAPIgNM2mUYZz5REO89eh3lPhLyc7tw745X+4ywZlo/Piqa0+6
+ BCldY9/nDR3csAUKx1+3hkpJPdqFALBWvG3SelGt44BqcoLsOJLB8QH6trCro39o
+ fEnBaUBTAkTAHA==
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-type:date:from:message-id
+ :mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender
+ :x-me-sender:x-sasl-enc; s=fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lq
+ nNZlsr/mb10zqE=; b=QYqLjUBZtfbO0DUlagvXRP2BPnpTgkvIna9/uCewkdoIH
+ /LuPW6DZUNWQa55qiDquqKXcs0tTODTEzYgeIDgqC+DDBTHnFQvXdWyS1X3o4sLL
+ 8dTKk8lv7M1/zKFxyg/ycNvPJGS9m4ZucGbxjwdgAcozhg7W1Qztxt9eVhPVnenS
+ 5sdeJ9mjIE7lYkKX4QVsXPOi86j6QlfMNyi/OnBfX2+95QiA/xPE/wEq4MYlLNm7
+ Av1P/8OrI4ImDKkOEivarktL+isYL7OXyGB4GfUTsydiy9dhP7RKPxrai1kJRu5S
+ b2470KXNatu2WkyMFrsdcwrSqyKIe096k5xPfVI2A==
+X-ME-Sender: <xms:mt4bXHFFQvAGVxMhHQP5DBif075kRubHE1KJQrR0OsDN2ClFFtlRXw>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedtkedrudejfedguddugecutefuodetggdotefrod
+ ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfhuthenuceurghilhhouhhtmecu
+ fedttdenucfuohhrthgvugcurhgvtghiphhsucdlgedtmdenucfjughrpefhvffufffkgg
+ gtsehgtderredttddtnecuhfhrohhmpefuvggrnhcuhghhihhtthhonhcuoehsphifhhhi
+ thhtohhnsehsphifhhhithhtohhnrdhnrghmvgeqnecurfgrrhgrmhepmhgrihhlfhhroh
+ hmpehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgenucevlhhushhtvghr
+ ufhiiigvpedt
+X-ME-Proxy: <xmx:mt4bXGqtKYX8StS4oLrucVpNHp3EoaoH6jbQkd2mTFzupx7FwIJ2dQ>
+ <xmx:mt4bXH457xpOO1PnlqWQoPt1r7kL_P9ta064wZO_JDW1QAycFDjIsg>
+ <xmx:mt4bXPQaOrPjDseNXftuMgX1Y9gyHDbVUCYSYNdX6oXwBQhAGwzqIw>
+ <xmx:mt4bXLbmA2mCpvakWE26qsCvS2IOX4eN0KdeU2tQi-SXYEBMLVpE3A>
+From: Sean Whitton <spwhitton@spwhitton.name>
+To: 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org,
+ 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org,
+ 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org,
+ 916876@bugs.debian.org
+Date: Thu, 20 Dec 2018 18:25:26 +0000
+Message-ID: <87r2ecrr6x.fsf@zephyr.silentflame.com>
+MIME-Version: 1.0
+X-CrossAssassin-Score: 3
+Received-SPF: pass client-ip=2607:f8f0:614:1::1274:39;
+ envelope-from=debbugs@buxtehude.debian.org; helo=buxtehude.debian.org
+x-debian-approved: yes
+Subject: [Pkg-emacsen-addons] Bug#916808: Increase severity to 'serious'
+X-BeenThere: pkg-emacsen-addons@alioth-lists.debian.net
+X-Mailman-Version: 2.1.23
+Precedence: list
+List-Id: Maintainers list for Emacs addon packages
+ <pkg-emacsen-addons.alioth-lists.debian.net>
+List-Unsubscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/options/pkg-emacsen-addons>,
+ <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=unsubscribe>
+List-Archive: <http://alioth-lists.debian.net/pipermail/pkg-emacsen-addons/>
+List-Post: <mailto:pkg-emacsen-addons@alioth-lists.debian.net>
+List-Help: <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=help>
+List-Subscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-emacsen-addons>,
+ <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=subscribe>
+Reply-To: Sean Whitton <spwhitton@spwhitton.name>, 916808@bugs.debian.org
+Content-Type: multipart/mixed; boundary="===============8231894308137086149=="
+Errors-To: pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net
+Sender: "Pkg-emacsen-addons"
+ <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>
+X-Spam_score: 2.8
+X-Spam_score_int: 28
+X-Spam_bar: ++
+
+--===============8231894308137086149==
+Content-Type: multipart/signed; boundary="=-=-=";
+ micalg=pgp-sha512; protocol="application/pgp-signature"
+
+--=-=-=
+Content-Type: text/plain
+Content-Transfer-Encoding: quoted-printable
+
+control: severity -1 serious
+
+Hello,
+
+Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+all the e-mails).
+
+=2D-=20
+Sean Whitton
+
+--=-=-=
+Content-Type: application/pgp-signature; name="signature.asc"
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEm5FwB64DDjbk/CSLaVt65L8GYkAFAlwb3pYACgkQaVt65L8G
+YkDLrhAAhORxZzZDE5vlXRm89JYA3jd9OyleioZvDDRCrpEd7CQ5AHiMMJizW1lU
+gn6OBIoW4O04TZ5oOuUnDHK/rS0G4zgNCJUyNf06zVECmdvkzspNNpQ3J5aOi4t2
+lhjRIFOKA9ifGsEqYLwP2dork1xFuyHEqHkDH8zpCTvdzkWky1bwAD/Pj5dArd7t
+FeQGsPm7/64H1/rHk8pSP2pQgRsMDX6rIdx3vuQ7r+NssdRq+II4e479l02TiCDi
+FBOX+n3nPXxREPdZ9EKL4SauL/AnRqpeC9GX6fC9OOnQeQ1xVTzNWKa6ixrqkFoH
+TI/vy51p16jFNgdkLkyLtZA8Tq72TIAKWbZC0GFzWJVNASWu7WDIoMn5pgoi454w
+TgsvK9MOnEYeABiDUa1ppaoMiP4+3j5yT0eWttTMSkcKjk1Ap1o+RfUxlIGl0Rog
+ShbG2y6Mv8FERtjzPVQ7VMLDN9zRIbtlSJFm7CboPNSAygzzzaA/RIN/e8MdbZoM
+a8AT9KiAVHEEcw+nWFAatAew5VP9iRZVgrVdWBszuaWOolxnYvpAL45WanqG0eab
+VMe66+rZ8momI0MsM9JcqBwXO+fOf8CrPSO9PL8VFEJXFLZQS7asFStJf2l8msWE
+3IYhvk4B6Nf1R96XzpXLlkOnoGtcnPVAvotrGU/rDfk5i/WF810=
+=mWfF
+-----END PGP SIGNATURE-----
+--=-=-=--
+
+
+--===============8231894308137086149==
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: base64
+Content-Disposition: inline
+
+X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KUGtnLWVtYWNz
+ZW4tYWRkb25zIG1haWxpbmcgbGlzdApQa2ctZW1hY3Nlbi1hZGRvbnNAYWxpb3RoLWxpc3RzLmRl
+Ymlhbi5uZXQKaHR0cHM6Ly9hbGlvdGgtbGlzdHMuZGViaWFuLm5ldC9jZ2ktYmluL21haWxtYW4v
+bGlzdGluZm8vcGtnLWVtYWNzZW4tYWRkb25zCg==
+
+--===============8231894308137086149==--
+
--- /dev/null
+Return-path: <bounces+20181220-bremner=debian.org@tracker.debian.org>
+Envelope-to: david@tethera.net
+Delivery-date: Thu, 20 Dec 2018 13:27:16 -0500
+Received: from muffat.debian.org ([2607:f8f0:614:1::1274:33])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <bounces+20181220-bremner=debian.org@tracker.debian.org>)
+ id 1ga32S-0005aw-5h
+ for david@tethera.net; Thu, 20 Dec 2018 13:27:16 -0500
+Received: from ticharich.debian.org ([2001:41c8:1000:21::21:23])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=ticharich.debian.org,EMAIL=hostmaster@ticharich.debian.org (verified)
+ by muffat.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <bounces+20181220-bremner=debian.org@tracker.debian.org>)
+ id 1ga32R-0008QQ-PL
+ for david@tethera.net; Thu, 20 Dec 2018 18:27:15 +0000
+Received: from localhost ([::1] helo=ticharich.debian.org)
+ by ticharich.debian.org with esmtp (Exim 4.89)
+ (envelope-from <bounces+20181220-bremner=debian.org@tracker.debian.org>)
+ id 1ga32Q-0008BB-Ad
+ for david@tethera.net; Thu, 20 Dec 2018 18:27:14 +0000
+Received: from muffat.debian.org ([2607:f8f0:614:1::1274:33])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=muffat.debian.org,EMAIL=hostmaster@muffat.debian.org (verified)
+ by ticharich.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32Q-0008Ai-2N
+ for dispatch+haskell-mode@tracker.debian.org; Thu, 20 Dec 2018 18:27:14 +0000
+Received: from quantz.debian.org ([2001:41c8:1000:21::21:28])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=quantz.debian.org,EMAIL=hostmaster@quantz.debian.org (verified)
+ by muffat.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32O-0008QE-Eo
+ for dispatch+haskell-mode@tracker.debian.org; Thu, 20 Dec 2018 18:27:12 +0000
+Received: from qa by quantz.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32M-0006hb-Vn
+ for dispatch+haskell-mode@tracker.debian.org; Thu, 20 Dec 2018 18:27:10 +0000
+Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39])
+ from C=NA,ST=NA,L=Ankh Morpork,O=Debian SMTP,OU=Debian SMTP CA,CN=buxtehude.debian.org,EMAIL=hostmaster@buxtehude.debian.org (verified)
+ by quantz.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32J-0006gG-1x
+ for haskell-mode@packages.qa.debian.org; Thu, 20 Dec 2018 18:27:07 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32H-0005SF-MF; Thu, 20 Dec 2018 18:27:05 +0000
+X-Loop: owner@bugs.debian.org
+Subject: Bug#916807: Increase severity to 'serious'
+Reply-To: Sean Whitton <spwhitton@spwhitton.name>, 916807@bugs.debian.org
+Resent-From: Sean Whitton <spwhitton@spwhitton.name>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Emacs addons team <pkg-emacsen-addons@lists.alioth.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Thu, 20 Dec 2018 18:27:04 +0000
+Resent-Message-ID: <handler.916807.B916807.154533033319820@bugs.debian.org>
+X-Debian-PR-Message: followup 916807
+X-Debian-PR-Package: src:haskell-mode
+X-Debian-PR-Keywords: ftbfs
+References: <87efaeh3zr.fsf@zephyr.silentflame.com>
+X-Debian-PR-Source: haskell-mode
+Received: via spool by 916807-submit@bugs.debian.org id=B916807.154533033319820
+ (code B ref 916807); Thu, 20 Dec 2018 18:27:04 +0000
+Received: (at 916807) by bugs.debian.org; 20 Dec 2018 18:25:33 +0000
+X-Spam-Checker-Version: SpamAssassin 3.4.2-bugs.debian.org_2005_01_02
+ (2018-09-13) on buxtehude.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-11.3 required=4.0 tests=BAYES_00,DKIM_SIGNED,
+ DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,PGPSIGNATURE,RCVD_IN_DNSWL_LOW,
+ SORTED_RECIPS,SPF_HELO_PASS,SPF_PASS,SUSPICIOUS_RECIPS,TXREP
+ autolearn=unavailable autolearn_force=no
+ version=3.4.2-bugs.debian.org_2005_01_02
+X-Spam-Bayes: score:0.0000 Tokens: new, 0; hammy, 115; neutral, 22; spammy, 1.
+ spammytokens:0.902-+--emails hammytokens:0.000-+--HX-ME-Sender:xms,
+ 0.000-+--H*RU:10.202.2.43,
+ 0.000-+--Hx-spam-relays-external:10.202.2.43,
+ 0.000-+--H*F:D*spwhitton.name, 0.000-+--H*F:U*spwhitton
+Received: from wout2-smtp.messagingengine.com ([64.147.123.25])
+ by buxtehude.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <spwhitton@spwhitton.name>)
+ id 1ga30m-00059C-Kv; Thu, 20 Dec 2018 18:25:32 +0000
+Received: from compute3.internal (compute3.nyi.internal [10.202.2.43])
+ by mailout.west.internal (Postfix) with ESMTP id C50B712F6;
+ Thu, 20 Dec 2018 13:25:30 -0500 (EST)
+Received: from mailfrontend1 ([10.202.2.162])
+ by compute3.internal (MEProxy); Thu, 20 Dec 2018 13:25:31 -0500
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spwhitton.name;
+ h=from:to:subject:date:message-id:mime-version:content-type; s=
+ fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lqnNZlsr/mb10zqE=; b=X9S4kI14
+ zxdg9IKMIyALL2Eao3GTyyojuATJkgCqJvZWd8RWpty6RBStZfyeOqR1L3Gr5m3/
+ EVeiHQyFNnPor2xjMDmblfPS/u09JxVlc0KMpT0XXRfNWsVQn+U40nNRX15kXzZ/
+ D1rYhxpxzKRzU2tByUULCgbGlXAwJQtOXMDw3mpj1BxcoO13H/0H/KQTQ+AcpiOw
+ BV3JFKL/jA+mH8uAPIgNM2mUYZz5REO89eh3lPhLyc7tw745X+4ywZlo/Piqa0+6
+ BCldY9/nDR3csAUKx1+3hkpJPdqFALBWvG3SelGt44BqcoLsOJLB8QH6trCro39o
+ fEnBaUBTAkTAHA==
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-type:date:from:message-id
+ :mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender
+ :x-me-sender:x-sasl-enc; s=fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lq
+ nNZlsr/mb10zqE=; b=QYqLjUBZtfbO0DUlagvXRP2BPnpTgkvIna9/uCewkdoIH
+ /LuPW6DZUNWQa55qiDquqKXcs0tTODTEzYgeIDgqC+DDBTHnFQvXdWyS1X3o4sLL
+ 8dTKk8lv7M1/zKFxyg/ycNvPJGS9m4ZucGbxjwdgAcozhg7W1Qztxt9eVhPVnenS
+ 5sdeJ9mjIE7lYkKX4QVsXPOi86j6QlfMNyi/OnBfX2+95QiA/xPE/wEq4MYlLNm7
+ Av1P/8OrI4ImDKkOEivarktL+isYL7OXyGB4GfUTsydiy9dhP7RKPxrai1kJRu5S
+ b2470KXNatu2WkyMFrsdcwrSqyKIe096k5xPfVI2A==
+X-ME-Sender: <xms:mt4bXHFFQvAGVxMhHQP5DBif075kRubHE1KJQrR0OsDN2ClFFtlRXw>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedtkedrudejfedguddugecutefuodetggdotefrod
+ ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfhuthenuceurghilhhouhhtmecu
+ fedttdenucfuohhrthgvugcurhgvtghiphhsucdlgedtmdenucfjughrpefhvffufffkgg
+ gtsehgtderredttddtnecuhfhrohhmpefuvggrnhcuhghhihhtthhonhcuoehsphifhhhi
+ thhtohhnsehsphifhhhithhtohhnrdhnrghmvgeqnecurfgrrhgrmhepmhgrihhlfhhroh
+ hmpehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgenucevlhhushhtvghr
+ ufhiiigvpedt
+X-ME-Proxy: <xmx:mt4bXGqtKYX8StS4oLrucVpNHp3EoaoH6jbQkd2mTFzupx7FwIJ2dQ>
+ <xmx:mt4bXH457xpOO1PnlqWQoPt1r7kL_P9ta064wZO_JDW1QAycFDjIsg>
+ <xmx:mt4bXPQaOrPjDseNXftuMgX1Y9gyHDbVUCYSYNdX6oXwBQhAGwzqIw>
+ <xmx:mt4bXLbmA2mCpvakWE26qsCvS2IOX4eN0KdeU2tQi-SXYEBMLVpE3A>
+From: Sean Whitton <spwhitton@spwhitton.name>
+To: 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org, 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org, 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org, 916876@bugs.debian.org
+Date: Thu, 20 Dec 2018 18:25:26 +0000
+Message-ID: <87r2ecrr6x.fsf@zephyr.silentflame.com>
+MIME-Version: 1.0
+Content-Type: multipart/signed; boundary="=-=-=";
+ micalg=pgp-sha512; protocol="application/pgp-signature"
+X-CrossAssassin-Score: 2
+Delivered-To: haskell-mode@packages.qa.debian.org
+Delivered-To: dispatch+haskell-mode@tracker.debian.org
+X-Loop: dispatch@tracker.debian.org
+X-Distro-Tracker-Keyword: bts
+X-Distro-Tracker-Package: haskell-mode
+List-Id: <haskell-mode.tracker.debian.org>
+X-Debian: tracker.debian.org
+X-Debian-Package: haskell-mode
+X-PTS-Package: haskell-mode
+X-PTS-Keyword: bts
+Precedence: list
+List-Unsubscribe: <mailto:control@tracker.debian.org?body=unsubscribe%20haskell-mode>
+X-Spam_score: 2.8
+X-Spam_score_int: 28
+X-Spam_bar: ++
+
+--=-=-=
+Content-Type: text/plain
+Content-Transfer-Encoding: quoted-printable
+
+control: severity -1 serious
+
+Hello,
+
+Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+all the e-mails).
+
+=2D-=20
+Sean Whitton
+
+--=-=-=
+Content-Type: application/pgp-signature; name="signature.asc"
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEm5FwB64DDjbk/CSLaVt65L8GYkAFAlwb3pYACgkQaVt65L8G
+YkDLrhAAhORxZzZDE5vlXRm89JYA3jd9OyleioZvDDRCrpEd7CQ5AHiMMJizW1lU
+gn6OBIoW4O04TZ5oOuUnDHK/rS0G4zgNCJUyNf06zVECmdvkzspNNpQ3J5aOi4t2
+lhjRIFOKA9ifGsEqYLwP2dork1xFuyHEqHkDH8zpCTvdzkWky1bwAD/Pj5dArd7t
+FeQGsPm7/64H1/rHk8pSP2pQgRsMDX6rIdx3vuQ7r+NssdRq+II4e479l02TiCDi
+FBOX+n3nPXxREPdZ9EKL4SauL/AnRqpeC9GX6fC9OOnQeQ1xVTzNWKa6ixrqkFoH
+TI/vy51p16jFNgdkLkyLtZA8Tq72TIAKWbZC0GFzWJVNASWu7WDIoMn5pgoi454w
+TgsvK9MOnEYeABiDUa1ppaoMiP4+3j5yT0eWttTMSkcKjk1Ap1o+RfUxlIGl0Rog
+ShbG2y6Mv8FERtjzPVQ7VMLDN9zRIbtlSJFm7CboPNSAygzzzaA/RIN/e8MdbZoM
+a8AT9KiAVHEEcw+nWFAatAew5VP9iRZVgrVdWBszuaWOolxnYvpAL45WanqG0eab
+VMe66+rZ8momI0MsM9JcqBwXO+fOf8CrPSO9PL8VFEJXFLZQS7asFStJf2l8msWE
+3IYhvk4B6Nf1R96XzpXLlkOnoGtcnPVAvotrGU/rDfk5i/WF810=
+=mWfF
+-----END PGP SIGNATURE-----
+--=-=-=--
+
--- /dev/null
+Return-path: <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>
+Envelope-to: david@tethera.net
+Delivery-date: Thu, 20 Dec 2018 13:27:16 -0500
+Received: from mailly.debian.org ([2001:41b8:202:deb:6564:a62:52c3:4b72])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32S-0005ax-Kz
+ for david@tethera.net; Thu, 20 Dec 2018 13:27:16 -0500
+Received: from alioth-lists-01.debian.net ([2001:ba8:0:2c77:0:4:0:1])
+ by mailly.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32S-0004W6-8i
+ for david@tethera.net; Thu, 20 Dec 2018 18:27:16 +0000
+Received: from localhost ([::1] helo=alioth-lists-01.debian.net)
+ by alioth-lists-01.debian.net with esmtp (Exim 4.89)
+ (envelope-from <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>)
+ id 1ga32S-0003xQ-0U
+ for bremner@debian.org; Thu, 20 Dec 2018 18:27:16 +0000
+Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39])
+ by alioth-lists-01.debian.net with esmtps
+ (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>) id 1ga32Q-0003wj-1x
+ for pkg-emacsen-addons@lists.alioth.debian.org; Thu, 20 Dec 2018 18:27:14 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32O-0005Te-Sa; Thu, 20 Dec 2018 18:27:12 +0000
+X-Loop: owner@bugs.debian.org
+Resent-From: Sean Whitton <spwhitton@spwhitton.name>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Emacs addons team
+ <pkg-emacsen-addons@lists.alioth.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Thu, 20 Dec 2018 18:27:11 +0000
+Resent-Message-ID: <handler.916811.B916811.154533033319851@bugs.debian.org>
+X-Debian-PR-Message: followup 916811
+X-Debian-PR-Package: src:weechat-el
+X-Debian-PR-Keywords: ftbfs
+References: <8736quh3la.fsf@zephyr.silentflame.com>
+X-Debian-PR-Source: weechat-el
+Received: via spool by 916811-submit@bugs.debian.org id=B916811.154533033319851
+ (code B ref 916811); Thu, 20 Dec 2018 18:27:11 +0000
+Received: (at 916811) by bugs.debian.org; 20 Dec 2018 18:25:33 +0000
+X-Spam-Checker-Version: SpamAssassin 3.4.2-bugs.debian.org_2005_01_02
+ (2018-09-13) on buxtehude.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-15.2 required=4.0 tests=BAYES_00,DKIM_SIGNED,
+ DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,PGPSIGNATURE,RCVD_IN_DNSWL_LOW,
+ SORTED_RECIPS,SPF_HELO_PASS,SPF_PASS,SUSPICIOUS_RECIPS,TXREP
+ autolearn=unavailable autolearn_force=no
+ version=3.4.2-bugs.debian.org_2005_01_02
+X-Spam-Bayes: score:0.0000 Tokens: new, 0; hammy, 115; neutral, 22; spammy, 1.
+ spammytokens:0.902-+--emails
+ hammytokens:0.000-+--HX-ME-Sender:xms,
+ 0.000-+--H*RU:10.202.2.43,
+ 0.000-+--Hx-spam-relays-external:10.202.2.43,
+ 0.000-+--H*F:D*spwhitton.name, 0.000-+--H*F:U*spwhitton
+Received: from wout2-smtp.messagingengine.com ([64.147.123.25])
+ by buxtehude.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89) (envelope-from <spwhitton@spwhitton.name>)
+ id 1ga30m-00059C-Kv; Thu, 20 Dec 2018 18:25:32 +0000
+Received: from compute3.internal (compute3.nyi.internal [10.202.2.43])
+ by mailout.west.internal (Postfix) with ESMTP id C50B712F6;
+ Thu, 20 Dec 2018 13:25:30 -0500 (EST)
+Received: from mailfrontend1 ([10.202.2.162])
+ by compute3.internal (MEProxy); Thu, 20 Dec 2018 13:25:31 -0500
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spwhitton.name;
+ h=from:to:subject:date:message-id:mime-version:content-type; s=
+ fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lqnNZlsr/mb10zqE=; b=X9S4kI14
+ zxdg9IKMIyALL2Eao3GTyyojuATJkgCqJvZWd8RWpty6RBStZfyeOqR1L3Gr5m3/
+ EVeiHQyFNnPor2xjMDmblfPS/u09JxVlc0KMpT0XXRfNWsVQn+U40nNRX15kXzZ/
+ D1rYhxpxzKRzU2tByUULCgbGlXAwJQtOXMDw3mpj1BxcoO13H/0H/KQTQ+AcpiOw
+ BV3JFKL/jA+mH8uAPIgNM2mUYZz5REO89eh3lPhLyc7tw745X+4ywZlo/Piqa0+6
+ BCldY9/nDR3csAUKx1+3hkpJPdqFALBWvG3SelGt44BqcoLsOJLB8QH6trCro39o
+ fEnBaUBTAkTAHA==
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-type:date:from:message-id
+ :mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender
+ :x-me-sender:x-sasl-enc; s=fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lq
+ nNZlsr/mb10zqE=; b=QYqLjUBZtfbO0DUlagvXRP2BPnpTgkvIna9/uCewkdoIH
+ /LuPW6DZUNWQa55qiDquqKXcs0tTODTEzYgeIDgqC+DDBTHnFQvXdWyS1X3o4sLL
+ 8dTKk8lv7M1/zKFxyg/ycNvPJGS9m4ZucGbxjwdgAcozhg7W1Qztxt9eVhPVnenS
+ 5sdeJ9mjIE7lYkKX4QVsXPOi86j6QlfMNyi/OnBfX2+95QiA/xPE/wEq4MYlLNm7
+ Av1P/8OrI4ImDKkOEivarktL+isYL7OXyGB4GfUTsydiy9dhP7RKPxrai1kJRu5S
+ b2470KXNatu2WkyMFrsdcwrSqyKIe096k5xPfVI2A==
+X-ME-Sender: <xms:mt4bXHFFQvAGVxMhHQP5DBif075kRubHE1KJQrR0OsDN2ClFFtlRXw>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedtkedrudejfedguddugecutefuodetggdotefrod
+ ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfhuthenuceurghilhhouhhtmecu
+ fedttdenucfuohhrthgvugcurhgvtghiphhsucdlgedtmdenucfjughrpefhvffufffkgg
+ gtsehgtderredttddtnecuhfhrohhmpefuvggrnhcuhghhihhtthhonhcuoehsphifhhhi
+ thhtohhnsehsphifhhhithhtohhnrdhnrghmvgeqnecurfgrrhgrmhepmhgrihhlfhhroh
+ hmpehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgenucevlhhushhtvghr
+ ufhiiigvpedt
+X-ME-Proxy: <xmx:mt4bXGqtKYX8StS4oLrucVpNHp3EoaoH6jbQkd2mTFzupx7FwIJ2dQ>
+ <xmx:mt4bXH457xpOO1PnlqWQoPt1r7kL_P9ta064wZO_JDW1QAycFDjIsg>
+ <xmx:mt4bXPQaOrPjDseNXftuMgX1Y9gyHDbVUCYSYNdX6oXwBQhAGwzqIw>
+ <xmx:mt4bXLbmA2mCpvakWE26qsCvS2IOX4eN0KdeU2tQi-SXYEBMLVpE3A>
+From: Sean Whitton <spwhitton@spwhitton.name>
+To: 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org,
+ 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org,
+ 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org,
+ 916876@bugs.debian.org
+Date: Thu, 20 Dec 2018 18:25:26 +0000
+Message-ID: <87r2ecrr6x.fsf@zephyr.silentflame.com>
+MIME-Version: 1.0
+X-CrossAssassin-Score: 5
+Received-SPF: pass client-ip=2607:f8f0:614:1::1274:39;
+ envelope-from=debbugs@buxtehude.debian.org; helo=buxtehude.debian.org
+x-debian-approved: yes
+Subject: [Pkg-emacsen-addons] Bug#916811: Increase severity to 'serious'
+X-BeenThere: pkg-emacsen-addons@alioth-lists.debian.net
+X-Mailman-Version: 2.1.23
+Precedence: list
+List-Id: Maintainers list for Emacs addon packages
+ <pkg-emacsen-addons.alioth-lists.debian.net>
+List-Unsubscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/options/pkg-emacsen-addons>,
+ <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=unsubscribe>
+List-Archive: <http://alioth-lists.debian.net/pipermail/pkg-emacsen-addons/>
+List-Post: <mailto:pkg-emacsen-addons@alioth-lists.debian.net>
+List-Help: <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=help>
+List-Subscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-emacsen-addons>,
+ <mailto:pkg-emacsen-addons-request@alioth-lists.debian.net?subject=subscribe>
+Reply-To: Sean Whitton <spwhitton@spwhitton.name>, 916811@bugs.debian.org
+Content-Type: multipart/mixed; boundary="===============5359377007738902760=="
+Errors-To: pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net
+Sender: "Pkg-emacsen-addons"
+ <pkg-emacsen-addons-bounces+bremner=debian.org@alioth-lists.debian.net>
+X-Spam_score: 2.8
+X-Spam_score_int: 28
+X-Spam_bar: ++
+
+--===============5359377007738902760==
+Content-Type: multipart/signed; boundary="=-=-=";
+ micalg=pgp-sha512; protocol="application/pgp-signature"
+
+--=-=-=
+Content-Type: text/plain
+Content-Transfer-Encoding: quoted-printable
+
+control: severity -1 serious
+
+Hello,
+
+Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+all the e-mails).
+
+=2D-=20
+Sean Whitton
+
+--=-=-=
+Content-Type: application/pgp-signature; name="signature.asc"
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEm5FwB64DDjbk/CSLaVt65L8GYkAFAlwb3pYACgkQaVt65L8G
+YkDLrhAAhORxZzZDE5vlXRm89JYA3jd9OyleioZvDDRCrpEd7CQ5AHiMMJizW1lU
+gn6OBIoW4O04TZ5oOuUnDHK/rS0G4zgNCJUyNf06zVECmdvkzspNNpQ3J5aOi4t2
+lhjRIFOKA9ifGsEqYLwP2dork1xFuyHEqHkDH8zpCTvdzkWky1bwAD/Pj5dArd7t
+FeQGsPm7/64H1/rHk8pSP2pQgRsMDX6rIdx3vuQ7r+NssdRq+II4e479l02TiCDi
+FBOX+n3nPXxREPdZ9EKL4SauL/AnRqpeC9GX6fC9OOnQeQ1xVTzNWKa6ixrqkFoH
+TI/vy51p16jFNgdkLkyLtZA8Tq72TIAKWbZC0GFzWJVNASWu7WDIoMn5pgoi454w
+TgsvK9MOnEYeABiDUa1ppaoMiP4+3j5yT0eWttTMSkcKjk1Ap1o+RfUxlIGl0Rog
+ShbG2y6Mv8FERtjzPVQ7VMLDN9zRIbtlSJFm7CboPNSAygzzzaA/RIN/e8MdbZoM
+a8AT9KiAVHEEcw+nWFAatAew5VP9iRZVgrVdWBszuaWOolxnYvpAL45WanqG0eab
+VMe66+rZ8momI0MsM9JcqBwXO+fOf8CrPSO9PL8VFEJXFLZQS7asFStJf2l8msWE
+3IYhvk4B6Nf1R96XzpXLlkOnoGtcnPVAvotrGU/rDfk5i/WF810=
+=mWfF
+-----END PGP SIGNATURE-----
+--=-=-=--
+
+
+--===============5359377007738902760==
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: base64
+Content-Disposition: inline
+
+X19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX19fX18KUGtnLWVtYWNz
+ZW4tYWRkb25zIG1haWxpbmcgbGlzdApQa2ctZW1hY3Nlbi1hZGRvbnNAYWxpb3RoLWxpc3RzLmRl
+Ymlhbi5uZXQKaHR0cHM6Ly9hbGlvdGgtbGlzdHMuZGViaWFuLm5ldC9jZ2ktYmluL21haWxtYW4v
+bGlzdGluZm8vcGtnLWVtYWNzZW4tYWRkb25zCg==
+
+--===============5359377007738902760==--
+
--- /dev/null
+Return-path: <debian-science-maintainers-bounces+david=tethera.net@alioth-lists.debian.net>
+Envelope-to: david@tethera.net
+Delivery-date: Thu, 20 Dec 2018 13:27:21 -0500
+Received: from alioth-lists-01.debian.net ([2001:ba8:0:2c77:0:4:0:1])
+ by fethera.tethera.net with esmtp (Exim 4.89)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@alioth-lists.debian.net>)
+ id 1ga32X-0005bA-Go
+ for david@tethera.net; Thu, 20 Dec 2018 13:27:21 -0500
+Received: from localhost ([::1] helo=alioth-lists-01.debian.net)
+ by alioth-lists-01.debian.net with esmtp (Exim 4.89)
+ (envelope-from <debian-science-maintainers-bounces+david=tethera.net@alioth-lists.debian.net>)
+ id 1ga32U-0003yc-Ai
+ for david@tethera.net; Thu, 20 Dec 2018 18:27:18 +0000
+Received: from buxtehude.debian.org ([2607:f8f0:614:1::1274:39])
+ by alioth-lists-01.debian.net with esmtps
+ (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256) (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>) id 1ga32S-0003xN-6J
+ for debian-science-maintainers@lists.alioth.debian.org;
+ Thu, 20 Dec 2018 18:27:16 +0000
+Received: from debbugs by buxtehude.debian.org with local (Exim 4.89)
+ (envelope-from <debbugs@buxtehude.debian.org>)
+ id 1ga32R-0005U9-0Z; Thu, 20 Dec 2018 18:27:15 +0000
+X-Loop: owner@bugs.debian.org
+Subject: Bug#916867: Increase severity to 'serious'
+Resent-From: Sean Whitton <spwhitton@spwhitton.name>
+Resent-To: debian-bugs-dist@lists.debian.org
+Resent-CC: Debian Science Maintainers
+ <debian-science-maintainers@lists.alioth.debian.org>
+X-Loop: owner@bugs.debian.org
+Resent-Date: Thu, 20 Dec 2018 18:27:13 +0000
+Resent-Message-ID: <handler.916867.B916867.154533033319861@bugs.debian.org>
+X-Debian-PR-Message: followup 916867
+X-Debian-PR-Package: src:hkl
+X-Debian-PR-Keywords: ftbfs
+References: <87sgyt2xyg.fsf@zephyr.silentflame.com>
+X-Debian-PR-Source: hkl
+Received: via spool by 916867-submit@bugs.debian.org id=B916867.154533033319861
+ (code B ref 916867); Thu, 20 Dec 2018 18:27:13 +0000
+Received: (at 916867) by bugs.debian.org; 20 Dec 2018 18:25:33 +0000
+X-Spam-Checker-Version: SpamAssassin 3.4.2-bugs.debian.org_2005_01_02
+ (2018-09-13) on buxtehude.debian.org
+X-Spam-Level:
+X-Spam-Status: No, score=-13.0 required=4.0 tests=BAYES_00,DKIM_SIGNED,
+ DKIM_VALID,DKIM_VALID_AU,DKIM_VALID_EF,PGPSIGNATURE,RCVD_IN_DNSWL_LOW,
+ SORTED_RECIPS,SPF_HELO_PASS,SPF_PASS,SUSPICIOUS_RECIPS,TXREP
+ autolearn=unavailable autolearn_force=no
+ version=3.4.2-bugs.debian.org_2005_01_02
+X-Spam-Bayes: score:0.0000 Tokens: new, 0; hammy, 115; neutral, 22; spammy, 1.
+ spammytokens:0.902-+--emails
+ hammytokens:0.000-+--HX-ME-Sender:xms,
+ 0.000-+--H*RU:10.202.2.43,
+ 0.000-+--Hx-spam-relays-external:10.202.2.43,
+ 0.000-+--H*F:D*spwhitton.name, 0.000-+--H*F:U*spwhitton
+Received: from wout2-smtp.messagingengine.com ([64.147.123.25])
+ by buxtehude.debian.org with esmtps (TLS1.2:ECDHE_RSA_AES_256_GCM_SHA384:256)
+ (Exim 4.89) (envelope-from <spwhitton@spwhitton.name>)
+ id 1ga30m-00059C-Kv; Thu, 20 Dec 2018 18:25:32 +0000
+Received: from compute3.internal (compute3.nyi.internal [10.202.2.43])
+ by mailout.west.internal (Postfix) with ESMTP id C50B712F6;
+ Thu, 20 Dec 2018 13:25:30 -0500 (EST)
+Received: from mailfrontend1 ([10.202.2.162])
+ by compute3.internal (MEProxy); Thu, 20 Dec 2018 13:25:31 -0500
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=spwhitton.name;
+ h=from:to:subject:date:message-id:mime-version:content-type; s=
+ fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lqnNZlsr/mb10zqE=; b=X9S4kI14
+ zxdg9IKMIyALL2Eao3GTyyojuATJkgCqJvZWd8RWpty6RBStZfyeOqR1L3Gr5m3/
+ EVeiHQyFNnPor2xjMDmblfPS/u09JxVlc0KMpT0XXRfNWsVQn+U40nNRX15kXzZ/
+ D1rYhxpxzKRzU2tByUULCgbGlXAwJQtOXMDw3mpj1BxcoO13H/0H/KQTQ+AcpiOw
+ BV3JFKL/jA+mH8uAPIgNM2mUYZz5REO89eh3lPhLyc7tw745X+4ywZlo/Piqa0+6
+ BCldY9/nDR3csAUKx1+3hkpJPdqFALBWvG3SelGt44BqcoLsOJLB8QH6trCro39o
+ fEnBaUBTAkTAHA==
+DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=
+ messagingengine.com; h=content-type:date:from:message-id
+ :mime-version:subject:to:x-me-proxy:x-me-proxy:x-me-sender
+ :x-me-sender:x-sasl-enc; s=fm1; bh=jAd0CK1DR6lBA7NSLH5FsHIYQv3lq
+ nNZlsr/mb10zqE=; b=QYqLjUBZtfbO0DUlagvXRP2BPnpTgkvIna9/uCewkdoIH
+ /LuPW6DZUNWQa55qiDquqKXcs0tTODTEzYgeIDgqC+DDBTHnFQvXdWyS1X3o4sLL
+ 8dTKk8lv7M1/zKFxyg/ycNvPJGS9m4ZucGbxjwdgAcozhg7W1Qztxt9eVhPVnenS
+ 5sdeJ9mjIE7lYkKX4QVsXPOi86j6QlfMNyi/OnBfX2+95QiA/xPE/wEq4MYlLNm7
+ Av1P/8OrI4ImDKkOEivarktL+isYL7OXyGB4GfUTsydiy9dhP7RKPxrai1kJRu5S
+ b2470KXNatu2WkyMFrsdcwrSqyKIe096k5xPfVI2A==
+X-ME-Sender: <xms:mt4bXHFFQvAGVxMhHQP5DBif075kRubHE1KJQrR0OsDN2ClFFtlRXw>
+X-ME-Proxy-Cause: gggruggvucftvghtrhhoucdtuddrgedtkedrudejfedguddugecutefuodetggdotefrod
+ ftvfcurfhrohhfihhlvgemucfhrghsthforghilhdpqfhuthenuceurghilhhouhhtmecu
+ fedttdenucfuohhrthgvugcurhgvtghiphhsucdlgedtmdenucfjughrpefhvffufffkgg
+ gtsehgtderredttddtnecuhfhrohhmpefuvggrnhcuhghhihhtthhonhcuoehsphifhhhi
+ thhtohhnsehsphifhhhithhtohhnrdhnrghmvgeqnecurfgrrhgrmhepmhgrihhlfhhroh
+ hmpehsphifhhhithhtohhnsehsphifhhhithhtohhnrdhnrghmvgenucevlhhushhtvghr
+ ufhiiigvpedt
+X-ME-Proxy: <xmx:mt4bXGqtKYX8StS4oLrucVpNHp3EoaoH6jbQkd2mTFzupx7FwIJ2dQ>
+ <xmx:mt4bXH457xpOO1PnlqWQoPt1r7kL_P9ta064wZO_JDW1QAycFDjIsg>
+ <xmx:mt4bXPQaOrPjDseNXftuMgX1Y9gyHDbVUCYSYNdX6oXwBQhAGwzqIw>
+ <xmx:mt4bXLbmA2mCpvakWE26qsCvS2IOX4eN0KdeU2tQi-SXYEBMLVpE3A>
+From: Sean Whitton <spwhitton@spwhitton.name>
+To: 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org,
+ 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org,
+ 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org,
+ 916876@bugs.debian.org
+Date: Thu, 20 Dec 2018 18:25:26 +0000
+Message-ID: <87r2ecrr6x.fsf@zephyr.silentflame.com>
+MIME-Version: 1.0
+X-CrossAssassin-Score: 6
+Received-SPF: pass client-ip=2607:f8f0:614:1::1274:39;
+ envelope-from=debbugs@buxtehude.debian.org; helo=buxtehude.debian.org
+x-debian-approved: yes
+X-BeenThere: debian-science-maintainers@alioth-lists.debian.net
+X-Mailman-Version: 2.1.23
+Precedence: list
+List-Id: Mailing list for maintainer discussions and BTS messages
+ <debian-science-maintainers.alioth-lists.debian.net>
+List-Unsubscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/options/debian-science-maintainers>,
+ <mailto:debian-science-maintainers-request@alioth-lists.debian.net?subject=unsubscribe>
+List-Archive: <http://alioth-lists.debian.net/pipermail/debian-science-maintainers/>
+List-Post: <mailto:debian-science-maintainers@alioth-lists.debian.net>
+List-Help: <mailto:debian-science-maintainers-request@alioth-lists.debian.net?subject=help>
+List-Subscribe: <https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/debian-science-maintainers>,
+ <mailto:debian-science-maintainers-request@alioth-lists.debian.net?subject=subscribe>
+Reply-To: Sean Whitton <spwhitton@spwhitton.name>, 916867@bugs.debian.org
+Content-Type: multipart/mixed; boundary="===============7686561040995282884=="
+Errors-To: debian-science-maintainers-bounces+david=tethera.net@alioth-lists.debian.net
+Sender: "debian-science-maintainers"
+ <debian-science-maintainers-bounces+david=tethera.net@alioth-lists.debian.net>
+X-Spam_score: 2.8
+X-Spam_score_int: 28
+X-Spam_bar: ++
+
+--===============7686561040995282884==
+Content-Type: multipart/signed; boundary="=-=-=";
+ micalg=pgp-sha512; protocol="application/pgp-signature"
+
+--=-=-=
+Content-Type: text/plain
+Content-Transfer-Encoding: quoted-printable
+
+control: severity -1 serious
+
+Hello,
+
+Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+all the e-mails).
+
+=2D-=20
+Sean Whitton
+
+--=-=-=
+Content-Type: application/pgp-signature; name="signature.asc"
+
+-----BEGIN PGP SIGNATURE-----
+
+iQIzBAEBCgAdFiEEm5FwB64DDjbk/CSLaVt65L8GYkAFAlwb3pYACgkQaVt65L8G
+YkDLrhAAhORxZzZDE5vlXRm89JYA3jd9OyleioZvDDRCrpEd7CQ5AHiMMJizW1lU
+gn6OBIoW4O04TZ5oOuUnDHK/rS0G4zgNCJUyNf06zVECmdvkzspNNpQ3J5aOi4t2
+lhjRIFOKA9ifGsEqYLwP2dork1xFuyHEqHkDH8zpCTvdzkWky1bwAD/Pj5dArd7t
+FeQGsPm7/64H1/rHk8pSP2pQgRsMDX6rIdx3vuQ7r+NssdRq+II4e479l02TiCDi
+FBOX+n3nPXxREPdZ9EKL4SauL/AnRqpeC9GX6fC9OOnQeQ1xVTzNWKa6ixrqkFoH
+TI/vy51p16jFNgdkLkyLtZA8Tq72TIAKWbZC0GFzWJVNASWu7WDIoMn5pgoi454w
+TgsvK9MOnEYeABiDUa1ppaoMiP4+3j5yT0eWttTMSkcKjk1Ap1o+RfUxlIGl0Rog
+ShbG2y6Mv8FERtjzPVQ7VMLDN9zRIbtlSJFm7CboPNSAygzzzaA/RIN/e8MdbZoM
+a8AT9KiAVHEEcw+nWFAatAew5VP9iRZVgrVdWBszuaWOolxnYvpAL45WanqG0eab
+VMe66+rZ8momI0MsM9JcqBwXO+fOf8CrPSO9PL8VFEJXFLZQS7asFStJf2l8msWE
+3IYhvk4B6Nf1R96XzpXLlkOnoGtcnPVAvotrGU/rDfk5i/WF810=
+=mWfF
+-----END PGP SIGNATURE-----
+--=-=-=--
+
+
+--===============7686561040995282884==
+Content-Type: text/plain; charset="utf-8"
+MIME-Version: 1.0
+Content-Transfer-Encoding: base64
+Content-Disposition: inline
+
+LS0gCmRlYmlhbi1zY2llbmNlLW1haW50YWluZXJzIG1haWxpbmcgbGlzdApkZWJpYW4tc2NpZW5j
+ZS1tYWludGFpbmVyc0BhbGlvdGgtbGlzdHMuZGViaWFuLm5ldApodHRwczovL2FsaW90aC1saXN0
+cy5kZWJpYW4ubmV0L2NnaS1iaW4vbWFpbG1hbi9saXN0aW5mby9kZWJpYW4tc2NpZW5jZS1tYWlu
+dGFpbmVycw==
+
+--===============7686561040995282884==--
+
--- /dev/null
+From: Notmuch Test Suite <test_suite@notmuchmail.org>
+To: Sean Whitton <spwhitton@spwhitton.name>, 916811@bugs.debian.org, 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org, 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org, 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org, 916876@bugs.debian.org
+Subject: Re: [Pkg-emacsen-addons] Bug#916811: Increase severity to 'serious'
+In-Reply-To: <87r2ecrr6x.fsf@zephyr.silentflame.com>
+Fcc: MAIL_DIR/sent
+--text follows this line--
+Sean Whitton <spwhitton@spwhitton.name> writes:
+
+> control: severity -1 serious
+>
+> Hello,
+>
+> Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+> all the e-mails).
+>
+> --
+> Sean Whitton
+> _______________________________________________
+> Pkg-emacsen-addons mailing list
+> Pkg-emacsen-addons@alioth-lists.debian.net
+> https://alioth-lists.debian.net/cgi-bin/mailman/listinfo/pkg-emacsen-addons
--- /dev/null
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+ [ multipart/mixed (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Mikhail Gusarov <dottedmag@dottedmag.net>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+ [ multipart/mixed (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+ [ text/plain (hidden) ]
+ Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+ [ text/plain (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Keith Packard <keithp@keithp.com>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+ [ multipart/mixed (hidden) ]
+ Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+ [ text/plain (hidden) ]
--- /dev/null
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+[ text/plain ]
+I saw the LWN article and decided to take a look at notmuch. I'm
+currently using mutt and mairix to index and read a collection of
+Maildir mail folders (around 40,000 messages total).
+
+notmuch indexed the messages without complaint, but my attempt at
+searching bombed out. Running, for example:
+
+ notmuch search storage
+
+Resulted in 4604 lines of errors along the lines of:
+
+ Error opening
+ /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ Too many open files
+
+I'm curious if this is expected behavior (i.e., notmuch does not work
+with Maildir) or if something else is going on.
+
+Cheers,
+
+[ 4-line signature. Click/Enter to show. ]
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0x9D20F6503E338888 or unsupported algorithm ]
+ [ text/plain ]
+
+ Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did
+ gyre and gimble:
+
+ LK> Resulted in 4604 lines of errors along the lines of:
+
+ LK> Error opening
+ LK>
+ /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ LK> Too many open files
+
+ See the patch just posted here.
+
+ [ 2-line signature. Click/Enter to show. ]
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Mikhail Gusarov <dottedmag@dottedmag.net>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+ [ multipart/mixed (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+ [ text/plain (hidden) ]
+ Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+ [ text/plain (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Keith Packard <keithp@keithp.com>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+ [ multipart/mixed (hidden) ]
+ Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+ On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman <lars at
+ seas.harvard.edu> wrote:
+ > I saw the LWN article and decided to take a look at notmuch. I'm
+ > currently using mutt and mairix to index and read a collection of
+ > Maildir mail folders (around 40,000 messages total).
+
+ Welcome, Lars!
+
+ I hadn't even seen that Keith's blog post had been picked up by lwn.net.
+ That's very interesting. So, thanks for coming and trying out notmuch.
+
+ > Error opening
+ > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ > Too many open files
+
+ Sadly, the lwn article coincided with me having just introduced this
+ bug, and then getting on a Trans-Atlantic flight. So I fixed the bug
+ fairly quickly, but there was quite a bit of latency before I could push
+ the fix out. It should be fixed now.
+
+ > I'm curious if this is expected behavior (i.e., notmuch does not work
+ > with Maildir) or if something else is going on.
+
+ Notmuch works just fine with maildir---it's one of the things that it
+ likes the best.
+
+ Happy hacking,
+
+ -Carl
--- /dev/null
+Sean Whitton <spwhitton@spwhitton.name> (2018-12-20) (inbox signed) 4/5
+Subject: [Pkg-emacsen-addons] Bug#916811: Increase severity to 'serious'
+To: 916805@bugs.debian.org, 916807@bugs.debian.org, 916808@bugs.debian.org, 916809@bugs.debian.org, 916811@bugs.debian.org, 916867@bugs.debian.org, 916869@bugs.debian.org, 916872@bugs.debian.org, 916875@bugs.debian.org, 916876@bugs.debian.org
+Date: Thu, 20 Dec 2018 18:25:26 +0000
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ Unknown key ID 0x695B7AE4BF066240 or unsupported algorithm ]
+[ text/plain ]
+control: severity -1 serious
+
+Hello,
+
+Emacs 26.1 has reached Debian unstable (sooner than expected; sorry for
+all the e-mails).
+
+[ 2-line signature. Click/Enter to show. ]
+[ signature.asc: application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
--- /dev/null
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+ [ multipart/mixed (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Mikhail Gusarov <dottedmag@dottedmag.net>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+ [ multipart/mixed (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+ Twas brillig at 15:33:01 17.11.2009 UTC-05 when lars at seas.harvard.edu
+ did gyre and gimble:
+
+ LK> Is the list archived anywhere? The obvious archives
+ LK> (http://notmuchmail.org/pipermail/notmuch/) aren't available, and I
+ LK> think I subscribed too late to get the patch (I only just saw the
+ LK> discussion about it).
+
+ LK> It doesn't look like the patch is in git yet.
+
+ Just has been pushed
+
+ [ 10-line signature. Click/Enter to show. ]
+ Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+ [ text/plain (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Keith Packard <keithp@keithp.com>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+ [ text/plain ]
+ > I've also pushed a slightly more complicated (and complete) fix to my
+ > private notmuch repository
+
+ The version of lib/messages.cc in your repo doesn't build because it's
+ missing "#include <stdint.h>" (for the uint32_t on line 466).
+
+ [ 4-line signature. Click/Enter to show. ]
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+ On Tue, 17 Nov 2009 14:00:54 -0500, Lars Kellogg-Stedman <lars at
+ seas.harvard.edu> wrote:
+ > I saw the LWN article and decided to take a look at notmuch. I'm
+ > currently using mutt and mairix to index and read a collection of
+ > Maildir mail folders (around 40,000 messages total).
+
+ Welcome, Lars!
+
+ I hadn't even seen that Keith's blog post had been picked up by lwn.net.
+ That's very interesting. So, thanks for coming and trying out notmuch.
+
+ > Error opening
+ > /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ > Too many open files
+
+ Sadly, the lwn article coincided with me having just introduced this
+ bug, and then getting on a Trans-Atlantic flight. So I fixed the bug
+ fairly quickly, but there was quite a bit of latency before I could push
+ the fix out. It should be fixed now.
+
+ > I'm curious if this is expected behavior (i.e., notmuch does not work
+ > with Maildir) or if something else is going on.
+
+ Notmuch works just fine with maildir---it's one of the things that it
+ likes the best.
+
+ Happy hacking,
+
+ -Carl
--- /dev/null
+Alex Botero-Lowry <alex.boterolowry@gmail.com> (2009-11-17) (attachment inbox)
+Subject: [notmuch] preliminary FreeBSD support
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 11:36:14 -0800
+
+[ multipart/mixed ]
+[ multipart/alternative ]
+[ text/plain ]
+I saw the announcement this morning, and was very excited, as I had been
+hoping sup would be turned into a library,
+since I like the concept more than the UI (I'd rather an emacs interface).
+
+I did a preliminary compile which worked out fine, but
+sysconf(_SC_SC_GETPW_R_SIZE_MAX) returns -1 on
+FreeBSD, so notmuch_config_open segfaulted.
+
+Attached is a patch that supplies a default buffer size of 64 in cases where
+-1 is returned.
+
+http://www.opengroup.org/austin/docs/austin_328.txt - seems to indicate this
+is acceptable behavior,
+and
+http://mail-index.netbsd.org/pkgsrc-bugs/2006/06/07/msg016808.htmlspecifically
+uses 64 as the
+buffer size.
+[ text/html (hidden) ]
+[ 0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch: text/x-diff ]
+From e3bc4bbd7b9d0d086816ab5f8f2d6ffea1dd3ea4 Mon Sep 17 00:00:00 2001
+From: Alexander Botero-Lowry <alex.boterolowry@gmail.com>
+Date: Tue, 17 Nov 2009 11:30:39 -0800
+Subject: [PATCH] Deal with situation where sysconf(_SC_GETPW_R_SIZE_MAX) returns -1
+
+---
+ notmuch-config.c | 2 ++
+ 1 files changed, 2 insertions(+), 0 deletions(-)
+
+diff --git a/notmuch-config.c b/notmuch-config.c
+index 248149c..e7220d8 100644
+--- a/notmuch-config.c
++++ b/notmuch-config.c
+@@ -77,6 +77,7 @@ static char *
+ get_name_from_passwd_file (void *ctx)
+ {
+ long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
++ if (pw_buf_size == -1) pw_buf_size = 64;
+ char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
+ struct passwd passwd, *ignored;
+ char *name;
+@@ -101,6 +102,7 @@ static char *
+ get_username_from_passwd_file (void *ctx)
+ {
+ long pw_buf_size = sysconf(_SC_GETPW_R_SIZE_MAX);
++ if (pw_buf_size == -1) pw_buf_size = 64;
+ char *pw_buf = talloc_zero_size (ctx, pw_buf_size);
+ struct passwd passwd, *ignored;
+ char *name;
+--
+1.6.5.2
+
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+ Carl Worth <cworth@cworth.org> (2009-11-17) (inbox unread)
--- /dev/null
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+[ text/plain (hidden) ]
+[ application/pgp-signature ]
+[ text/plain (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0x9D20F6503E338888 or unsupported algorithm ]
+ [ text/plain (hidden) ]
+ [ application/pgp-signature ]
+ [ text/plain (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Mikhail Gusarov <dottedmag@dottedmag.net>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+ [ text/plain (hidden) ]
+ [ application/pgp-signature ]
+ [ text/plain (hidden) ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+ [ text/plain (hidden) ]
+ Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+ [ text/plain (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Keith Packard <keithp@keithp.com>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+ [ text/plain (hidden) ]
+ [ application/pgp-signature ]
+ [ text/plain (hidden) ]
+ Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+ [ text/plain (hidden) ]
--- /dev/null
+Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+Subject: [notmuch] Working with Maildir storage?
+To: notmuch@notmuchmail.org
+Date: Tue, 17 Nov 2009 14:00:54 -0500
+
+[ multipart/mixed ]
+[ multipart/signed ]
+[ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+[ text/plain (hidden) ]
+[ application/pgp-signature ]
+[ text/plain ]
+[ 4-line signature. Click/Enter to show. ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 01:02:38 +0600
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0x9D20F6503E338888 or unsupported algorithm ]
+ [ text/plain ]
+
+ Twas brillig at 14:00:54 17.11.2009 UTC-05 when lars@seas.harvard.edu did
+ gyre and gimble:
+
+ LK> Resulted in 4604 lines of errors along the lines of:
+
+ LK> Error opening
+ LK>
+ /home/lars/Mail/read-messages.2008/cur/1246413773.24928_27334.hostname,U=3026:2,S:
+ LK> Too many open files
+
+ See the patch just posted here.
+
+ [ 2-line signature. Click/Enter to show. ]
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-17) (inbox signed)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Mikhail Gusarov <dottedmag@dottedmag.net>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 15:33:01 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+ [ text/plain (hidden) ]
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ Mikhail Gusarov <dottedmag@dottedmag.net> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:50:48 +0600
+
+ [ text/plain (hidden) ]
+ Keith Packard <keithp@keithp.com> (2009-11-17) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 13:24:13 -0800
+
+ [ text/plain (hidden) ]
+ Lars Kellogg-Stedman <lars@seas.harvard.edu> (2009-11-18) (inbox signed unread)
+ Subject: Re: [notmuch] Working with Maildir storage?
+ To: Keith Packard <keithp@keithp.com>
+ Cc: notmuch@notmuchmail.org
+ Date: Tue, 17 Nov 2009 19:50:40 -0500
+
+ [ multipart/mixed ]
+ [ multipart/signed ]
+ [ Unknown key ID 0xD74695063141ACD8 or unsupported algorithm ]
+ [ text/plain ]
+ > I've also pushed a slightly more complicated (and complete) fix to my
+ > private notmuch repository
+
+ The version of lib/messages.cc in your repo doesn't build because it's
+ missing "#include <stdint.h>" (for the uint32_t on line 466).
+
+ [ 4-line signature. Click/Enter to show. ]
+ [ application/pgp-signature ]
+ [ text/plain ]
+ [ 4-line signature. Click/Enter to show. ]
+ Carl Worth <cworth@cworth.org> (2009-11-18) (inbox unread)
+ Subject: [notmuch] Working with Maildir storage?
+ To: notmuch@notmuchmail.org
+ Date: Wed, 18 Nov 2009 02:08:10 -0800
+
+ [ text/plain (hidden) ]
--0016e687869333b14e0478963d33--
--0016e687869333b1570478963d35
-Content-Type: application/octet-stream;
+Content-Type: text/x-diff;
name="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
Content-Disposition: attachment;
filename="0001-Deal-with-situation-where-sysconf-_SC_GETPW_R_SIZE_M.patch"
-e 's|"id": "[^"]*",|"id": "XXXXX",|g' \
-e 's|"Date": "Fri, 05 Jan 2001 [^"]*0000"|"Date": "GENERATED_DATE"|g' \
-e 's|"filename": "signature.asc",||g' \
+ -e 's|"duplicate": 1,||g' \
-e 's|"filename": \["/[^"]*"\],|"filename": \["YYYYY"\],|g' \
-e 's|"timestamp": 97.......|"timestamp": 42|g' \
-e 's|"content-length": [1-9][0-9]*|"content-length": "NONZERO"|g'
}
+notmuch_sexp_show_sanitize () {
+ sed \
+ -e 's|:id "[^"]*"|:id "XXXXX"|g' \
+ -e 's|:Date "Sat, 01 Jan 2000 [^"]*0000"|:Date "GENERATED_DATE"|g' \
+ -e 's|:filename "signature.asc"||g' \
+ -e 's|:duplicate 1 ||g' \
+ -e 's|:filename ("/[^"]*")|:filename ("YYYYY")|g' \
+ -e 's|:timestamp 9........|:timestamp 42|g' \
+ -e 's|:content-length [1-9][0-9]*|:content-length "NONZERO"|g'
+}
+
+notmuch_sexp_search_sanitize () {
+ sed -e 's|:thread "[^"]*"|:thread "XXX"|'
+}
+
notmuch_emacs_error_sanitize () {
local command
command=$1
-e 's/^Date: Fri, 05 Jan 2001 .*0000/Date: GENERATED_DATE/'
}
+# remove redundant parts of notmuch-git internal paths
+notmuch_git_sanitize () {
+ sed -e 's,tags/\([0-9a-f]\{2\}/\)\{2\},,' -e '/FORMAT/d'
+}
notmuch_uuid_sanitize () {
sed 's/[0-9a-f]\{8\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{4\}-[0-9a-f]\{12\}/UUID/g'
}
notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr | notmuch_exception_sanitize | notmuch_debug_sanitize > OUTPUT
}
+test_private_C () {
+ local exec_file test_file
+ exec_file="test${test_count}"
+ test_file="${exec_file}.c"
+ echo '#include <notmuch-private.h>' > ${test_file}
+ cat >> ${test_file}
+ ${TEST_CC} ${TEST_CFLAGS} -I${NOTMUCH_SRCDIR}/test -I${NOTMUCH_SRCDIR}/lib -I${NOTMUCH_SRCDIR}/util -I${NOTMUCH_SRCDIR}/compat ${NOTMUCH_GMIME_CFLAGS} -o ${exec_file} ${test_file} ${NOTMUCH_BUILDDIR}/lib/libnotmuch.a ${NOTMUCH_GMIME_LDFLAGS} ${NOTMUCH_XAPIAN_LDFLAGS} ${NOTMUCH_BUILDDIR}/util/libnotmuch_util.a ${NOTMUCH_SFSEXP_LDFLAGS} ${NOTMUCH_BUILDDIR}/parse-time-string/libparse-time-string.a -ltalloc -lstdc++
+ echo "== stdout ==" > OUTPUT.stdout
+ echo "== stderr ==" > OUTPUT.stderr
+ ./${exec_file} "$@" 1>>OUTPUT.stdout 2>>OUTPUT.stderr
+ notmuch_dir_sanitize OUTPUT.stdout OUTPUT.stderr | notmuch_exception_sanitize | notmuch_debug_sanitize > OUTPUT
+}
+
make_shim () {
local base_name test_file shim_file
base_name="$1"